Things used in this project

Hardware components:
Neopixel strip
NeoPixel strip
Could be replaced by individual RGB LEDs.
×1
Ardgen 101
Arduino 101 & Genuino 101
×1
Omron b3f 1000 image 75px
SparkFun Pushbutton switch 12mm
×1
Mfr 25frf52 10k sml
Resistor 10k ohm
×1
11026 02
Jumper wires (generic)
You may need long wires to guide the LEDs from your shoulder to your hand.
×1
Tens70
9V battery (generic)
×1
Adafruit industries ada80 image 75px
9V to Barrel Jack Connector
×1
1260 00
Adafruit Flora RGB Neopixel LEDs- Pack of 4
This can be used to replace the Neopixel strip.
×1
Adafruit industries ada640 image 75px
Sewable Conductive Thread
Option to replace some wires.
×1
Software apps and online services:
Ide web
Arduino IDE
Hand tools and fabrication machines:
Hy gluegun
Hot glue gun (generic)
sewing machine (generic)
09507 01
Soldering iron (generic)

Schematics

NeoPixel LEDs
It's just connecting the LEDs to the Arduino 101 pins and a button to pin 4 (as described in the code: https://www.arduino.cc/en/Tutorial/Button). The circuit diagram is similar to this Fritzing from Adafruit: https://learn.adafruit.com/adafruit-neopixel-uberguide/basic-connections
I plugged the 9 V battery directly into the barrel jack.
Leds wiring diagram 0dtcncui78

Code

PME_LEDArduino
This is using the Curie PME to control LEDs. It's basically a combination of the Draw in the Air PME code and the Adafruit NeoPixel example code. This code is not exactly what I used (which is a bit complex) as I doubt you'll make the exact same kind of constellation arrangement. Rather, here is a generic code which you can modify to personalize your need, e.g. you can change how to distribute the pins for different LED strips. I may update it with an improved code later.
/*
 * This example demonstrates using the pattern matching engine (CuriePME)
 * to classify streams of accelerometer data from CurieIMU.The code is a modification of the Draw in the Air example:
 * https://github.com/01org/Intel-Pattern-Matching-Technology
 *
 * First, the sketch will prompt you to draw patterns in the air (just
 * imagine you are writing on an invisible whiteboard, using your board as the
 * pen), and the IMU data from these motions is used as training data for the
 * PME. Once training is finished, you can keep drawing letters and the PME
 * will try to guess which letter you are drawing.
 *
 * This example requires a button to be connected to digital pin 4
 * https://www.arduino.cc/en/Tutorial/Button
 *
 * NOTE: For best results, draw big letters, at least 1-2 feet tall.
 *
 * Copyright (c) 2016 Intel Corporation.  All rights reserved.
 * See license notice at end of file.
 */

#include "CurieIMU.h"
#include "CuriePME.h"

#include <Adafruit_NeoPixel.h>
#define PIN 6  //// what pin are the NeoPixels connected to?
Adafruit_NeoPixel strip = Adafruit_NeoPixel(54, PIN, NEO_GRB + NEO_KHZ800);  /// the strip is 15 pixels long.  You can change this for the number of pixels in your individual strip.

int tr = 0;  //Some variables to hold "color-target" and "color-current" for smoothing...
int tg = 0;
int tb = 0;
int r = 0;
int g = 0;
int b = 0;

int rawX = 0;  /////  to hold values from the Curie's accelerometer
int rawY = 0;
//int rawZ = 0;
float angle = 0.0;


/*  This controls how many times a letter must be drawn during training.
 *  Any higher than 4, and you may not have enough neurons for all 26 letters
 *  of the alphabet. Lower than 4 means less work for you to train a letter,
 *  but the PME may have a harder time classifying that letter. */
const unsigned int trainingReps = 4;

/* Increase this to 'A-Z' if you like-- it just takes a lot longer to train */
const unsigned char trainingStart = 'A';
const unsigned char trainingEnd = 'D';

/* The input pin used to signal when a letter is being drawn- you'll
 * need to make sure a button is attached to this pin */
const unsigned int buttonPin = 4;

/* Sample rate for accelerometer */
const unsigned int sampleRateHZ = 200;

/* No. of bytes that one neuron can hold */
const unsigned int vectorNumBytes = 128;

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

/* This value is used to convert ASCII characters A-Z
 * into decimal values 1-26, and back again. */
const unsigned int upperStart = 0x40;

const unsigned int sensorBufSize = 2048;
const int IMULow = -32768;
const int IMUHigh = 32767;

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

    pinMode(buttonPin, INPUT);

    /* Start the IMU (Intertial Measurement Unit) */
    CurieIMU.begin();

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

    CurieIMU.setAccelerometerRate(sampleRateHZ);
    CurieIMU.setAccelerometerRange(2);

    trainLetters();
    //Serial.println("Training complete. Now, draw some letters (remember to ");
   // Serial.println("hold the button) and see if the PME can classify them.");
    
  strip.begin();  //  intialize neopixel strip
  strip.show();   // Initialize all pixels to 'off'
}

