Intel® Curie™ Open Developer Kit (ODK)

Overview 

The Intel® Curie™ ODK includes the software, tools and documentation for developers to build boards based on the Intel® Curie™ module and turn them into products.

The aim of the Intel® Curie™ ODK is to help users develop their own boards starting with Arduino 101* (branded Genuino 101* in some countries) or tinyTILE* boards, and later on using the open source schematics and board files, and via command line access the open source product firmware, the C++ class libraries, the entire hardware in the Intel® Curie™ module and other open sourced third-party code.

Open Software Developer Kit

The open software developer kit includes all the tools needed to develop the software for Curie-based devices: Arduino* core class libraries from the Arduino 101* (branded Genuino 101* in some countries) product, optimized compilers for ARC* and IA32 (Intel® Quark™ SE microcontroller), debug GDB with JTAG OpenOCD, Developer Flashpack, and bin utilities.

The open software developer kit targets experienced developers familiar with embedded systems and C++ programming. Three firmware source trees are available for developers, with their respective compilers and debuggers:

  • The A Tree provides access to Arduino 101* firmware and Arduino core libraries and tools. The A tree is available at CODK-A
  • The M tree is a mix of the A and Z trees. The firmware based on Zephyr* runs on the Intel® Quark™ processor core, and the user code with Arduino* class libraries runs on the ARC* EM core. The M tree is available at CODK-M
  • The Z tree is the version of Zephyr* firmware that targets Arduino 101* as host core. The Z tree is available at CODK-Z

The A tree is the simplest to use. If you are familiar with the Arduino* ecosystem, the A tree is an ideal working environment. You can import sketches, use the sketch converter to convert the sketches in .ino file format to C++ application code, rework the code, compile the code file and flash the binary image to the board, and run the application to validate it on the board. We recommend that you start with the A tree.

The M tree includes the firmware based on Zephyr* that runs on Intel® Quark™ processor core, and the software with Arduino* class libraries that runs on the ARC* core. The M tree software is similar to the A tree software, with modifications so it is compatible with the Zephyr-based firmware. Compared to the Z tree firmware, the M tree uses Zephyr APIs and also Arduino-style APIs for the Intel® Quark™ processor core as these are simpler to use.

The Z tree includes the Zephyr* libraries and drivers. In addition to running the system initialization firmware, you can run your software applications on both the Intel® Quark™ processor and the ARC* cores. You will use the driver APIs that are built with Zephyr* to apply changes to both cores.

Hardware

The Intel® Curie™ module is ideal for "always-on" applications. It includes the 32 MHz Intel® Quark™ SE C1000 microcontroller, on-chip Flash/RAM (384 kB, 80 kB), Bluetooth® low energy, 6-axis motion sensors (accelerometer and gyroscope), a sensor hub processor, and a pattern matching engine.

The Intel® Quark™ SE C1000 microcontroller inside the Intel® Curie™ module, also available as stand-alone processor, includes two processor cores: the Intel® Quark™ processor core that hosts the USB and other system level duties, and the ARC* core that runs the user sketches (built-in examples in Arduino* software).

The pattern matching engine inside the Intel® Quark™ SE C1000 microcontroller allows to identify different motions and activities. It takes signals from the on-board gyroscope and accelerometer.

Arduino 101* (branded Genuino 101* in some countries) and tinyTILE* are great starting points to build your own Intel® Curie™-based products.

  • Arduino 101* (branded Genuino 101* in some countries) is the first retail product based on the Intel® Curie™ module. It is compatible with Arduino* shields and software. The schematics, board layout files and software are open source. There is also a rich library of code to build on as well as existing third-party open source code and hardware. The Arduino 101* targets the maker and education audience.
  • tinyTILE* from element14 is also powered with the Intel® Curie™ module. The software is compatible with Arduino 101* in a small form factor. tinyTILE* is designed for developers and product makers. The schematics and board layout files are open source. The software is open source (mostly L-GPL 2.1 with permissive licence). Compared to Arduino 101*, tinyTILE* exposes more of the Intel® Curie™ module pins.

Tools

The open developer kit for Intel® Curie™ module includes a set of development, manufacturing and certification tools to help you with getting into production, pass the regulatory certification tests and get on the market faster.

The tools include provisioning scripts to program the OTP pages, scripts for FCC and CE certification, and the flashing process for starting with a blank Intel® Curie™ module.

Documentation

The documentation covers the software (getting started and user guides for open-source licensing and non-open parts), support for hardware (tinyTILE* and Arduino 101*) and the tools (testing, debugging, manufacturing, certification).

Open Software Developer Kit

This section provides instructions for the installation and the use of the three trees as well as the connection and verification of the boards. It also gives information about the bootloader, the pattern matching library, the Arc32 toolchain and how to reset the board to the factory settings. Finally there is an overview of Intel Curie ODK directory content.

About the Bootloader

The Arduino 101* and the tinyTILE* boards are pre-flashed with a bootloader that supports flashing over USB using the DFU protocol. Flashing over USB keeps the original bootloader intact and does not require a JTAG adaptor. The bootloader is the same for the three trees. It supports the flashing of the firmware for the Bluetooth® low energy. Flashing an image to the board does not overwrite the bootloader unless you have modified the content of the bootloader in the A tree.

We strongly recommend that you DO NOT modify the bootloader content that is stored in the A tree. The bootloader is used for boot-up initialization and to upload the firmware and software using DFU. In case you have changed some content and have damaged the bootloader, you will need to use JTAG to recover it.

Operating System

The supported operating system for open software developer kit is Linux* Ubuntu 16.04 – 64 bit and 14.04 – 64 bit with at least 40 GB of free space.

Installing and Using the A Tree

The A tree is the actual Arduino 101* product firmware source code and the Arduino C++ class libraries for the ARC core, tool sets and materials organized for embedded developers who want to go beyond what is possible in the Arduino IDE by targeting both cores.

Note Always open a new terminal or new shell when you install or use the A tree after you have installed or used the M tree or Z tree.

Content

  • x86: Arduino 101 firmware
  • ARC: Arduino sketches (.ino) or *.cpp

Supported Platforms

  • Ubuntu 16.04 - 64 bit and Ubuntu 14.04 - 64 bit

Installation

Prior to installing the CODK-A, run a software update and install curl:

$ sudo apt-get update
$ sudo apt-get install curl

Use the following commands to create a directory, clone the GitHub* repository, install the dependencies and setup the environment:

$ mkdir -p ~/CODK && cd $_
$ git clone https://github.com/01org/CODK-A.git
$ cd CODK-A
$ make clone
$ sudo make install-dep
$ make setup

Note The sudo make install-dep command will add the user to the "dialout" group, so you need to log out and log back in for the update to be effective.

Compiling the x86 and ARC

At the start of every development session, the following command must be run from within the CODK-A directory to set the environment.

$ export CODK_DIR=$(pwd)

Use the following commands to compile the x86 and ARC after you have installed the CODK-A.

To compile x86:

$ make compile-x86

To compile ARC:

$ make compile-arc

The top-level Makefile builds the ASCIITable example by default. See the section Connecting and Verifying the Board Proof of Life for step-by-step instructions to run the example on Arduino 101 or tinyTILE board.

Uploading a Binary Image to the Board (Flashing)

Flashing Using USB/DFU

To flash the x86 binary image:

$ make upload-x86-dfu

To flash the ARC binary image:

$ make upload-arc-dfu

Flashing Using JTAG

To flash the x86 binary image:

$ make upload-x86-jtag

To flash the ARC binary image:

$ make upload-arc-jtag

Flashing Using J-Link

To flash the x86 binary image:

$ make upload-x86-jlink

To flash the ARC binary image:

$ make upload-arc-jlink

Updating the Bluetooth® low energy firmware

The Intel® Curie™ ODK requires an update of the BLE firmware. If your board is based on the factory settings, run the following command to update the Bluetooth® low energy firmware:

$ make upload-ble-dfu

The script will prompt you to manually reset the board.

Debugging

See the section Software Developer Tools.

Using the Sketch Converter

For users wishing to base their ARC application on an Arduino sketch, a utility is included to convert a sketch to a C++ file.

From within the CODK-A directory, run the following commands to convert an Arduino sketch (<sketch-filename>.ino) to C++:

$ export CODK_DIR=$(pwd)
$ cd $CODK_DIR/arc/examples/<folder-name>
$ make convert-sketch SKETCH=<sketch_filename>.ino

The resulting C++ file created in the application directory will be named <sketch_filename>.ino.cpp

Copy and paste an application Makefile from another example, and edit the content to add the special libraries required. In the following example code, two libraries are required. The variable is empty for simple examples.

LIBDIRS  =  $(ARDUINOIDE_DIR)/libraries/WiFi/src \
            $(ARDUINOSW_DIR)/corelibs/libraries/SPI/src

Build and run the application as usual

$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Running the Application

The flashing or upload process will automatically reset the board and run the application.

Switching to Another Tree

  • Exit out of the current terminal to clear out the environment variables.
  • Restore the Arduino 101 board factory settings as explained in section Reflashing the Arduino 101 with Factory Settings
  • Open a new terminal and use the cd command into the desired tree.

Installing and Using the M Tree

The M-Tree is a mix of the current Zephyr* RTOS for Intel Quark SE processor core (x86), the Arduino C++ class libraries for ARC core, with some modifications to each to make them work together. This is also the future direction for the Arduino 101* product. It is for developers who want to leverage the ease of combining the Arduino C++ class libraries with a more modern version of the Zephyr-based firmware under development for the Intel® Curie™ module-based Arduino products.

Note Always open a new terminal or new shell when you install or use the M tree after you have installed or used the A tree or Z tree.

Content

  • x86: Zephyr application
  • ARC: Arduino sketches (.ino) or *.cpp

Supported Platforms

Ubuntu* 16.04 - 64 bit and Ubuntu 14.04 - 64 bit

Installation

Prior to installing the CODK-M, run a software update and install curl:

$ sudo apt-get update
$ sudo apt-get install curl

Use the following commands to create a directory, clone the GitHub* repository, install the dependencies and setup the environment:

$ mkdir -p ~/CODK && cd $_
$ git clone https://github.com/01org/CODK-M.git
$ cd CODK-M
$ make clone
$ sudo make install-dep
$ make setup

Note The sudo make install-dep command will add the user to the "dialout" group, so you need to log out and log back in for the update to be effective.

Compiling the Firmware and Software

At the start of every development session, the following commands must be run from within the CODK-M directory to set the environment:

$ export CODK_DIR=$(pwd)
$ source ../zephyr/zephyr-env.sh

Use the following commands to compile the x86 and ARC.

To compile x86:

$ make compile-x86

To compile ARC:

$ make compile-arc

The top-level Makefile builds the ASCIITable example by default. See the section Connecting and Verifying the Board Proof of Life for step-by-step instructions to run the example on Arduino 101 or tinyTILE board.

