Project tutorial
NeoPixel Animation with Gestures

NeoPixel Animation with Gestures © GPL3+

In this tutorial we're going to play with a gesture sensor and a NeoPixel ring to learn how to combine them both using an Arduino.

  • 2,236 views
  • 0 comments
  • 7 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)

About this project

In this tutorial, we're going to play with a gesture sensor (APDS-9960) and a NeoPixel ring to learn how to combine them both using an Arduino UNO.

The end product will respond to left - right gestures by animating LED movement right or left, and to up-down gestures by changing LED's color.

In the next steps, you'll briefly overview the part list and how to connect the components. And then we'll review the code step by step to learn how it works.

Assembly

Before you'll get started make sure you have all the components on you're table. We'll have some nice steps to follow :). I've also attached the Fritzing schematic as a picture and also in fritzing format.

1. Solder 3 male pins to the NeoPixel ring (GND, PWR, control pin)

2. Attach the NeoPixel ring to the breadboard

3. Attach the APDS9960 sensor to the breadboard

4. Connect the grounds: battery pack, Arduino UNO, APDS9960 and NeoPixel to the breadboard ground

5. Connect the power: Arduino UNO 3V to APDS9960 power pin, NeoPixel to battery pack power

6. Connect the NeoPixel control pin to Arduino D6 pin

7. Connect SDA and SCL of the APDS9960 to the A4 and A5 respectively

8. Connect the APDS9960 interrupt pin to the Arduino D2

Code upload

First of all you'll need to download and install the necessary Arduino libraries:

1. NeoPixel ring library: https://github.com/adafruit/Adafruit_NeoPixel

2. Gesture sensor library: https://github.com/sparkfun/SparkFun_APDS-9960_Sensor_Arduino_Library

If you don't know how to install Arduino libraries check out this tutorial.

After you have downloaded and installed the libraries above, you can clone or download my Arduino repository located here: https://github.com/danionescu0/arduino, and we'll use this sketch: https://github.com/danionescu0/arduino/tree/master/projects/neopixel_ring_gestures

In the next section I'll embed the code directly into this tutorial, so if you like you can copy and paste it from there.

Finally, connect the Arduino the computer using the USB cable, put 1.5 v batteries into the battery pack, and upload the sketch into the Arduino.

How does it work?

In this last part we'll learn how these components are combined together, how to use their libraries and how i've structured my code:

First let's get a quick glance through the sensor and the NeoPixel library API methods that we'll use

1. NeoPixel API from Adafruit

From this library we'll be using the methods that control individual led's color and apply them

- Include the library:

#include <Adafruit_NeoPixel.h>

- Declare the library

#define NEOPIXED_CONTROL_PIN 6
#define NUM_LEDS 24
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEOPIXED_CONTROL_PIN, NEO_RBG + NEO_KHZ800);

- Initialize

#typically inside the setup block
void setup() {    
   strip.begin();     
   # maybe some other stuff here    
   # ....
}

- Light up individual pixels then apply all modifications to the strip (render it in a way)

# set up pixel 0 to be red
strip.setPixelColor(0, strip.Color(255, 0, 0)); 
# set up pixel 1 to be green
strip.setPixelColor(1, strip.Color(0, 255, 0));
# set up pixel 2 to be blue
strip.setPixelColor(2, strip.Color(0, 0 255));
strip.show();

2. APDS 9960 gesture sensor

From this library we'll be using the "read gesture" function. This function will be able to distinguish between left-right, up-down, close-far commands. There is a trick here, we're not going to ask the sensor continuously for the last gesture perceived. The board has the capability to "ping" through a interrupt that a gesture has been found.

  • Include the library, similar to the neopixel
  • Declare the library the interrupt pin, and the interrupt flag
void setup() 
{
   # declare the interrupt pin as INPUT and attach a function to it
   pinMode(APDS9960_INT, INPUT);
   attachInterrupt(0, interruptRoutine, FALLING);
   if ( apds.init() && apds.enableGestureSensor(true)) {
        Serial.println("APDS-9960 initialization complete");
   } else {
       Serial.println("Something went wrong during APDS-9960 init!");
   }
  # initialize other things maybe
}

- Define the interrupt function, here we'll set only a flag