void loop ()
{
  
/// these functions are written out at the bottom of the sketch.  
Serial.println("Training complete. Now, draw some letters (remember to ");
Serial.println("hold the button) and see if the PME can classify them.");
    
  
   byte vector[vectorNumBytes];
    unsigned int category;
    char letter;
    char pattern;

    /* Record IMU data while button is being held, and
     * convert it to a suitable vector */
    readVectorFromIMU(vector); 

    /* Use the PME to classify the vector, i.e. return a category
     * from 1-26, representing a letter from A-Z */
    category = CuriePME.classify(vector, vectorNumBytes);

   if (category == CuriePME.noMatch) {
       Serial.println("Don't recognise that one-- try again.");
         
        //theaterChase();
 theaterChase(strip.Color(127, 127, 127), 50); // White
          strip.show();
        // delay(10);
          
    } else {
       letter = category + upperStart;
       
              
        pattern = letter;
       if ( pattern == 'A' ) {
         //red
  colorWipe(strip.Color(0, 255, 0), 50); // Green
  theaterChase(strip.Color(127, 127, 127), 50); // White
          strip.show();
  
       } else if ( pattern == 'B') {
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  theaterChase(strip.Color(127, 127, 127), 50); // White
          strip.show();
        
        } else if ( pattern == 'C') {
  colorWipe(strip.Color(0, 0, 255), 50); // Blue
  theaterChase(strip.Color(127, 127, 127), 50); // White
          strip.show();
        
        } else if ( pattern == 'D') {
   colorWipe(strip.Color(255, 0, 255), 50); // Blue
   theaterChase(strip.Color(127, 127, 127), 50); // White
          strip.show();
        
        }
Serial.println(letter); 
   
       
       
   }
          
 
  
}

/* Simple "moving average" filter, removes low noise and other small
 * anomalies, with the effect of smoothing out the data stream. */
byte getAverageSample(byte samples[], unsigned int num, unsigned int pos,
                   unsigned int step)
{
    unsigned int ret;
    unsigned int size = step * 2;

    if (pos < (step * 3) || pos > (num * 3) - (step * 3)) {
        ret = samples[pos];
    } else {
        ret = 0;
        pos -= (step * 3);
        for (unsigned int i = 0; i < size; ++i) {
            ret += samples[pos - (3 * i)];
        }

        ret /= size;
    }

    return (byte)ret;
}

/* We need to compress the stream of raw accelerometer data into 128 bytes, so
 * it will fit into a neuron, while preserving as much of the original pattern
 * as possible. Assuming there will typically be 1-2 seconds worth of
 * accelerometer data at 200Hz, we will need to throw away over 90% of it to
 * meet that goal!
 *
 * This is done in 2 ways:
 *
 * 1. Each sample consists of 3 signed 16-bit values (one each for X, Y and Z).
 *    Map each 16 bit value to a range of 0-255 and pack it into a byte,
 *    cutting sample size in half.
 *
 * 2. Undersample. If we are sampling at 200Hz and the button is held for 1.2
 *    seconds, then we'll have ~240 samples. Since we know now that each
 *    sample, once compressed, will occupy 3 of our neuron's 128 bytes
 *    (see #1), then we know we can only fit 42 of those 240 samples into a
 *    single neuron (128 / 3 = 42.666). So if we take (for example) every 5th
 *    sample until we have 42, then we should cover most of the sample window
 *    and have some semblance of the original pattern. */
void undersample(byte samples[], int numSamples, byte vector[])
{
    unsigned int vi = 0;
    unsigned int si = 0;
    unsigned int step = numSamples / samplesPerVector;
    unsigned int remainder = numSamples - (step * samplesPerVector);

    /* Centre sample window */
    samples += (remainder / 2) * 3;
    for (unsigned int i = 0; i < samplesPerVector; ++i) {
        for (unsigned int j = 0; j < 3; ++j) {
            vector[vi + j] = getAverageSample(samples, numSamples, si + j, step);
        }

        si += (step * 3);
        vi += 3;
    }
}

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

    unsigned int samples = 0;
    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;
            ++samples;

            /* 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, samples, vector);
}

void trainLetter(char letter, unsigned int repeat)
{
    unsigned int i = 0;

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

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

        readVectorFromIMU(vector);
        CuriePME.learn(vector, vectorNumBytes, letter - upperStart);

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

void trainLetters()
{
    for (char i = trainingStart; i <= trainingEnd; ++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.");

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

///////////////Special light functions from Adafruit Strandtest Example Code

// Rainbow!  Note- this function blocks new position inputs until it's finished.
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Input a value 0 to 255 to get a color value.  Used for rainbow effect above.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

    //////Theater Chase lights from Adafruit strandtest example code.  This takes whatever the curent RGB value is, and does a "theatre chase" effect with it.
//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}
//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

Credits

Coin kitty ttcqofkfsi
Kitty Yeung

Physicist/Artist/Musician/Fashion Designer/Engineer working at Intel www.kittyyeung.com

Contact

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

PHPoC - Web-Based Thermometer
Easy
  • 88
  • 7

Full instructions

This project allows to check temperature at home from anywhere using web browser.

Arduino - Have Fun with Color Sensor
Easy
  • 208
  • 9

Full instructions

This project shows how to change color of Minion using Arduino, PHPoC WiFi Shield and color sensor.

Arduino - Web-Based Thermometer
Easy
  • 499
  • 11

Full instructions

This project allows you to check temperature at home from anywhere using web browser.

Electronic Voting Machine Using Arduino
Easy
  • 136
  • 5

It is an EVM using Arduino. There are five switches in all, assigned for various activities.

Using Python and Arduino MKR1000 for Secure IoT
Easy
  • 522
  • 7

Full instructions

Getting started with Python and Arduino MKR1000 for secure IoT projects.

Assemble a PCB using Upverter and Fusion PCBA
Easy
  • 250
  • 5

Full instructions

Using an existing well written guide, our Hackster Live chapter designed, fabricated, and assembled an Arduino shield for a Simon Says game.

ProjectsCommunitiesTopicsContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login