Uploading a Binary Image to the Board (Flashing)

Flashing Using USB/DFU

To flash x86 binary image:

$ make upload-x86-dfu

To flash ARC binary image:

$ make upload-arc-dfu

Flashing Using JTAG

To flash x86 binary image:

$ make upload-x86-jtag

To flash ARC binary image:

$ make upload-arc-jtag

Flashing Using J-Link

To flash x86 binary image:

$ make upload-x86-jlink

The flash ARC binary image:

$ make upload-arc-jlink

Updating the Bluetooth® low energy firmware

The Intel® Curie™ ODK requires an update of the BLE firmware. If your board is based on the factory settings, run the following command to update the BLE firmware:

$ make upload-ble-dfu

The script will prompt you to manually reset the board.

Debugging

See the section Software Developer Tools.

Using the Sketch Converter

For users wishing to base their ARC application on an Arduino sketch, a utility is included to convert a sketch to a C++ file.

From within the CODK-M directory, run the following commands to convert an Arduino sketch (<sketch-filename>.ino) to C++:

$ source ../zephyr/zephyr-env.sh
$ export CODK_DIR=$(pwd)
$ cd $CODK_DIR/arc/examples/<folder-name>
$ make convert-sketch SKETCH=<sketch_filename>.ino

The resulting C++ file created in the application directory will be named <sketch_filename>.ino.cpp

Copy and paste an application Makefile from another example, and edit the content to add the special libraries required. In the following example code, two libraries are required. The variable is empty for simple examples.

LIBDIRS  =  $(ARDUINOIDE_DIR)/libraries/WiFi/src \
            $(ARDUINOSW_DIR)/corelibs/libraries/SPI/src

Build and run the application as usual

$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Running the Application

The flashing or upload process will automatically reset the board and run the application.

Switching to Another Tree

  • Exit out of the current terminal to clear out the environment variables.
  • Restore the Arduino 101 board factory settings as explained in section Reflashing the Arduino 101 with Factory Settings
  • Open a new terminal and use the cd command into the desired tree.

Installing and Using the Z Tree

The Z-Tree is Zephyr* RTOS targeting both the Intel® Quark™ SE processor core (x86) and the ARC core, but does not include the Arduino C++ class libraries for the ARC core. It is for developers who want the greatest control over the entire system and do not wish to use the Arduino C++ class libraries.

Note: ALWAYS open a new terminal or new shell when you install or use the Z tree after you have installed or used the A tree or M tree.

Content

  • x86: Zephyr application
  • ARC: Zephyr application

Supported Platforms

  • Ubuntu 16.04 - 64 bit and Ubuntu 14.04 - 64 bit

Installation

Prior to installing the CODK-Z, run a software update and install curl:

$ sudo apt-get update
$ sudo apt-get install curl

Use the following commands to create a directory, clone the GitHub repository, install the dependencies and setup the environment:

$ mkdir -p ~/CODK && cd $_
$ git clone https://github.com/01org/CODK-Z.git
$ cd CODK-Z
$ make clone
$ sudo make install-dep

Note The sudo make install-dep command will add the user to the "dialout" group, so you need to log out and log back in for the update to be effective.

Log out and log in again. Open a terminal window and finalize the installation:

$ cd CODK/CODK-Z
$ make setup

Compiling the Firmware and Software

At the start of every development session, the following command must be run from within the CODK-Z directory to set the environment

$ source ../zephyr/zephyr-env.sh

Use the following commands to compile the x86 and ARC.

To compile x86:

$ make compile-x86

To compile ARC:

$ make compile-arc

The top-level Makefile builds the Hello example by default. See the section Connecting and Verifying the Board Proof of Life for step-by-step instructions to run the example on Arduino 101 or tinyTILE board.

Uploading a Binary Image to the Board (Flashing)

Flashing Using USB or DFU

To flash x86 binary image:

$ make upload-x86-dfu

To flash ARC binary image:

$ make upload-arc-dfu

Flashing Using JTAG

To flash x86 binary image:

$ make upload-x86-jtag

To flash ARC binary image:

$ make upload-arc-jtag

Flashing Using J-Link

To flash x86 binary image:

$ make upload-x86-jlink

To flash ARC binary image:

$ make upload-arc-jlink

Updating the Bluetooth® low energy firmware

The Intel® Curie™ ODK requires an update of the BLE firmware. If your board is based on the factory settings, run the following command to update the BLE firmware:

$ make upload-ble-dfu

The script will prompt you to manually reset the board.

Debugging

See the section Software Developer Tools.

Running the Application

The flashing or upload process will automatically reset the board and run the application.

Switching to Another Tree

  • Exit out of the current terminal to clear out the environment variables.
  • Restore the Arduino 101* board factory settings as explained in section Reflashing the Arduino 101 with Factory Settings.
  • Open a new terminal and use the cd command into the desired tree.

Connecting and Verifying the Board Proof of Life 

Do not flash an image to the board without first checking that it works correctly.

Connecting the Arduino 101 and tinyTILE

Use the following type of cable to connect the board to your computer:

  • Arduino 101: use a standard USB 2.0 A plug to B plug
  • tinyTILE: use a USB device cable type A male to type micro B male

Check that the power LED labeled ON lights on the board.

Verifying the Board Proof of Life

A Tree and M Tree

Running the ASCIITable example with CuteCom

Ensure that your board is connected to your PC.

Use the following command to check the serial device name:

$ dmesg | grep tty

Install a serial port terminal like CuteCom

$ sudo apt-get install cutecom

Use the following command to open CuteCom application in a new terminal.

$ cutecom &

In CuteCom, change the name of the deviceto your serial device name (for example /dev/ttyACM0) in the CuteCom window.

Changing the device name in CuteCom

Run the commands to compile the x86 and ARC. The top-level makefile builds the ASCIITable example by default.

Example for CODK-A:

$ cd CODK
$ cd CODK-A
$ export CODK_DIR=$(pwd)
$ make compile-x86
$ make compile-arc

Type the following commands to run the ASCIITable example in the same terminal window where you compiled x86 and ARC :

$ cd $CODK_DIR/arc/examples/ASCIITable
$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Wait for 10 seconds after the upload and select Open Device in the CuteCom terminal.

Using CuteCom to run the ASCIITable example

You should see rows of ASCII characters displayed in CuteCom serial port terminal.

Checking the board with ASCIITable example in CuteCom serial port terminal

Select Close Device and Quit to stop running the ASCIITable example in CuteCom.

Running the ASCIITable example with Screen

Use the following command to install Screen

$ sudo apt-get install screen

Ensure that your board is connected to your PC.

Use the following command to check the serial device name:

$ dmesg | grep tty

Run the commands to compile the x86 and ARC. The top-level makefile builds the ASCIITable example by default.

Example for CODK-A:

$ cd CODK
$ CODK-A
$ export CODK_DIR=$(pwd)
$ make compile-x86
$ make compile-arc

In the same terminal, use the following commands to run the ASCIITable example:

$ cd $CODK_DIR/arc/examples/ASCIITable
$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Wait for a few seconds and in the same terminal window, use the following command, assuming that your serial device name is ttyACM0:

$ screen /dev/ttyACM0 115200,cs8

To exit the Screen window, first use the combination of the Ctrl and A keys, release and then press the \ key.

Confirm that you want to quit Screen by typing 'y'

Confirming Screen termination

The confirmation "Screen terminating" is displayed on the Screen window.

Confirmation of Screen termination

For more information about Screen, look atGNU's Screen User’s Manual

Running the Blink example on Arduino 101 board

You can use the blink example to check the Arduino 101 as the board is equipped with two LEDs. The sketch converter will translate the sketch into a C++ file that you can compile and flash to the board. The procedure for converting, building and uploading an existing sketch is as follows:

Check the serial device name, for example "/dev/ttyACM0".

Ensure that your Arduino 101 board is connected to your computer.

Use the following commands:

$ cd $CODK_DIR/arc/examples/Blink
$ make convert-sketch SKETCH=Blink.ino
$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

The LED on pin number 13 should light on and off every second.

Z Tree

The Hello example application writes "Hello from ARC!" to the UART at a baud rate of 115200.

Setup a 3-pin connection with a Future Technology Devices International Ltd* (FTDI) USB to serial converter cable (for example TTL-232r-3v3 available at FIDI USB TTL Serial Cables) as detailed hereafter:

  • Connect Ground or GND pin 1 to the ground of Arduino 101 or tinyTILE board
  • Connect TXD pin 4 (orange) to the digital pin 0 of Arduino 101 or tinyTILE board
  • Connect RXD pin 5 (yellow) to the digital pin 1 of Arduino 101 or tinyTILE board

Using a FTDI USB to serial converter cable to run Hello example

Use the following command to check the serial device name (for example "/dev/ttyUSB0"):

$ dmesg | grep tty

Open a serial terminal in a new window.

Use the following commands to run the Hello example (staying at CODK-Z level):

$ export CODK_DIR=$(pwd)
$ make compile
$ make upload SERIAL_PORT=/dev/ttyUSB0

You should see "Hello from ARC!" displayed in the Cutecom window:

Hello example running on CODK-Z

Creating and Using Your Own Project

This tutorial illustrates how to create and use your own project for the Intel Curie ODK.

The example below uses the M firmware source code tree for the purpose of illustration. It assumes that you have successfully installed the one of the source trees and verified that that your board is operating normally. The three source tree options are described in the introduction to the Intel Curie ODK, and instructions on how to install them are also available on this site.

The following steps will enable you to create your own project and set you up to compile your own code:

  1. After installing the M tree, exit your current shell session and start a new one.
  2. In the CODK-M directory, run make project, specifying the desired name of your project directory:
    $ make project PROJ_DIR=my_project
  3. Source the generated $PROJ_DIR/env.sh
    $ source my_project/env.sh

This sets CODK_DIR, ARC_PROJ_DIR, X86_PROJ_DIR and sources the zephyr environment at $CODK_DIR/../zephyr/zephyr-env.sh, so you don't need to do these things now when working in my_project; just source env.sh.

You are now ready to compile code in my_project/arc and my_project/x86.

Function-Specific Libraries 

Intel® Curie™ Pattern Matching Engine

Intel-Pattern-Matching-Technology GitHub* repository includes a folder with a few examples that you can try out. Below is the list of examples currently available.

Intel-Pattern-Matching-Technology
                                 |- src
                                        |- CuriePME.cpp
                                        |- CuriePME.h
                                 |- examples
                                        |- a_SimplePatternMatching
                                        |- b_SavingKnowledge
                                        |- c_RestoringKnowledge
                                        |- d_k-nearest-neighbor
                                        |- DrawingInTheAir 

The tutorial Using the Pattern Matching Engine uses the DrawingInTheAir example.

To use the pattern matching examples first clone the GitHub* repository:

$ git clone https://github.com/01org/Intel-Pattern-Matching-Technology

