The Sparkfun APDS-9960 RGB and Gesture Sensor is a fairly low-cost ($15) option for hands-free control of all kinds of things. I’ve had this sitting around for a while waiting to be integrated into a project, but I wanted to explore it on its own before making that leap.
For the size, this is a sophisticated sensor, with capability to do proximity, gesture (left/right, up/down, near/far), color sensing and some other options. My initial interest was gesture sensing for a Teensy project, but I chose to explore this sensor on the Sparkfun ESP8266 Thing and Pro Mini 3.3V boards first, just to become familiar with the code.
The trickiest part of my initial tests were the interrupt settings. I ran into issues on the Sparkfun ESP8266 Thing where I’d get a successful sensor initialization, but no gestures. I had better luck with the Pro Mini, though still had some issues with gesture sensing and it was much easier to get Proximity working (shown below).
Here’s the code for the LED proximity threshold test based on Shawn Hymel’s original sensor test sketch:
#include <Wire.h> #include <SparkFun_APDS9960.h> // Global Variables SparkFun_APDS9960 apds = SparkFun_APDS9960(); uint8_t proximity_data = 0; int LED_PIN = 3; // JJD LED for if then void setup() { pinMode(LED_PIN,OUTPUT); //configure LED_PIN as OUTPUT digitalWrite(LED_PIN, LOW); // set LED off // Initialize Serial port Serial.begin(9600); Serial.println(); Serial.println(F("------------------------------------")); Serial.println(F("SparkFun APDS-9960 - ProximitySensor")); Serial.println(F("------------------------------------")); // Initialize APDS-9960 (configure I2C and initial values) if ( apds.init() ) { Serial.println(F("APDS-9960 initialization complete")); } else { Serial.println(F("Something went wrong during APDS-9960 init!")); } // Adjust the Proximity sensor gain if ( !apds.setProximityGain(PGAIN_2X) ) { Serial.println(F("Something went wrong trying to set PGAIN")); } // Start running the APDS-9960 proximity sensor (no interrupts) if ( apds.enableProximitySensor(false) ) { Serial.println(F("Proximity sensor is now running")); } else { Serial.println(F("Something went wrong during sensor init!")); } } void loop() { // Read the proximity value if ( !apds.readProximity(proximity_data) ) { Serial.println("Error reading proximity value"); //digitalWrite(LED_PIN, LOW); JJD LED TEST } else { //Serial.print("Proximity: "); //digitalWrite(LED_PIN, HIGH); JJD LED TEST //Serial.println(proximity_data); } // JJD Section 2/10/18 to set LED on/off at threshold if ( proximity_data < 100 ) { digitalWrite(LED_PIN, LOW); Serial.print("LED OFF, Proximity under 100: "); Serial.println(proximity_data); } else { digitalWrite(LED_PIN, HIGH); Serial.print("LED ON!! Proximity over 100: "); Serial.println(proximity_data); } }
Gestures took some more tries, with a complete refresh of my test code, but eventually it worked (below).
While testing gestures, I tried to include some code to turn an LED on/off. This seemed to slow things down and didn’t work as well as I’d hoped. Response was a bit sluggish, probably because of the delay I used in gesture routine. Even after I took out the LED code, I thought the gestures responded a bit sluggishly.
I’m still not 100% sure why I found so many conflicting tutorials on the interrupt settings.
attachInterrupt(0, interruptRoutine, FALLING);
attachInterrupt(digitalPinToInterrupt(interruptPin), interruptRoutine, FALLING);
Setting that aside, I worked the proximity sensor into a Teensy project to control audio volume and that worked great. I’d like to leave that intact, so I’m going to order another sensor to dedicate to troubleshooting this further. Next step is to a) refine gesture response and b) see if both gesture and proximity can be used from same sensor. Combining them from the same sensor seems feasible, but involves more than just calling the Proximity and Gesture functions in the same sketch. Sparkfun provided some guidance:
“…the gesture function runs off of the proximity function (pages 9-17; the diagrams on 9 & 15 might be more helpful to visualize). You would probably have to internally call the register addresses for that proximity data (page 26).”
So there’s much more to explore!
/**************************************************************** 2/9/18 JJD Reworking with SparkFun ESP8266 Thing WRL-13231 (not Dev Thing) 2/11/18 JJD Pro Mini tests WORKING APDS-9960 RGB and Gesture Sensor Based on Shawn Hymel @ SparkFun Electronics May 30, 2014 https://github.com/sparkfun/APDS-9960_RGB_and_Gesture_Sensor Tests the gesture sensing abilities of the APDS-9960. Configures APDS-9960 over I2C and waits for gesture events. Calculates the direction of the swipe (up, down, left, right) and displays it on a serial console. To perform a NEAR gesture, hold your hand far above the sensor and move it close to the senasor (within 2 inches). Hold your hand there for at least 1 second and move it away. To perform a FAR gesture, hold your hand within 2 inches of the sensor for at least 1 second and then move it above (out of range) of the sensor. Hardware Connections: IMPORTANT: The APDS-9960 can only accept 3.3V! Arduino Pin APDS-9960 Board Function 3.3V VCC Power GND GND Ground A4 SDA I2C Data A5 SCL I2C Clock 2 INT Interrupt Resources: Include Wire.h and SparkFun_APDS-9960.h Developed in Arduino 1.0.5, revised by JJD 1.8.5 Tested with SparkFun Arduino Pro Mini 3.3V ****************************************************************/ #include <Wire.h> #include <SparkFun_APDS9960.h> //#include <ESP8266WiFi.h> // Pins #define APDS9960_INT 2 // Needs to be an interrupt pin #define APDS9960_SDA A4 // I2C Data Must use A for Analog #define APDS9960_SCL A5 // I2C Clock Must use A for Analog // Constants // Global Variables SparkFun_APDS9960 apds = SparkFun_APDS9960(); int isr_flag = 0; const byte interruptPin = 2; int LED_PIN = 3; void setup() { Wire.begin(); //Start I2C with pins defined above pinMode(APDS9960_INT, INPUT); pinMode(LED_PIN,OUTPUT); digitalWrite(LED_PIN, LOW); // Initialize Serial port Serial.begin(9600); Serial.println(); Serial.println(F("--------------------------------")); Serial.println(F("SparkFun APDS-9960 - GestureTest")); Serial.println(F("--------------------------------")); // Initialize interrupt service routine attachInterrupt(0, interruptRoutine, FALLING); // attachInterrupt(digitalPinToInterrupt(interruptPin), interruptRoutine, FALLING); // JJD interrupt // Initialize APDS-9960 (configure I2C and initial values) if ( apds.init() ) { Serial.println(F("APDS-9960 initialization complete")); } else { Serial.println(F("Something went wrong during APDS-9960 init!")); } // Start running the APDS-9960 gesture sensor engine if ( apds.enableGestureSensor(true) ) { Serial.println(F("Gesture sensor is now running")); } else { Serial.println(F("Something went wrong during gesture sensor init!")); } } void loop() { //digitalWrite(LED_PIN, LOW); if( isr_flag == 1 ) { detachInterrupt(0); handleGesture(); isr_flag = 0; attachInterrupt(0, interruptRoutine, FALLING); } } void interruptRoutine() { isr_flag = 1; } void handleGesture() { if ( apds.isGestureAvailable() ) { switch ( apds.readGesture() ) { case DIR_UP: //digitalWrite(LED_PIN, HIGH); Serial.println("UP"); break; case DIR_DOWN: //digitalWrite(LED_PIN, HIGH); Serial.println("DOWN"); break; case DIR_LEFT: //digitalWrite(LED_PIN, HIGH); Serial.println("LEFT"); break; case DIR_RIGHT: //digitalWrite(LED_PIN, HIGH); Serial.println("RIGHT"); break; case DIR_NEAR: //digitalWrite(LED_PIN, HIGH); Serial.println("NEAR"); break; case DIR_FAR: //digitalWrite(LED_PIN, HIGH); Serial.println("FAR"); break; default: Serial.println("NONE"); digitalWrite(LED_PIN, HIGH); delay(100); digitalWrite(LED_PIN, LOW); } } }