void interruptRoutine() {
   isr_flag = 1;
}

- Inside the loop function check the flag periodically to see if a gesture has been detected

void loop() 
{
  # check the flag
   if( isr_flag == 1 ) {
    # if the flag is set, remove the interrupt, make the necessary processing inside handleGesture() function
     # and then reset the flag and reattach the interrupt 
     detachInterrupt(0);
     handleGesture();
     isr_flag = 0;
     attachInterrupt(0, interruptRoutine, FALLING);
   }
   # some other code here maybe
}

- Define handleGesture() function where we can ask for the last gesture

void handleGesture() {
   # if no gesture is avalible return, this is only a safe check
   if ( !apds.isGestureAvailable() ) {
       return;
   }
   # reads the last gesture, compares with the known ones and print a message
   switch ( apds.readGesture() ) {
     case DIR_UP:
       Serial.println("UP");
       break;
    case DIR_DOWN:
       Serial.println("DOWN");
       break;        
     case DIR_LEFT:
       Serial.println("LEFT");
       break;
     case DIR_RIGHT:
       Serial.println("RIGHT");
       break;
     case DIR_FAR:
       Serial.println("FAR");
       break;
   }
}

Now let's see the whole code in action...

So I've explained the base API of the gesture sensor and the neopixel ring, now let's put things together:

The algorithm runs like this:

  • Initialize the libraries (see the code above)
  • Create an array of led intensities called "ledStates". This array will contain 24 led intensities that are arranged in a descending manner from 150 to 2
  • Inside the main loop check if the interrupt pin has been modified if so it's time to change led's animation or color
  • The "handleGesture()" function checks the last gesture and calls the function "toggleColor" for UP -DOWN gestures or set a global variable "ledDirection" for LEFT - RIGHT gestures
  • The "toggleColor()" function simply changes a global variable named "colorSelection" with one of the values 0, 1, 2
  • Also inside the main loop function another function named "animateLeds();" is called. This function checks if 100 milliseconds passed, and if so it rotates the leds using "rotateLeds()" function and then redraws them
  • The "rotateLeds()" will "rotates" the leds forward or backward by using another array called "intermediateLedStates".

The rotation "effect" will look like this:

# after initialization
{150, 100, 70, 50, 40, 30, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
# after rotateLeds() is called
{0, 150, 100, 70, 50, 40, 30, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
# after rotateLeds() is called again
{0, 0, 150, 100, 70, 50, 40, 30, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 
# and so on 

For this first creates the new array and copies the old led intensities on the new positions (increment the position or decrement it). After that it overwrites the "ledStates" array with the "intermediateLedStates" so the process will continue after another 100 milliseconds.

#include "SparkFun_APDS9960.h"
#include "Adafruit_NeoPixel.h"
#include "Wire.h"
#define NEOPIXED_CONTROL_PIN 6
#define NUM_LEDS 24
#define APDS9960_INT 2
#define LED_SPEED_STEP_INTERVAL 100
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEOPIXED_CONTROL_PIN, NEO_RBG + NEO_KHZ800);
SparkFun_APDS9960 apds = SparkFun_APDS9960();
unsigned long lastLedChangeTime = 0;
short ledDirection = 0;
short colorSelection = 0;
byte ledStates[] = {150, 100, 70, 50, 40, 30, 10, 2, 0, 0, 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};          
int isr_flag = 0;
void setup() 
{
   Serial.begin(9600);
   Serial.println("Program started");
   strip.begin(); 
   pinMode(APDS9960_INT, INPUT);
   attachInterrupt(0, interruptRoutine, FALLING);
   if ( apds.init() && apds.enableGestureSensor(true)) {
        Serial.println("APDS-9960 initialization complete");
   } else {
       Serial.println("Something went wrong during APDS-9960 init!");
   }
   lastLedChangeTime = millis();
   Serial.println("Init succesfully");
}
void loop() 
{
   if( isr_flag == 1 ) {
     detachInterrupt(0);
     handleGesture();
     isr_flag = 0;
     attachInterrupt(0, interruptRoutine, FALLING);
   }
   animateLeds();
}
void interruptRoutine() 
{
   isr_flag = 1;
}
/**
* This will handle gestures from the APDS9960 sensor
* Up and Down gestures will call toggleColor function
* Left and Right gestures will change the led animation
*/
void handleGesture() {
   if ( !apds.isGestureAvailable() ) {
       return;
   }
   switch ( apds.readGesture() ) {
     case DIR_UP:
       Serial.println("UP");
       toggleColor();
       break;
    case DIR_DOWN:
       Serial.println("DOWN");
       toggleColor();
       break;        
     case DIR_LEFT:
       ledDirection = 1;
       Serial.println("LEFT");
       break;
     case DIR_RIGHT:
       ledDirection = -1; 
       Serial.println("RIGHT");
       break;
     case DIR_FAR:
       ledDirection = 0;
       Serial.println("FAR");
       break;
   }
}
/**
* Change current leds color
* Each time this function is called will change the leds state
*/
void toggleColor()
{
   if (colorSelection == 0) {
       colorSelection = 1;
   } else if (colorSelection == 1) {
       colorSelection = 2;
   } else {
       colorSelection = 0;
   }
}
/**
* The animation will run after LED_SPEED_STEP_INTERVAL millis
* First the rotateLeds function is called, then the leds colors are set using the strip api
*/
void animateLeds() 
{
   if (millis() - lastLedChangeTime < LED_SPEED_STEP_INTERVAL) {
       return;
   }
   rotateLeds();
   for (int i=0; i < NUM_LEDS; i++) {
       strip.setPixelColor(i, getColor(ledStates[i])); 
       strip.show();
   }  
   lastLedChangeTime = millis();
}
/**
* Using a secondary array "intermediateLedStates", leds intensities are animated
* First the values from "ledStates" are copied on "intermediateLedStates" like so
* let's sat the "ledStates" array is {100, 80, 60, 0, 0, 0} and the ledDirection is 1
* then after this function is called "ledStates" array is {0, 100, 80, 60, 0, 0} simulating a rotation effect
*/
void rotateLeds()
{
   byte intermediateLedStates[NUM_LEDS];
   for (int i=0; i < NUM_LEDS; i++) {
       intermediateLedStates[i] = 0;
   }         
   for (int i=0; i < NUM_LEDS; i++) {
       if (ledDirection == 1) {
           if (i == NUM_LEDS -1) {
               intermediateLedStates[0] = ledStates[i];
           } else {
               intermediateLedStates[i + 1] = ledStates[i];
           }
       } else {
           if (i == 0) {
               intermediateLedStates[NUM_LEDS - 1] = ledStates[i];
           } else {
               intermediateLedStates[i - 1] = ledStates[i];
           }          
       }       
   }
   for (int i=0; i < NUM_LEDS; i++) {
       ledStates[i] = intermediateLedStates[i];
   }
}
uint32_t getColor(int intensity)
{
   switch (colorSelection) {
       case 0:
         return strip.Color(intensity, 0, 0);
       case 1:
         return strip.Color(0, intensity, 0);
       default:
         return strip.Color(0, 0, intensity);
   }   
}

I hope you enjoyed this, you can use the comments section to ask me questions.

Code

Arduino repository
navigate to projects/neopixel_ring_gestures

Schematics

Schematic
sketch_S0IS9MWfCR.fzz
Arduino library
Navigate to projects/neopixel_ring_gestures for this specific project

Comments

Similar projects you might like

Gyroscope Fun with NeoPixel Ring

Project tutorial by danionescu

  • 18,051 views
  • 3 comments
  • 48 respects

Arduino Clock with Neopixel Ring Animation

Project tutorial by Alexander

  • 42,067 views
  • 39 comments
  • 64 respects

Pixie: An Arduino Based NeoPixel Wristwatch

Project tutorial by Konstantin Dimitrov

  • 6,739 views
  • 5 comments
  • 45 respects

NeoPixel Christmas (Color) Animation

Project tutorial by ericBcreator

  • 840 views
  • 2 comments
  • 7 respects

VU Meter on Steroids: Arduino Nano and NeoPixel WS2812B

Project in progress by Team WannaDuino

  • 21,817 views
  • 81 comments
  • 80 respects

Indoor NeoPixel Thermometer 

Project tutorial by Jonathan Eskow

  • 4,642 views
  • 3 comments
  • 12 respects
Add projectSign up / Login