Store the content of the repository under CODK-A/arc/libraries or CODK-M/arc/libraries.

Open each example folder and look at the makefile for each example to see that the path to the relevant libraries is up-to-date. For example sketch a_SimplePatternMatching:

LIBDIRS = $(ARDUINOSW_DIR)/libraries/Intel-Pattern-Matching-Technology/src

For example sketches b_SavingKnowledge, c_RestoringKnowledge and d_nearest-neighbor:

LIBDIRS = $(ARDUINOSW_DIR)/libraries/Intel-Pattern-Matching-Technology/src \
          $(ARDUINOSW_DIR)/corelibs/libraries/SPI/src \
          $(ARDUINOSW_DIR)/corelibs/libraries/SerialFlash

From within the CODK-A directory or the CODK-M directory, run the following command to convert a pattern matching example sketch and build the application. For example:

$ cd $CODK_DIR/arc/libraries/Intel-Pattern-Matching-Technology/examples/a_SimplePatternMatching
$ make convert-sketch SKETCH=a_SimplePatternMatching.ino
$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Intel® Curie™ Mailbox Library

The Intel Curie mailbox library provides access to the inter-processor mailbox that allows interrupt-based communication between the Intel Quark SE processor core (x86) and the ARC core contained in the Intel Curie module.

The path to Intel® Curie™ Mailbox library is CODK-M/arc/corelibs/libraries/CurieMailbox.

The README file details the message format and API reference.

The tutorial on Message Passing explains how to run the SharedCounter and String examples.

Appendix

This section includes additional information about the Intel Curie Open Developer Kit (ODK).

Arc32 Toolchain

The Arc32 toolchain is installed with the initial make setup command, and is located in $CODK_DIR/software/arc32/bin

Reflashing the Arduino 101 with Factory Settings

Use the following link to reflash the board. The instructions are included in the ReadMe.pdf document in the package.

Arduino 101* Software Package

Note Once you have reflashed the board wit the factory settings, run the following command to update the BLE firmware for Intel Curie ODK:

$ make upload-ble-dfu

The script will prompt you to manually reset the board.

Overview of Intel Curie ODK Directory

Note When you follow the instructions to install Intel Curie ODK trees, the top-level folder is named "CODK" .

Please refer to the ReadMe file in each tree as it includes a lot of information about how to use the open software developer kit.

The firmware directory contains the source and tools to create binaries for the Intel Quark processor core (x86 core).

The software directory contains the source and tools to create binaries for the ARC core.

The flashpack directory contains the flashing tools and other various helper scripts

CODK

├── CODK-A

│ ├── x86

│ ├── flashpack

│ ├── Makefile

│ ├── README.md

│ └── arc

├── CODK-M

│ ├── x86

│ ├── flashpack

│ ├── Makefile

│ ├── README.md

│ ├── arc

│ └── x86-samples

└── CODK-Z

├── x86

├── flashpack

├── Makefile

├── README.md

└── arc

Hardware

This section presents information about the hardware part of Intel® Curie™ Open Developer Kit (ODK):

Intel® Curie™ Module 

Intel® Curie™ module is a tiny hardware product offering design flexibility. This complete low-power solution comes with compute capabilities, motion sensors, Bluetooth® low energy, battery charging, and a pattern matching engine for optimized analysis of sensor data. The combined use of data capture, data processing and pattern matching engine enables quick identification of actions and motions. The Intel® Curie™ module is an ideal low-power solution for wearable devices as well as consumer and industrial applications. The Intel® Curie™ module is ideal for "always-on" applications such as social media, sports, and fitness activities. The module is packaged into a tiny form factor and runs a new software platform created specifically for the Intel® Curie™ module.

Main Features

  • 32-bit Intel® Quark™ SE microcontroller C1000
  • 384 kB flash memory, 80 kB SRAM
  • Integrated digital signal processor (DSP) sensor hub
  • Pattern matching engine
  • Bluetooth® low energy
  • 6-axis combo sensor with accelerometer and gyroscope
  • Battery charging circuitry
  • Temperature range from -25ºC to +70ºC

Block Diagram

Intel® Curie™ module block diagram

Overview

Intel® Quark™ SE Microcontroller C1000

The Intel® Quark™ SE microcontroller C1000 combines the Intel® Quark™ processor core with an on-board sensor subsystem to manage power consumption through programmable wake cues. The Intel® Quark™ SE microcontroller also features a pattern matching engine that allows it to learn and differentiate. The result is always-sensing intelligence, bringing real-time response down to the next generation of intelligent devices.

  • Full x86 instruction set architecture for compatibility and scalability, offering low-power performance that can be scaled throughout a solution for end-to-end deployments.
  • Integrated sensor hub that keeps power to a minimum by intelligently handling and processing data from external sensors
  • Pattern-matching engine that recognizes patterns from incoming sensor data, providing real-time and actionable insights

Flash Memory and SRAM

  • 384 kB Flash non-volatile memory
  • 80 kB Static RAM (SRAM) volatile memory

Integrated Digital Signal Processor (DSP) Sensor Hub

The sensor hub is a digital signal processor that monitors and processes data captured by the 6-axis sensor that combines accelerometer and gyroscope. It provides real time accurate data with minimal power consumption and contributes to performance improvement as it takes over some processing from the main processing unit, the Intel Quark processor core. The sensor hub interface with the 6-axis sensor via an SPI interface.

Pattern Matching Engine

The pattern matching engine inside Intel Quark SE microcontroller can identify different motions and activities.

Within the Intel Quark SE microcontroller, the pattern matching engine reads the signals coming from the 6-axis combo sensor that combines accelerometer and gyroscope and compares the incoming pattern with a reference pattern held in memory. The engine is used to classify input patterns, and identify new or abnormal patterns.

Bluetooth® Low Energy

The low-power functionality of Bluetooth® low energy is ideal for applications that run for long periods and communicate in wireless mode with devices over Bluetooth. The Bluetooth® low energy device switches to low-power sleep mode when not sending or receiving messages. The device communicates with Intel Quark SE microcontroller via UART.

Six-axis Combo Sensor with Accelerometer and Gyroscope

The six-axis combo sensor includes a 16-bit tri-axial accelerometer and a 16-bit tri-axial gyroscope. The SPI interface between the DSP sensor hub and the sensor is used for the sensor configuration and the reading of the sensor data. There is also an I2C interface for compatible external magnetometers. The 6-axis sensor is capable of data sampling which reduces the data processing for the DSP sensor Hub and contributes to performance improvement.

Documentation

The Related Documentation page in this Open Developer Kit (ODK) has a list of documentation to refer to. For frequently asked questions about the Intel® Curie™ module, visit the Intel® Curie™ Module Support page.

If you need additional help, or want to find and share solutions with Intel® Curie™ users across the world, please visit the Intel® Curie™ Forum in Intel's Support Communities.

Arduino 101* Board

Main Features

Schematics and Layouts

The schematics and CAD files for the Arduino 101 (branded Genuino 101 in some countries) are open source:

Digital and Analog I/Os

The 20 general purpose I/O pins can be used for digital input and digital output. All pins operate at 3.3 V and can be used as interrupt source. Each pin can source or sink a maximum of 20 mA current.

Among the 20 I/O pins:

  • The four pins numbered 3, 5, 6 and 9 can be used for PWM output.
  • The six pins labeled A0, A1, A2, A3, A4, A5 provide a 10-bit resolution and measure values from ground to 3.3 V.
  • The serial pins labeled 0 (RX) and 1 (TX) are used to receive (RX) and transmit (TX) serial data.
  • All pins can trigger an external interrupt, that is an interrupt on a low value, a high value, on a rising edge or a falling edge. In addition, the pins labelled 2, 5, 7, 8, 10, 11, 12, 13 can trigger an external interrupt on a change or value.
  • The digital pin 13 drives a built-in LED. When the pin value is HIGH, the LED is on, when the pin is LOW, the LED is off.
  • The SDA and SCL pins support serial data transfer using I2C/TWI protocol.

Power Supply

There are three ways to power the Arduino 101* (branded Genuino 101* in some countries) board:

  • By using the USB connection
  • By connecting an AC-to-DC adapter into the board's power jack
  • By inserting the leads from a battery in the GND and Vin pin headers of the power connector

List of Material

You need to the following equipment to use the Arduino 101* (branded Genuino 101* in some countries) board with the open developer kit for Intel® Curie™ module:

  • ARM Micro JTAG connector
  • FlySwatter2* or J-Link* debugger
  • FTDI USB to serial converter cable (for example part reference TTL-232r-3v3 or TTL-232R-5v)
  • Standard USB 2.0 A plug to B plug
  • Arduino 101 board

Connection to Host

See Connecting and Verifying the Board Proof of Life.

Getting Started with the Board

See Connecting and Verifying the Board Proof of Life.

tinyTILE* Board 

Main Features

  • Intel® Curie™ module dual-core ((Intel® Quark™ processor core and ARC* core) with Bluetooth® low energy, 6-axis combo sensor and pattern matching engine
  • 14 digital input/output pins (four can be used as PWM output pins)
  • Four PWM output pins
  • Six analog input pins
  • Strictly 3.3 V I/Os only
  • 20 mA DC current per I/O pin
  • 196 kB Flash memory
  • 24 kB SRAM
  • 32 MHz clock speed
  • USB connector for serial communication and firmware updates (DFU protocol)
  • 35 mm length and 26 mm width

Documentation

The following documents are available on element14 website:

Schematics and Layouts

The board support package files and list of material for tinyTILE board are available on element14 website:

Digital and Analog I/Os

The 20 general purpose I/O pins can be used for digital input and digital output. All pins operate at strictly 3.3 V and can be used as interrupt source. Each pin can source or sink a maximum of 20 mA current.

Among the 20 I/O pins:

  • The four pins numbered 3, 5, 6 and 9 can be used for PWM output.
  • The six pins labeled A0, A1, A2, A3, A4, A5 provide a 10-bit resolution and measure values from ground to 3.3 V.
  • The serial pins labeled 0 (RX) and 1 (TX) are used to receive (RX) and transmit (TX) serial data.
  • All pins can trigger an external interrupt, that is an interrupt on a low value, a high value, on a rising edge or a falling edge. In addition, the pins labelled 2, 5, 7, 8, 10, 11, 12, 13 can trigger an external interrupt on a change or value.
  • The SDA and SCL pins support serial data transfer using I2C/TWI protocol.

Power Supply

There are two ways to power the tinyTILE* board:

  • By using the USB connection
  • By inserting the leads from a battery in the GND and Vin pin headers of the power connector

List of Material

You need to the following equipment to use the tinyTILE* board with the open developer kit for Intel® Curie™ module:

Connection to Host

See Connecting and Verifying the Board Proof of Life.

Getting Started with the Board

See Connecting and Verifying the Board Proof of Life.

Tools

The following chapters of this Intel Curie module Open Developer Kit (ODK) provide a set of tools and examples to help you get into production, pass regulatory certification tests, and get to market faster.

Software development tools help you perform necessary tasks such as debugging and converting sketches into valid .cpp files.

An example documents the manufacturing and testing process that the Arduino 101 board underwent and some of examples of board certification are provided. They are not meant as step-by-step task lists to be followed literally, but provided as an example to guide you as you plan and execute your own product's journey through the processes. The last page in this section of the ODK explains how to restore the factory settings on your Arduino 101 board.

The following chapters of this ODK present:

Software Development Tools 

This chapter documents software development tools provided as part of the Intel Curie module Open Developer Kit (ODK) to enable you to perform necessary tasks. they comprise of the following:

Sketch Converter 

The A tree and M tree applications are developed in C or C++. The sketch converter tool available in the A and M trees converts a sketch (.ino) file into a valid .cpp file. Thus, you can import a sketch, and use the tool to convert it to a C++ file, modify the code, compile it and flash it to the board.

Assuming that the sketch (<sketch-filename>.ino) is stored in the <folder-name> folder in arc/examples folder, use the following commands to convert a sketch into a C++ code file:

$ cd $CODK_DIR/arc/examples/<folder-name>
$ make convert-sketch SKETCH=<sketch-filename>.ino

The resulting C++ file created in the application directory will be named <sketch_filename>.ino.cpp

Build and run your application as usual:

$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

JTAG Debugger 

JTAG Connection of tinyTILE Board

Use the TC2050-IDC-050-ALL connector from Tag-Connect* to connect the board to a FlySwatter2 debugger. See information about the connector at: TC2050-IDC-NL-050-ALL

Equipement for the JTAG connection of tinyTILE* boardYou will need the following equipment set for the JTAG connection of the tinyTILE board:

  • A FlySwatter2 hardware debugger (in green on the photo)
  • A cable connector (in red on the photo)
  • A TC2050-IDC-050-ALL connector from Tag-Connect
  • Tag-Connect clip reference TC2050-CLIP)
  • A tinyTILE board from element14

 

Here is a close-up view of the tag-connect cable and the clip with the three fitting holes.

Close-up view of the tag-connect* cable and clip

Insert the probe on the top of the board using the three fitting holes.

Inserting the tag-connect* probe in tinyTILE* fitting holes

Secure the position of the probe by fitting the clip on the bottom side of the board.

Securing the probe with a clip on tinyTILE* board

The entire JTAG connection setup for the tinyTILE board looks as shown on this last photo.

Full JTAG connection setup for tinyTILE* board

JTAG Connection of Arduino 101 Board

Follow the instructions on JTAG connection of Arduino 101 board on https://docs.zephyrproject.org/latest/boards/

Debugging Instructions

OpenOCD is installed with the initial make setup command, and is located in $CODK_DIR/flashpack/bin.

After uploading a binary image to the board, run the command set numbered 1 in the background, and the command set numbered 2 or 3 in a separate window.

Command Set 1

Use the following command to start an OpenOCD session:

$ make debug-server

Command Set 2

Use the following command to start a GDB session:

$ make debug-x86

At the GDB prompt, run the command:

gdb> target remote localhost:3334

Command Set 3

Use the following command to start a GDB session:

$ make debug-arc

At the GDB prompt, run the command:

gdb> target remote localhost:3333

Note GDB may display some expected messages or errors when connecting to the target:

  • Error: Couldn't find core register feature in supplied target description.
  • Warning: Architecture rejected target-supplied description

Manufacturing Process Example

The Intel Curie module manufacturing process example describes the main steps of the manufacturing flow used for the Arduino 101 board. It is provided as an example for makers of printed circuit boards that include an Intel Curie module.

Files

Select the Download button below to download the document. The document refers to some files that can be downloaded as a zip file:

  • Intel Curie Manufacturing Process Example:
    Download
  • Attachments:
    Download
    The attachments include:
    1. flash-jtag_provision.cfg
    2. Arduino101_TestShield_v2.ino
    3. ArduinoTestShield.pdf

Examples of Board Certification

This chapter presents some of the certifications required for a board, using the example of the certifications that were obtained for the Arduino 101 board.

The chapter covers:

International Directives 

A product that is intended for sale and use in worldwide markets must comply with the applicable international requirements for wireless usage, electromagnetic compatibility (EMC), essential safety and usage information, international product environmental compliance requirements (e.g. WEEE, RoHS, REACH, etc.), quality, and, where appropriate, requirements for use in hazardous locations. Products delivered into the European Economic Area (EEA) must comply with the directives of the European Community (EC). Products delivered into North America must comply with the respective directives of each country.

Many countries, including the United States, have legislation based on the EC/EU RoHS rules. The requirements and scope of applicability will, however, differ depending on the specific legislation. All requirements of applicable legislation must be met—based on your geographical distribution.

The rest of this chapter discusses some of the specific requirements.

EC Compliance 

RoHS Directive

All the components and solder alloys used must comply with RoHS directive 2011/65/EU. This directive is aimed at preventing all new electrical and electronic equipment placed on the market in the European Economic Area from containing more than agreed levels of lead, cadmium, mercury, hexavalent chromium, polybrominated biphenyls (PBB) and polybrominated diphenyl ethers (PBBE). Other substances will be restricted effective 2019.

Requirements
  • Compliance with the essential requirements of RoHS Directive 2011/65/EU.
  • Legal declaration of compliance to Directive 2011/65/EU including indication where applicable of RoHS substances in the product, with the appropriate exemption.
  • Maintenance of technical file as evidence of product compliance. File may be comprised of supplier component declarations (e.g.  MDDS for Intel Curie module) as well as material test reports.

REACH

REACH is a regulation imposed by the EU and stands for Registration, Evaluation, Authorization and Restriction of Chemicals. The legislation is managed by the European Chemicals Agency and requires the reporting of Substances of Very High Concern (SVHCs) where present above reportable thresholds in products.

WEEE

The Waste Electrical and Electronic Equipment Directive (WEEE Directive) is European Community directive 2012/19/EU on waste electrical and electronic equipment (WEEE). The first WEEE Directive (Directive 2002/96/EC) entered into force in February 2003. The directive provided for the creation of collection schemes where consumers return their WEEE free of charge. These schemes aim to increase the recycling or re-use of WEEE. In December 2008, the European Commission proposed revising the directive in order to tackle the fast increasing waste stream. The new WEEE Directive, 2012/19/EU, entered into force on 13 August 2012 and became effective on 14 February 2014.

Requirements
  • Compliance with the essential requirements of Directive 2012/19/EU for products that are within its scope.

Other EC Environmental Laws

Many other environmental compliance laws may apply depending on the intended distribution.

Product Safety Directive

The Arduino 101 board complies with the essential requirements on product safety for EC compliance.

Requirements
  • Compliance with the essential requirements of EU Directive 2001/95/CE on product safety.
  • Evaluation against IEC 60950 or IEC 62368.

Electromagnetic Compliance (EMC) Directive

The Arduino 101 board complies with the EMC directive, which ensures that electrical and electronic equipment does not generate, or is not affected by, electromagnetic disturbance.

Requirements
  • Compliance with the essential requirements of EU Directive 2014/30/EU on EMC.
  • Evaluate against IEC 55032 and IEC 55035.

CE Marking Directive

The Arduino 101 board complies with the CE marking directive.

Requirements

Compliance with the essential requirements of EU Directive 93/68/EEC on CE marking

Radio Equipment Directive (RED)

The Radio Equipment Directive (RED) ensures that radio equipment complies with spectrum requirements for wireless transmission and is immune to the environment around it.

Requirements
  • Comply with the essential requirements of RED directive 2014/53/EU on radio equipment.
  • If appropriate harmonized standard to RED does not exist, test to harmonized standard for R&TTE Directive and switch to RED when harmonized standard is available.
  • Test with a notified body.

US Compliance Requirements

FCC Compliance 

Part 15 of FCC Title 47– Radio Frequency Devices

Devices must comply with Title 47 Part 15, of the Federal Communications Commission rules and regulations regarding unlicensed transmissions (often just referred to as Part 15.209 of the FCC rules). Operation of the device is thus subject to the following two conditions:

  1. The device may not cause harmful interference
  2. The device must accept any interference received, including interference that may cause undesired operation.

Class B digital device

The Arduino 101 board has been tested and found to comply with the limits for a Class B digital device, pursuant to Part 15 of FCC rules.

These limits are designed to provide reasonable protection against harmful interference in a residential area. The equipment generates, uses, and can radiate radio frequency energy and, if not installed and used in accordance with the instructions, may cause harmful interference to radio communication.

Wireless Device

For wireless devices, conformity assessment for FCC Part 15.247 must be completed at an FCC-listed laboratory.

FCC Policy on Human Exposure to Radio Frequency Electromagnetic Fields

The Federal Communications Commission (FCC) evaluates the effect of emissions from FCC-regulated transmitters on the quality of the human environment.

The American National Standards Institute (ANSI), the Institute of Electrical and Electronics Engineers, Inc. (IEEE), and the National Council on Radiation Protection and Measurements (NCRP) have issued recommendations for human exposure to RF electromagnetic fields.

The Arduino 101 board meets the requirements for human exposure detailed in Parts 1 and 2 of the FCC's Rules and Regulations [47 C.F.R. 1.1307(b), 1.1310, 2.1091, 2.1093].

Requirements

The Commission's requirements are detailed in Parts 1 and 2 of the FCC's Rules and Regulations [47 C.F.R. 1.1307(b), 1.1310, 2.1091, 2.1093].

US National RoHS Rules

As mentioned above, the United States, like many other countries, has legislation based on the EC/EU RoHS rules. The requirements and scope of applicability differ from the EU rules. All requirements of applicable legislation must be met—based on your geographical distribution.

IC Compliance (Canada) 

Interference-Causing Equipment Standard ICES-003

ICES-003, Issue 6, sets out the standard requirements for information technology equipment (ITE), including digital apparatus.

Devices must not exceed the Class B limits for radio noise emissions.

Requirements
  • Following the guidelines laid out in ICES-003 Information Technology Equipment (including Digital Apparatus) – Limits and Methods of Measurements
  • A test report in accordance with the requirements of the reference publication used from Section 3 Normative Reference Publications.

Board Default Factory Settings

Restoring the Board Default Factory Settings

Follow these steps to restore the factory settings on your Arduino 101 board.

  1. Download the Arduino 101 factory settings available at
    Arduino 101 Software Package
  2. Extract the compressed or archived file arduino101-factory_recovery-flashpack.tar.bz2
  3. Move the folder arduino101-factory_recovery-flashpack generated in Step 2 to the CODK folder.
  4. Connect your Arduino 101 board to the computer.
  5. Run the following commands to flash the default factory settings to the board:
    $ cd arduino101-factory_recovery-flashpack
    $ ./flash_dfu.sh

    Reset the board when prompted.

Tutorials

This section presents tutorials or application examples based on the main features of Intel Curie module and the open software developer firmware and software trees and tools. Each tutorial uses at least one Intel Curie module's libraries:

CurieMailbox - Message Passing between Cores 

This tutorial presents two examples of message passing between the Intel Quark SE processor core (x86) and the ARC processor in the Intel Curie module, using CurieMailbox library. The inter-processor mailbox is used for the interrupt-based communication between the x86 core processor and ARC processor.

Warning If you send a mailbox message and the destination processor is not configured to receive it, the source processor will busy-wait indefinitely. Don’t write to the mailbox unless you know the other processor is configured to receive messages.

SharedCounter example

The SharedCounter example is stored in ~/CODK/CODK-M/arc/corelibs/libraries/CurieMailbox/examples folder.

The SharedCounter example demonstrates how a single integer value is shared between the x86 processor and ARC cores. The sketch runs on ARC core. It first sends a value of zero using CureMailbox.put() function. The value is received by the application running on x86 processor core which increments the value by one and sends it back via the mailbox. The sketch reads the reply, increments the value by one and sends it back to x86 processor core via the mailbox. The count value increments indefinitely.

The SharedCounter example uses files stored in x86-samples/CurieMailbox_SharedCounter folder and arc/corelibs/libraries/CurieMailbox/examples/SharedCounterfolder.

The arc/corelibs/libraries/CurieMailbox/examples/SharedCounter folder includes the SharedCounter.ino sketch file that needs converting to a C++ file.

Setting up the Environment

$ cd CODK-M
$ export CODK_DIR=$(pwd)
$ source ../zephyr/zephyr-env.sh

Preparing the SharedCounter ARC Application

Use the sketch converter and set the ARC_PROJ_DIR variable:

$ cd $CODK_DIR/arc/corelibs/libraries/CurieMailbox/examples/SharedCounter
$ make convert-sketch SKETCH=SharedCounter.ino
$ export ARC_PROJ_DIR=$(pwd)

Preparing the SharedCounter x86 Application

Run the following commands:

$ cd $CODK_DIR
$ export X86_PROJ_DIR=x86-samples/CurieMailbox_SharedCounter

Opening and Setting Up CuteCom

Open CuteCom application in another terminal window.

$ cutecom &

In CuteCom window, update the serial port device name to dev/ttyACM0 (name confirmed when running the grep tty command) and the Baud rate to 9600.

Running the SharedCounter Example

In the first terminal window where you prepared the SharedCounter ARC and x86 applications, run the commands to compile the example and view the output on the serial monitor.

$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Press the reset button on the board if/when prompted. The code should execute within 5 seconds. Wait for a few seconds. Move to the CuteCom window and select Open Device to view the code execution.The output on the serial monitor will look as shown below:

Output of the SharedCounter example

String

The String example is stored in ~/CODK/CODK-M/arc/corelibs/libraries/CurieMailbox/examples folder.

The String example demonstrates sending a short string of less than 16 characters from the x86 processor core to the AR core. The sketch enables the mailbox channel 0 for receiving messages, and for each received message it prints out the channel payload as a string of ASCII characters.

The String example uses files stored in x86-samples/CurieMailbox_String folder and arc/corelibs/libraries/CurieMailbox/examples/String folder.

The arc/corelibs/libraries/CurieMailbox/examples/String folder includes the String.ino sketch file that needs converting to a C++ file.

Setting up the Environment

$ cd CODK-M
$ export CODK_DIR=$(pwd)
$ source ../zephyr/zephyr-env.sh

Preparing the String ARC Application

Use the sketch converter and set the ARC_PROJ_DIR variable:

$ cd $CODK_DIR/arc/corelibs/libraries/CurieMailbox/examples/String
$ make convert-sketch SKETCH=String.ino
$ export ARC_PROJ_DIR=$(pwd)

Preparing the String x86 Application

Run the following commands:

$ cd $CODK_DIR
$ export X86_PROJ_DIR=x86-samples/CurieMailbox_String

Opening and Setting Up CuteCom

Open CuteCom application in another terminal window.

$ cutecom &

In CuteCom window, update the serial port device name to dev/ttyACM0 (name confirmed when running the grep tty command) and the Baud rate to 9600.

Running the String example

In the first terminal window where you prepared the String ARC and x86 applications, run the following commands to compile the example and view the output on the serial monitor.

$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Press the reset button on the board if/when prompted. The code should execute within 5 seconds. Wait for a few seconds. Move to the CuteCom window and select Open Device to view the code execution.

CuriePME - Using the DrawingInTheAir Example

This section explains how to get and run the DrawingInTheAir example to try the pattern matching engine ability to learn and recognize letters. The following section illustrates how some functions of the pattern matching engine library (CuriePME) and CurieIMU are used in the DrawingInTheAir code for the successive parts of the code that involve learning, collecting data, reading data, filtering data and learning letters.

Running the DrawingInTheAir Example

The DrawingInTheAir example guides the users to draw letters in the air using their Arduino 101* (branded Genuino 101* in some countries) board as an imaginary pen, and have the letters being recognized as patterns stored in the accelerometer data from the CurieIMU library. The DrawingInTheAir example is available in the examples folder of the Intel-Pattern-Matching-Technology library.

Requirements

You need to connect a push button to digital pin 4 on the Arduino 101 board. We suggest the following:

To use the DrawingInTheAir example, first clone the Pattern-Matching-Engine-Technology GitHub*

$ git clone https://github.com/01org/Intel-Pattern-Matching-Technology

Store the content of the repository under CODK-A/arc/libraries or under CODK-M/arc/libraries.

Open the Makefile of the DrawingInTheAir example and check that the paths to the three library files Arduino.h, CurieIMU.h and CuriePME.h are correct

LIBDIRs = $(ARDUINOSW_DIR)/libraries/Intel-Pattern-Matching-Technology/src \
                   $(ARDUINOSW_DIR)/corelibs/libraries/CurieIMU/src \
                   $(ARDUINOSW_DIR)/corelibs/cores/arduino \

From within the CODK-A directory, or the CODK-M directory, check your serial device name:

$ dmesg | grep tty

Open Cutecom in a different window, update the serial port name in Cutecom if needed, as illustrated in Connecting and Verifying the Board Proof of Life

$ cutecom &

In the terminal window, run the following command to convert the DrawingInTheAir.ino sketch to a C++ file and build the application:

$ export CODK_DIR=$(pwd)
$ cd $CODK_DIR/arc/libraries/Intel-Pattern-Matching-Technology/examples/DrawingInTheAir
$ make convert-sketch SKETCH=DrawingInTheAir.ino
$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

Go to the Cutecom window, wait for a few seconds and click on the Open device button. You should see the conversions running:

Hex string to ASCII characters conversion running in Cutecom

Drawing Letters in the Air

The DrawingInTheAir example currently has the following settings:

  • At least four drawing trials for each letters
  • Draw the letters A, B, C, D, E and F (A-F)
  • The sample rate is 200 Hz that is 200 X, Y and Z values per second
  • Each neuron can hold 128 bytes (vectorNumBytes variable)

Setup the board with the push button. The photo shows the Grove button plugged to the digital pin 4 (D4) on the Base Shield placed on top of the Arduino 101.

Using the Grove button and base shield with Arduino 101*

Run the following commands in the main terminal:

$ cutecom &
$ export CODK_DIR=$(pwd)
$ cd $CODK_DIR/arc/libraries/Intel-Pattern-Matching-Technology/examples/DrawingInTheAir
$ make convert-sketch SKETCH=DrawingInTheAir.ino
$ make compile
$ make upload SERIAL_PORT=/dev/ttyACM0

You should see the following on the terminal window:

Confirmation of the DrawingInTheAir data transfer

Wait for a few seconds and switch to the Cutecom terminal and click on the Open device button. The program will prompt you to draw a letter, and will record the IMU data while the button is being held.

Capturing the letters A to F

When you draw a letter, the program converts the data into a suitable vector and uses the pattern matching engine to classify the vector by returning a category from 1 to 26, representing a letter from A to Z. Note that with the actual set of variables, the program should only consider the letters A to F, that is the categories 1 to 6.

Once you have drawn each letter A to F four times, the program takes you to the next step where you draw one of the six letters and the program displays the letter that has been recognized or the message "Don't recognize that one - - try again".

Capturing of letter is complete. Now draw some letters

Run the program a few times in a raw until you master drawing the letters A to F in the air. Gradually you should get more letters recognized.

Getting your letters recognized by the PME

How to Develop your Own Application Using CuriePME 

Breakdown of the DrawingInTheAir Example

This section illustrates how some functions of CuriePME and CurieIMU libraries are used in the DrawingInTheAir code for the successive parts of the code that involve learning, collecting data, reading data, filtering data and learning letters.

The DrawingInTheAir example uses the learn() and classify() functions of the Pattern Matching Engine API. The API Reference for the Intel Pattern Matching Technology Library is available in the Documentation section. The basic functions supported by the CuriePME library include:

  • Learning patterns
  • Recognizing patterns
  • Storing pattern memory (Knowledge) [Requires the SerialFlash library]
  • Retrieving pattern memory (Knowledge) [Requires the SerialFlash library]

The example also uses functions of the CurieIMU library.

Learning Patterns

Before the pattern matching engine (PME) can be used to recognize (or categorize or classify) any pattern, we first have to feed the PME with some examples of the patterns we are looking for. The CuriePME library provides the learn() function for this:

uint16_t CuriePME.learn (uint8_t vector[], int32t vector_length, uint16_t category)

vector is an array containing the data that represents our pattern. category is the category that should be assigned to that pattern. By calling the function learn()with for example a category value of 1, we are effectively saying to the PME "Here is an example of a pattern that belongs to category 1. If you see another pattern that looks like this, then you should know it is category 1".

The pattern matching engine is a network of 128 individual memory units, called neurons. Each neuron can hold 128 bytes of data. Each time you call learn(), it writes your pattern to a new neuron in the network. This means that the maximum pattern size that can be accepted by learn() (that is the maximum value for vector_length) is 128 bytes, since the entire pattern must fit inside a single neuron.

As there are 128 neurons in total, you will eventually run out if you call learn()enough times. You can check how many neurons have been used up using the getComittedCount() function, as shown below:

if (CuriePME.getComittedCount() == CuriePME.MaxNeurons) {
    /* Network is fully committed */
}

Classifying the Data

Classifying allows to pass a new pattern to the pattern matching engine, and the pattern matching engine will report whether the new pattern matches any of its learned categories or not. The CuriePME library provides the classify() function for this:

uint16_t CuriePME.classify (uint8_t vector[], int32_t vector_length)

Sketch #1: Using the PME to Convert Hex Strings to ASCII Characters

This sketch trains the pattern matching engine (PME) to associate a very simple pattern with each letter of the alphabet. The pattern for each letter is a string of two ASCII characters representing the hex digits that make up that character's place in the ASCII table. For example, the letter "a" has a numerical value of decimal 97, or hex 61. To train the PME for the letter "a", we give the pattern "61" to the PME, and indicate that it belongs in category 97. We repeat this for all 26 letters from "a" to "z".

When the training is complete the users can enter strings via the serial monitor (for example "7a"), which will be written directly to the PME as a pattern to be classified. The PME will respond with the corresponding ASCII character (for example "z").

Below is the code used to train the PME

#include "CuriePME.h"

void setup()
{
    Serial.begin(9600);
    while(!Serial);

    /* Start the PME (Pattern Matching Engine) */
    CuriePME.begin();
    trainLetters();

    Serial.println("Training complete.");

    Serial.print("Write a hex value (e.g. 6e), and the PME will return the");
    Serial.println(" corresponding ASCII character");
}

void loop ()
{
    char buf[5];
    uint8_t vector[4];
    unsigned int category;

    while (readNumFromUser(buf, 5) < 2) {
        Serial.println("Expecting 2 hex characters, e.g. 6f");
    }

    vector[0] = vector[2] = buf[0];
    vector[1] = vector[3] = buf[1];

    category = CuriePME.classify(vector, 4);

    if (category == CuriePME.noMatch) {
        Serial.println("Don't recognize that one-- try again.");
    } else {
        Serial.println(String((char)category));
    }
}

int isLineEnding (char c)
{
    return (c == '\n' || c == '\r');
}

int readNumFromUser (char buf[], int bufsize)
{
    int i = 0;
    char c;

    /* Wait for something to read */
    while (Serial.available() == 0);

    /* Read new characters into buffer until full, or until line ending is seen */
    while (Serial.available() > 0) {
        while (i < (bufsize - 1) && !isLineEnding(c = Serial.read())) {
          buf[i++] = c;
        }

        Serial.read();
    }

    buf[i] = 0;
    return i;
}

void trainLetter (const char *buf, uint8_t category)
{
    uint8_t vector[4];

    /* Write pattern twice, to ensure a large-enough distance
     between categories */
    vector[0] = vector[2] = buf[0];
    vector[1] = vector[3] = buf[1];

    CuriePME.learn(vector, 4, category);
}

void trainLetters (void)
{
    trainLetter("61", 'a');
    trainLetter("62", 'b');
    trainLetter("63", 'c');
    trainLetter("64", 'd');
    trainLetter("65", 'e');
    trainLetter("66", 'f');
    trainLetter("67", 'g');
    trainLetter("68", 'h');
    trainLetter("69", 'i');
    trainLetter("6a", 'j');
    trainLetter("6b", 'k');
    trainLetter("6c", 'l');
    trainLetter("6d", 'm');
    trainLetter("6e", 'n');
    trainLetter("6f", 'o');
    trainLetter("70", 'p');
    trainLetter("71", 'q');
    trainLetter("72", 'r');
    trainLetter("73", 's');
    trainLetter("74", 't');
    trainLetter("75", 'u');
    trainLetter("76", 'v');
    trainLetter("77", 'w');
    trainLetter("78", 'x');
    trainLetter("79", 'y');
    trainLetter("7a", 'z');
}

Tutorial Sketch #1 Breakdown

We said that we were going to read two characters from the user, and write them directly to the PME to be classified. You may have noticed that we are not really doing that. Here is the function that takes the input string, something like "6f", and writes it to the PME for learning:

void trainLetter (const char *buf, uint8_t category)
{
    uint8_t vector[4];

    /* Write pattern twice, to ensure a large-enough distance
     between categories */
    vector[0] = vector[2] = buf[0];
    vector[1] = vector[3] = buf[1];

    CuriePME.learn(vector, 4, category);
}

The actual pattern that is passed to the learn() function is double the size- 4 bytes. We start with the input pattern like this:

contents of buf = "6f"

But we actually write this to the pattern matching engine:

contents of vector[] = {'6', 'f', '6', 'f'}

Why do we do this?

The basic reason is that if we don't write this, the categories for each letter will not be distinct enough, and we will get unexpected results when we try to classify. Take for example the first two categories , "a" and "b", whose patterns are "61" and "62" respectively. The raw data for each hex string looks like two ASCII characters "6" and "1". The Hex representation for "6" and "1" is 0x3631. Similarly, "62" is 0x3632. There is only a difference of 1 between the categories "a" and "b" but if we write the values twice successively, like 0x36313631 and 0x36323632, the difference between the two values is much bigger, 1001 in this example.

This problem we encounter with letters would probably not occur when working with "real" data. When larger and more complex patterns are used, for example with patterns derived from sensors or other real-world inputs, it is rather unlikely that there would be such a small difference between neurons of different categories.

With the letters example, the input strings are very simple and they never change so it is easy to test for equality. Either the string matches "61", and it represents "a", or it does not match. There is no in-between. But what if the pattern was not so simple? What if we need, instead of a way to test exact equality, a way to know if something is "almost" equal? This is where the real power of the PME becomes apparent. The classify() function lets you ask "Are these two items close enough to be considered equal?" and the learn() function allows you to define what "close enough" means.

Sketch #2: Using the IMU Data to Create a More Complex Pattern

Extra hardware required: For this sketch you will need a button connected to your Arduino 101* board, or a shield with a button built-in, for example from Sparkfun (SparkFun Danger Shield), which includes a button connected to digital pin 12. You can also use the Grove-Button and the Base Shield of the Grove Starter Kit Plus Intel® IoT Edition.

To use more complex patterns with the pattern matching engine we first need to analyze the data we have collected, and try to identify the patterns ourselves. Since the DrawingInTheAir example uses accelerometer data from the CurieIMU library, we start by collecting some accelerometer data.

Collecting Accelerometer Data

The following sketch waits until you press and hold the connected button, and while the button is being held, the accelerometer data is saved into a buffer at a rate of 100 Hz, that is 100 values per seconds for X, Y and Z.

#include "CurieIMU.h"

const unsigned int buttonPin = 12;

/* reading the accelerometer 100 times per second */
const unsigned int sampleRateHZ = 100;

void setup()
{
    Serial.begin(9600);
    while(!Serial);

    /* Set button pin as input */
    pinMode(buttonPin, INPUT);

    /* Start the IMU (Inertial Measurement Unit), enable accelerometer only */
    CurieIMU.begin(ACCEL);

    CurieIMU.setAccelerometerRate(sampleRateHZ);
    CurieIMU.setAccelerometerRange(4);
}

void loop ()
{
    int buf[1000];
    unsigned int numSamples = 0;

    /* Record IMU data while button is being held */
    numSamples = readFromIMU(buf, sizeof(buf));
    Serial.println("Read " + String(numSamples) + " samples from IMU");
}

unsigned int readFromIMU(int buf[], unsigned int buf_size)
{
    unsigned int i = 0;

    /* Wait until button is pressed */
    while (digitalRead(buttonPin) == HIGH);

    Serial.println("Recording motion... ");

    /* While button is being held... */
    while (digitalRead(buttonPin) == LOW) {

        /* Wait for new accelerometer data to be ready */
        if (CurieIMU.dataReady()) {

            /* Read the new x, y & z values into the buffer */
            CurieIMU.readAccelerometer(buf[i], buf[i + 1], buf[i + 2]);

            i += 3;

            /* If the buffer doesn't have enough space for the
             * next x, y & z values, we're finished. */
            if (i + 3 > buf_size) {
                break;
            }
        }
    }

    Serial.println("Got it!");
    return (i + 1) / 3;
}

Graphical Representation of the Captured Data

The sketch reads the accelerometer data into a buffer while the button is held, and when the button is released it prints the number of bytes that were read from the IMU. To identify any pattern in the data  we first need to visualize the data. For this we are going to wave the board into the air and make some pictures of the data so we can identify some patterns.

In the following graphs, the x-axis represents the time in terms of samples recorded. These samples are recorded at a frequency of 100 Hz. There are 230 samples in total, which means that the button was held for approximately 2.3 seconds. The y-axis represents the value read by the accelerometer in the value range of [-32768; 32767].

The first graph shows how data is captured when the board is help still and flat on a desk while holding the button.

Keeping the board still and flat on the desk while holding the button
Data captured with the board kept still and horizontal on a desk while holding the button

The second graph shows the data captured when gently shaking the board up and down, as if shaking hands, while holding the button.

Data captured when gently shaking the board up and down
Data captured when gently shaking the board up and down while holding the button

The third graph illustrates how data is captured when drawing a full circle in the air while holding the button.

Data captured when drawing a full circle in the air
Data captured when drawing a full circle in the air

The fourth graph illustrates how the accelerometer captures the data when we draw the letter "A" in the air.

Data captured when drawing the letter A
Data captured when drawing the letter "A" in the air

The fifth graph illustrates how the accelerometer captures the data when we draw the letter "B" in the air.

Data captured when drawing the letter B
Data captured when drawing the letter "B" in the air

The sixth graph illustrates how the accelerometer captures the data when we draw the letter "C" in the air.

Data captured when drawing the letter C
Data captured when drawing the letter "C" in the air

We see from the graphs that drawing the letters A, B and C produce rather different patterns. To see if the patterns are repeatable, we have drawn the letter A three times in a row.

Data captured when drawing the letter  a first time (left side), second time (center) and third times (right side)

The three graphs show that drawing the letter "A" three times produces a consistent pattern. With the exception of some low noise and minor variations, those three graphs are different versions of the same pattern.

We could assume that writing data directly to the pattern matching engine completes the training and classification. However, there are two major issues: first there is too much data, and second the data is too noisy.

Filtering the Data, Incorporating an Averaging Filter and Undersampling

The program uses a simple moving average filter to remove the now noise and some other small anomalies. This has the effect of smoothing out the data stream.

The stream of raw accelerometer data must be compressed into 128 bytes to fit into one neuron while still preserving as much of the original pattern as possible. This means that the program will only keep 1 to 2 seconds of the accelerometer data at 200 Hz and throw away 90% of the data. This is done in two ways:

  1. Way 1 - Each sample consists of three signed 16 bit values (one value for each X, Y and Z). The program maps each 16 bit value to a range of 0-255 and packs it into one byte. This cuts the sample size into half.
  2. Way 2 - Undersampling: If we are sampling (capturing accelerometer data) at 200 Hz and the button is held for 1.2 seconds, there will be approximately 240 samples. Each sample once compressed will occupy three bytes of the neuron's 128 bytes (three 16-bit values packed into three bytes as explained above). This means that we can only fit the result of 128 divided by 3, that is ~ 42 (42.666) of the 240 samples into a single neuron. The program will take every 5th sample until there are 42, in order to cover most of the sample window and keep some semblance of the original pattern.

Way 1 - Using the map() function to Cut the Sample Size

Considering the graphs captured for the letter "A" from above, we see that drawing the letter "A" takes an average of 1.6 seconds, which is 160 samples at a frequency of 100 Hz. Each sample (that is each call to curieIMU.readAccelerometer() function) takes up to three integer values (X, Y, Z) which are 4 bytes each, that is 12 bytes per sample.

int X, Y, Z;

/* Read raw IMU data, 12 bytes in total for all 3 values */
CurieIMU.readAccelerometer(X, Y, Z);

This makes a total of 1920 bytes (160 * 12) that we have to capture to represent the letter "A", which is far too much as the pattern cannot be larger than 128 bytes (the size of each neuron). We need to find a way to throw away 90% of the raw data, without destroying the original pattern.

One way to decrease the number of bytes captured by the accelerometer is to decrease the range of sample values. The accelerometer returns values in the range of -32,768 to 32,767, and we are using  a 4-byte integer to store each one. This is quite a big range, and we probably don't need such high precision for this application. So we could map each value from the original range to the 0-255 range, meaning that each value can fit in a single byte, so we will only need 3 bytes to store a single sample of x, y and z values. We can do this quite easily with the Arduino* map() function:

int X, Y, Z;
byte x, y, z;

/* Read raw IMU data, 14 bytes in total for all 3 values */
CurieIMU.readAccelerometer(X, Y, Z);

/* Map to smaller range, now 3 bytes in total for all 3 values */
x = (byte) map(X, -32768, 32767, 0, 255);
y = (byte) map(Y, -32768, 32767, 0, 255);
z = (byte) map(Z, -32768, 32767, 0, 255);

Now we are going to modify the readFromIMU() function from example sketch 2 to map the X, Y and Z values from the accelerometer and store them in byte array instead of an integer array:

/* Use some named constants to make life easier */
const int IMULow = -32768;
const int IMUHigh = 32767;

unsigned int readFromIMU(byte buf[], unsigned int buf_size)
{
    unsigned int i = 0;
    int temp[3];

    /* Wait until button is pressed */
    while (digitalRead(buttonPin) == HIGH);

    Serial.println("Recording motion... ");

    /* While button is being held... */
    while (digitalRead(buttonPin) == LOW) {

        /* Wait for new accelerometer data to be ready */
        if (CurieIMU.dataReady()) {

            /* Read the new x, y & z values into the buffer */
            CurieIMU.readAccelerometer(temp[0], temp[1], temp[2]);

            buf[i]     = (byte) map(temp[0], IMULow, IMUHigh, 0, 255);
            buf[i + 1] = (byte) map(temp[1], IMULow, IMUHigh, 0, 255);
            buf[i + 2] = (byte) map(temp[2], IMULow, IMUHigh, 0, 255);

            i += 3;

            /* If the buffer doesn't have enough space for the
             * next x, y & z values, we're finished. */
            if (i + 3 > buf_size) {
                break;
            }
        }
    }

    Serial.println("Got it!");
    return (i + 1) / 3;
}

Now the readFromIMU() function crushes each integer value into a byte, and makes the data 75% smaller. The following graphs show the data captured when drawing the letter "A" before and after using the map() function. Notice the smaller scale on the x-axis in the second picture as it only goes to 255.

The first graph shows the raw data captured by the accelerometer when drawing the letter A:

Raw captured data when drawing the letter

This second graph shows the accelerometer data mapped to a range of 0-255:

Mapped data in the range 0-255 when drawing the letter

The two graphs look very similar. The original pattern is almost completely intact and we threw away 75% of the data. But we still have some problems; even though each value is now 1 byte instead of 4 bytes, there is still too much data. In the graph shown above there are still 198 samples (the button was held for about 2 seconds), and given that each sample holds one byte each for X, Y and Z, the total size of the captured data is 594 bytes (198 * 3). This is still a lot more than the 128 bytes that are available in a neuron.

So, what else can we do? We could compress the accelerometer values even more, so each value takes up less than a byte, but that will make things really complicated. We will stick to 1 byte per accelerometer value, which also means that our pattern size is limited to 42 samples, or 126 bytes (42 * 3 = 126) -- just two bytes less than a neuron's full capacity.

Way 2 - Undersampling

The definition of undersampling in Wikipedia is as follows:

undersampling is a technique where one samples a bandpass-filtered signal at a sample rate below its Nyquist rate (twice the upper cutoff frequency), but is still able to reconstruct the signal

For us, this means that we will only take the samples that we need to accurately reproduce our pattern. We already know that we can only take 42 samples so we will simply stretch those 42 samples over the entire sample window.

Taking as an example the pattern from drawing the letter "A" in the previous section, which was 198 samples in total, we would need to take every 4th sample to cover the sample window with only 42 samples (198 / 42 = 4.71).

To illustrate what it would look like, the following graph shows the 198 samples that we saw in the previous section, but with every fourth point highlighted in the z-axis, so you can get an idea of what the final 42 samples would look like.  We would be taking the same points for x-axis and y-axis but these are not represented to avoid crowding the graph.

Highlight of one every fourth sample on the z-axis on mapped captured data when drawing the letter

Now we are going to write a function that does the undersampling. This function takes the full buffer of samples, determines how many samples must be skipped to cover the whole window with 42 samples, then iterates through the sample buffer (skipping the calculated number of samples with every step) and places the resulting 42 samples in an output buffer.

/* Number of processed samples (1 sample == accel x, y, z)
 * that can fit inside a neuron */
const unsigned int samplesPerVector = (CuriePME.maxVectorSize / 3);

void undersample(byte input[], int numSamples, byte output[])
{
    unsigned int oi = 0; /* Current position in output sample buffer */
    unsigned int ii = 0; /* Current position in input sample buffer */

    /* No. of samples to skip for each iteration */
    unsigned int step = numSamples / samplesPerVector;

    for (unsigned int i = 0; i < samplesPerVector; ++i) {
        for (unsigned int j = 0; j < 3; ++j) {
            /* Cherry-pick the (ii + j)th sample from sample window */
            output[oi + j] = input[ii + j];
        }

        /* Skip 'step' samples */
        ii += (step * 3);
        oi += 3;
    }
}

By passing the output of readFromIMU() function into the new undersample() function we will always get 42 samples out. However, this brings us to the second problem which is that the data is too noisy.

Looking at all the graphs of the captured accelerometer data from above, you will notice that all three axis consist of rather wobbly and shaky lines. The lines aren't smooth at all, as if they had been drawn with a pen and our hand constantly shaking. There are a few reasons for this. First of all, all the muscles in our hand and fingers are constantly making little movements and adjustments as we are holding the board. Even though we think that we have moved our hand in a straight line, the accelerometer is able to pick up a lot of little movements. Additionally, the accelerometer is not perfect, wo it will not be 100% accurate, and there will always be a little bit of noise in the signal.

All these little anomalies make it more difficult for the pattern matching engine to classify patterns, and we need to clean them up somehow.

Using an Averaging Filter

An averaging filter is a simple and effective way to smooth out a data set. It is commonly used for streams of ADC or accelerometer readings. In this section we explain how we can modify the undersample() function to incorporate an averaging filter.

As was shown with the undersample() function (continuing with the same example data set of 198 samples), we are only taking every fourth sample from the sample window, and throwing away the rest. But instead of throwing away the in-between samples, we can use them! See the following lines from the undersample() function where we copy a sample from the input buffer to the output buffer:

 /* Cherry-pick the (ii + j)th sample from sample window */
    output[oi + j] = input[ii + j];

Instead of just copying the samples "as is" from the input buffer, we can calculate the sum of this sample and the four skipped samples before it. Then we take the average of these samples to create a new sample which is the average of the five previous samples, and write that sample to the output buffer.

Now we are going to write a new function called getAvergaeSample() which can be used by undersample() function to get an average sample:

/* Number of processed samples (1 sample == accel x, y, z)
 * that can fit inside a neuron */
const unsigned int samplesPerVector = (CuriePME.maxVectorSize / 3);

byte getAverageSample(byte samples[], unsigned int num, unsigned int pos, unsigned int step)
{
    unsigned int ret;

    /* This is the number of samples that will be used to create each
     * average sample; i.e. all the skipped samples before and after
     * the current sample */
    unsigned int size = step * 2;

    /* Don't do any averaging, if we are at the beginning or end
     * of the sample window */
    if (pos < (step * 3) || pos > (num * 3) - (step * 3)) {
        ret = samples[pos];
    } else {
        ret = 0;
        pos -= (step * 3);

        /* Calculate the sum of 'step' samples before, and after,
         * the current sample */
        for (unsigned int i = 0; i < size; ++i) {
            ret += samples[pos - (3 * i)];
        }

        ret /= size;
    }

    return (byte)ret;
}

void undersample(byte input[], int numSamples, byte output[])
{
    unsigned int oi = 0; /* Current position in output sample buffer */
    unsigned int ii = 0; /* Current position in input sample buffer */

    /* No. of samples to skip for each iteration */
    unsigned int step = numSamples / samplesPerVector;

    for (unsigned int i = 0; i < samplesPerVector; ++i) {
        for (unsigned int j = 0; j < 3; ++j) {
            /* Get an average sample for the current position
             * in the sample window */
            output[oi + j] = getAverageSample(input, numSamples, ii + j, step);
        }

        /* Skip 'step' samples */
        ii += (step * 3);
        oi += 3;
    }
}

This removes most of the noise while leaving the original pattern intact. We can now compare the graphs showing the raw data and the the final compressed, undersampled and smoothed data signal.

The first graph shows the raw accelerometer data when drawing the letter "A" in the air (2376 bytes in total)

Raw captured data when drawing the letter

The second graph shows the compressed, undersampled and smoothed data (126 bytes in total):

Compressed, undersampled and smoothed data of drawing a letter

We threw away 94.7% of the original data and still have a recognizable pattern. We have finished processing the data. We have the tools we need to turn the raw accelerometer data into patterns that the PME can learn and recognize.

As a final step for the pattern processing part, we are going to modify the readFromIMU() function to use the new undersampling function on the collected accelerometer data:

void readFromIMU(byte vector[])
{
    byte accel[sensorBufSize];
    int raw[3];

    unsigned int i = 0;

    /* Wait until button is pressed */
    while (digitalRead(buttonPin) == LOW);

    /* While button is being held... */
    while (digitalRead(buttonPin) == HIGH) {
        if (CurieIMU.dataReady()) {

            CurieIMU.readAccelerometer(raw[0], raw[1], raw[2]);

            /* Map raw values to 0-255 */
            accel[i] = (byte) map(raw[0], IMULow, IMUHigh, 0, 255);
            accel[i + 1] = (byte) map(raw[1], IMULow, IMUHigh, 0, 255);
            accel[i + 2] = (byte) map(raw[2], IMULow, IMUHigh, 0, 255);

            i += 3;

            /* If there's not enough room left in the buffers
            * for the next read, then we're done */
            if (i + 3 > sensorBufSize) {
                break;
            }
        }
    }

    undersample(accel, i / 3, vector);
}

Learning the Letters

We are now going to write a function called trainLetters(), that uses the readFromIMU() function to obtain the 126-byte pattern from the IMU. Then, we are going to call the CuriePME.learn() function using that 126-byte pattern as an input, and using the letters of the alphabet as categories. Here is the code for the trainLetters() function:

/* This function trains a single letter, by asking the user to
 * draw it 'repeat' times */
void trainLetter(char letter, unsigned int repeat)
{
    unsigned int i = 0;

    while (i < repeat) {
        byte vector[vectorNumBytes];

        if (i) Serial.println("And again...");

        /* Read IMU data and convert to 126-byte filtered pattern */
        readFromIMU(vector);

        /* Train the PME with the pattern. Convert letter from normal ASCII
         * range (65-90) to 1-based range (1-26).
         *
         * If you don't want to do that conversion, you don't need to--
         * things will still work if the categories range from 65-90 */
        CuriePME.learn(vector, vectorNumBytes, letter - upperStart);

        Serial.println("Got it!");
        delay(1000);
        ++i;
    }
}

/* This function calls 'trainLetter()' on each letter of the alphabet */
void trainLetters()
{
    /* You can change this to 'Z', if you want... it just takes a long time
     * to train the whole alphabet. My arm gets tired :) */
    for (char i = 'A'; i <= 'F'; ++i) {
        Serial.print("Hold down the button and draw the letter '");
        Serial.print(String(i) + "' in the air. Release the button as soon ");
        Serial.println("as you are done.");

        /* Train the current letter, and prompt user to draw the
         * letter 4 times in a row */
        trainLetter(i, 4);

        Serial.println("OK, finished with this letter.");
        delay(2000);
    }
}

That's it. We have finished the whole DrawingInTheAir example. While we did not discuss the code in the setup() and loop() functions, it should be rather easy to figure out, so it is left as an exercise for you.

Congratulations on making it all the way to the end of this (quite long) tutorial; hopefully you will now feel confident about using the CuriePME library in your own projects.

Documentation 

This chapter of Intel® Curie™ Open Developer Kit includes the list of references to documents and websites related to Intel® Curie™ module, and the API reference guide for the pattern matching engine library.

References 

This following list provides links to documents related to the Intel® Curie™ module and other relevant products:

The Troubleshooting and Frequently Asked Questions (FAQ) page in this Open Developer Kit (ODK) lists some known issues and suggested workarounds for them. For frequently asked questions about the Intel® Curie™ module, please visit the Intel® Curie™ module support page.
If you need further information, or to find and share solutions with Intel® Curie™ module users across the world, please visit the Intel® Curie™ Forum of Intel® Support Community.

API Reference for the Intel Pattern Matching Technology Library 

The Intel Pattern Matching Technology Library provides a range of API functions. The API functions and variables are described in this section:

Variables 

  • CuriePME.noMatch (uint32_t): The value returned by classify() to indicate a pattern could not be classified
  • CuriePME.minContext (uint16_t): Minimum context value
  • CuriePME.maxContext (uint16_t): Maximum context value
  • CuriePME.maxVectorSize (int32_t): Maximum pattern size (in bytes) that can be accepted by learn() and classify()
  • CuriePME.firstNeuronID (int32_t): ID of first neuron in network
  • CuriePME.lastNeuronID (int32_t): ID of last neuron in network
  • CuriePME.maxNeurons (int32_t): Number of neurons in the network

Initialization Functions 

CuriePME.begin() 

​void CuriePME.begin(void)

Initialize the PME so it is ready for operation.

Parameters

None

Return value

None

CuriePME.forget() 

void CuriePME.forget(void)

Clear any data committed to the network, making the network ready to learn again.

Parameters

None

Return value

None

Basic Functions 

CuriePME.learn() 

uint16_t CuriePME.learn(uint8_t *pattern_vector, int32_t vector_length, uint8_t category)

Takes a pattern pattern_vector of size vector_length, and commits it to the network as training data. The category parameter indicates to the PME which category this training vector belongs to. That is, if a future input has a sufficiently similar pattern, it will be classified as the same category passed with this pattern.

Parameters
  1. uint8_t *pattern_vector: Pointer to the training data. Training data must be no longer than 128 bytes
  2. int32_t vector_length: The size (in bytes) of your training vector
  3. uint8_t category: The category that should be assigned to this data
Return value

Total number of committed neurons in the network after the learning operation is complete.

CuriePME.classify() 

uint16_t CuriePME.classify(uint8_t *pattern_vector, int32_t vector_length)

Takes a pattern pattern_vector of size vector_length, and uses the committed neurons in the network to classify the pattern.

Parameters
  1. uint8_t *pattern_vector: Pointer to the data to be classified. Pattern data must be no longer than 128 bytes
  2. int32_t vector_length: The size (in bytes) of the data to be classified
Return value

CuriePME.noMatch if the input data did not match any of the trained categories. Otherwise, the trained category assigned by the network will be returned.

Saving Knowledge 

CuriePME.beginSaveMode() 

void CuriePME.beginSaveMode(void)

Puts the network into a state that allows the neuron contents to be read.

Parameters

None

Return value

None

CuriePME.iterateNeuronsToSave() 

uint16_t CuriePME.iterateNeuronsToSave(neuronData& data_array)

When in save mode, this method can be called to obtain the data for the next committed neuron. Each successive call will increment an internal pointer and return the data for successive committed neurons, until all committed neurons have been read.

Parameters

neuronData& data_array: a neuronData type in which the neuron data will be placed

Return value

0 when all committed neurons have been read. Otherwise, this method returns the trained category of the neuron being read.

CuriePME.endSaveMode() 

void CuriePME.endSaveMode(void)

Takes the network out of save mode.

Parameters

None

Return value

None

Restoring Knowledge 

CuriePME.beginRestoreMode() 

void CuriePME.beginRestoreMode(void)

Puts the network into a state that allows the neuron contents to be restored from a file.

Parameters

None

Return value

None

CuriePME.iterateNeuronsToRestore() 

void CuriePME.iterateNeuronsToRestore(neuronData& data_array)

When in restore mode, this method can be called to write data to the next available neuron. Each successive call will increment an internal pointer until all the neurons in the network have been written.

Parameters

neuronData& data_array: a neuronData type containing the neuron data

Return value

None

CuriePME.endRestoreMode() 

void CuriePME.endRestoreMode(void)

Takes the network out of restore mode.

Parameters

None

Return value

None

Configuraton Functions 

CuriePME.setClassifierMode() 

void CuriePME.setClassifierMode(PATTERN_MATCHING_CLASSIFICATION_MODE mode)

Sets the classifying function to be used by the network.

Parameters

PATTERN_MATCHING_CLASSIFICATION_MODE mode The classifying function to use. Valid values are:

  • RBF_Mode (default)
  • KNN_Mode
Return value

None

CuriePME.getClassifierMode() 

PATTERN_MATCHING_CLASSIFICATION_MODE CuriePME.getClassifierMode(void)

Gets the classifying function being used by the network.

Parameters

None

Return value

PATTERN_MATCHING_CLASSIFICATION_MODE mode The classifying function being used. Valid values are:

  • RBF_Mode
  • KNN_Mode

CuriePME.setDistanceMode() 

void CuriePME.setDistanceMode(PATTERN_MATCHING_DISTANCE_MODE mode)

Sets the distance function to be used by the network.

Parameters

PATTERN_MATCHING_DISTANCE_MODE mode The distance function to use. Valid values are:

  • LSUP_Distance (default)
  • L1_Distance
Return value

None

CuriePME.getDistanceMode() 

PATTERN_MATCHING_DISTANCE_MODE CuriePME.getDistanceMode(void)

Gets the distance function being used by the network.

Parameters

None

Return value

PATTERN_MATCHING_DISTANCE_MODE mode The distance function being used. Valid values are:

  • LSUP_Distance
  • L1_Distance

CuriePME.setGlobalContext() 

void CuriePME.setGlobalContext(uint16_t context)

Writes a value to the Global Context Register. Valid context values range between 1-127. A context value of 0 enables all neurons, with no regard to their context.

Parameters

uint16_t context: a value between 1-127 representing the desired context. A context value of 0 selects all neurons, regardless of their context value.

Return value

None

CuriePME.getGlobalContext() 

uint16_t CuriePME.getGlobalContext(void)

Reads the Global Context Register.

Parameters

None

Return value

uint16_t, the contents of the Global Context Register (a value between 0-127).

Other Functions 

CuriePME.getCommittedCount() 

uint16_t CuriePME.getCommittedCount(void)

Gets the number of committed neurons in the network.

Note this method should not be used in save or restore mode, because it will give inaccurate results.

Parameters

None

Return value

uint16_t, the number of comitted neurons in the network.

CuriePME.readNeuron() 

void CuriePME.readNeuron(int32_t neuronID, neuronData& data_array)

Read a specific neuron by its ID.

Parameters
  1. int32_t neuronID: value between 1-128 representing a specific neuron
  2. neuronData& data_array: neuronData type in which to write the neuron data
Return value

None

CuriePME.writeVector() (KNN_Mode only) 

uint16_t CuriePME.writeVector(uint8_t *pattern_vector, int32_t vector_length)

(Should only be used in KNN_Mode) Takes a pattern pattern_vector of size vector_length, and uses the committed neurons in the network to classify the pattern.

Parameters
  1. uint8_t *pattern_vector: Pointer to the data to be classified. Pattern data must be no longer than 128 bytes
  2. int32_t vector_length: The size (in bytes) of the data to be classified
Return value

CuriePME.noMatch if the input data did not match any of the trained categories. Otherwise, the trained category assigned by the network will be returned.

Troubleshooting and Frequently Asked Questions (FAQ) 

This page lists some known issues and suggested workarounds for them.

The References and Documentation sections in this Open Developer Kit (ODK) provide documentation and links to other relevant material for you to refer to. For frequently asked questions about the Intel® Curie™ module, please visit the Intel® Curie™ Module Support page.

Troubleshooting

Permission denied to /dev/ttyACM0

Some of the upload steps fail and the error message says "permission denied to /dev/ttyACM0" with CODK-A, CODK-M or CODK-Z. This can be solved by adding your user name to the group dialout.

  1. Run the command to add your user name (<username>) to the dialout group:
    S sudo usermod -a -G dialout <username>
  2. You must log out and log in again so the update is effective.

No output on the serial monitor although the application example is running (A and M trees)

The application example has compiled and started running but nothing is displayed on a serial terminal like Screen or CuteCom.

To solve this issue you need to update the BLE firmware once:

$ make upload-ble-dfu

Then move to the application example, compile and upload the example.

Para obtener información más completa sobre las optimizaciones del compilador, consulte nuestro Aviso de optimización.