APDS-9960 and Photon

Hello everybody!

I'm doing some tests on APDS-9960 sensor (SparkFun module)
I'm using it on a Photon Particle e with the SparkFun_APDS9960.h library.

Examples found use one feature of this sensor at a time. I'd like to use all of them simultaneously, just to test the timing, and eventually enable them in different moment to fit better my purpose, if timing doesn't.
According to datasheet, all the features can be enabled, they will be controlled by a state machine.

According to library, the setMode() is public and can be called by apds object.

I can't understand why i get this error when I attempt to set ADPS_ENABLE to ALL (0x7F)

../wiring/inc/spark_wiring_arduino_constants.h:28:21: return-state
ment with a value, in function returning 'void' [-fpermissive]

My relative code:

bool mode_ALL = apds.setMode(ALL, ON);

if( !mode_ALL ) {
    return false;
}
Serial.println(F("setMode ALL  ENABLE REG:"));
mode = apds.getMode();
Serial.println(mode, BIN);

Then, why enableGestureSensor set ENABLE this way:

Gesture sensor is now running
apds.enableGestureSensor ENABLE REG:
1001111

Shouldn't be 1000001 ?
Shouldn't be 0x80<4-1> reserved to ALS sensor? And, as far as ALS concern, does this sensor need the bit 1 set to HIGH for Power On?
Thank you!

Not seeing more of you code I'd guess the eror message may be complaining about your return false; inside a function that's defined as void ()

Thank you, ScruffR for you answer!
Thatā€™s my whole code:

#include <Wire.h>

#include <SparkFun_APDS9960.h>

SparkFun_APDS9960 apds = SparkFun_APDS9960();
uint8_t proximity_data = 0;

void setup() {
    
     // Initialize Serial port
  Serial.begin(9600);
    
    // 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!"));
  }
  
  Serial.println(F("apds.init() ENABLE REG:"));
  uint8_t mode = apds.getMode();
  Serial.println(mode, BIN);
  
   // 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!"));
  }
  
  Serial.println(F("apds.enableProximitySensor(false) ENABLE REG:"));
  mode = apds.getMode();
  Serial.println(mode, BIN);
  
  // Start running the APDS-9960 light sensor (no interrupts)
  if ( apds.enableLightSensor(false) ) {
    Serial.println(F("Light sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during light sensor init!"));
  }
  
  // Wait for initialization and calibration to finish
  delay(500);
  
  Serial.println(F("apds.enableLightSensor(false)  ENABLE REG:"));
  mode = apds.getMode();
  Serial.println(mode, BIN);
  
   // 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!"));
  }
  Serial.println(F("apds.enableLightSensor(false)  ENABLE REG:"));
  mode = apds.getMode();
  Serial.println(mode, BIN);
 
 //Trying to set all features 
   if( !apds.setMode(ALL, ON) ) {
        return false;
    }
  Serial.println(F("setMode ALL  ENABLE REG:"));
  mode = apds.getMode();
  Serial.println(mode, BIN);
  
}

void loop() {

}

Iā€™m just trying to control if I set set properly the REG.

But I canā€™t do manually (can I?) because the method is privete, so I need to call SetMode().
But when I attached //Trying to set all features code, i get that ERROR message on line 28. The same line that compile properly without the last portion of codeā€¦

I canā€™t understand whyā€¦

As I suspected, you are calling return false; in void setup() - this is not allowed. You can only call return; but not pass a return value when the function is explicitly declared not to return anything (void).

BTW, it's not code line 28 of your code, but of the file stated in the error message

This is where the fact that setup() is to be defined/implemented as void is first stated.

1 Like

I seeā€¦!
Thank you! Really a newby error :smiley:
I was seeing that ā€œreturnā€ as it was in setMode function (ok, I guess Iā€™m tired :D)

Ok, thatā€™s clear!
But, is there a way to write those register manually? (just curious)

Iā€™m sure you can do that manually since you can access the Wire object from user code without the use of the library, but why would you do that?
Libraries are meant to wrap complicated call sequences into more convenient - often single call - functions.

If you found an important feature not supported by a library itā€™s usually best to file a issue on the GitHub repo and let the contributor add the missing feature.

Just to know more about Photon.
Iā€™ve seen Wire.h has full methods but wouldnā€™t be faster to access directly such as it is in Asembly?

But Iā€™m pretty sure that lib is more convenient any way :slight_smile:

Actually the Wire implementation is also written in C++, but modern C++ compilers produce very well optimised machine code, so the execution speed should be the least of your worries for this kind of setup.

You are extensively Serial.print()ing and have a delay(500) in there, so collecting nano seconds on the way doesnā€™t appear to be of much use IMO.

1 Like

Yes, this short code is just for tests.
Iā€™m trying to understand if itā€™s possible to use this sensor in an efficient way without the state machine sequence.

I mean: iā€™d like to use: proximity->color->gestureā€¦ because the gesture would be slow and detected on FALLINGā€¦ so, if I want to print the color of the object that just passed along the sensor, would the normal sequence (prox->gesture->color) be good for this purpose? I donā€™t know yet, itā€™s what i try to find out know :slight_smile:

#include <Wire.h>
#include <SparkFun_APDS9960.h>

// Pins
#define APDS9960_INT    D3  // Needs to be an interrupt pin
#define LED_PIN         D7 // LED for showing interrupt

// Constants
#define PROX_INT_HIGH   50 // Proximity level for interrupt
#define PROX_INT_LOW    0  // No far interrupt

// Global variables
SparkFun_APDS9960 apds = SparkFun_APDS9960();
uint8_t proximity_data = 0;
int isr_flag = 0;

// Global Variables Color Sensor
uint16_t ambient_light = 0;
uint16_t red_light = 0;
uint16_t green_light = 0;
uint16_t blue_light = 0;

void setup() {
  
  // Set LED as output
  pinMode(LED_PIN, OUTPUT);
  pinMode(APDS9960_INT, INPUT);
  
  // Initialize Serial port
  Serial.begin(9600);
  Serial.println();
  Serial.println(F("---------------------------------------"));
  Serial.println(F("SparkFun APDS-9960 - ProximityInterrupt"));
  Serial.println(F("---------------------------------------"));
  
  // Initialize interrupt service routine
  attachInterrupt(APDS9960_INT, interruptRoutine, FALLING);
  
  // 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"));
  }
  
  // Set proximity interrupt thresholds
  if ( !apds.setProximityIntLowThreshold(PROX_INT_LOW) ) {
    Serial.println(F("Error writing low threshold"));
  }
  if ( !apds.setProximityIntHighThreshold(PROX_INT_HIGH) ) {
    Serial.println(F("Error writing high threshold"));
  }
  
  // Start running the APDS-9960 proximity sensor (interrupts)
  if ( apds.enableProximitySensor(true) ) {
    Serial.println(F("Proximity sensor is now running"));
  } else {
    Serial.println(F("Something went wrong during sensor init!"));
  }
}

void loop() {
  
  // If interrupt occurs, print out the proximity level
  if ( isr_flag == 1 ) {
  
    // Read proximity level and print it out
    if ( !apds.readProximity(proximity_data) ) {
      Serial.println("Error reading proximity value");
    } else {
      Serial.print("Proximity detected! Level: ");
      Serial.println(proximity_data);
      colorGetureDetect();
      } //fine else
    }  //fine primo if
    
    // Turn on LED for a half a second
    digitalWrite(LED_PIN, HIGH);
    delay(500);
    digitalWrite(LED_PIN, LOW);
    
    // Reset flag and clear APDS-9960 interrupt (IMPORTANT!)
    isr_flag = 0;
    if ( !apds.clearProximityInt() ) {
      Serial.println("Error clearing interrupt");
    }
}

void interruptRoutine() {
  isr_flag = 1;
}

void colorGetureDetect(){
    
    apds.disableProximitySensor();
    if ( !apds.enableLightSensor(false) ) {
      Serial.println("Error light sensor not enabled");
    } else {
          // Read the light levels (ambient, red, green, blue)
          if (  !apds.readAmbientLight(ambient_light) ||
                !apds.readRedLight(red_light) ||
                !apds.readGreenLight(green_light) ||
                !apds.readBlueLight(blue_light) ) {
            Serial.println("Error reading light values");
          } else {
            Serial.print("Ambient: ");
            Serial.print(ambient_light);
            Serial.print(" Red: ");
            Serial.print(red_light);
            Serial.print(" Green: ");
            Serial.print(green_light);
            Serial.print(" Blue: ");
            Serial.println(blue_light);
          }
          
          // Wait 1 second before next reading
          delay(1000);
          apds.disableLightSensor();
          gestureDetect();
          
        }
    apds.enableProximitySensor(true);    
}

void gestureDetect(){
    
    
    if ( !apds.enableGestureSensor(true) ) {
      Serial.println("Error gesture sensor not enabled");
    } else {
        if( isr_flag == 1 ) {
            detachInterrupt(APDS9960_INT);
            handleGesture();
            isr_flag = 0;
            attachInterrupt( APDS9960_INT, interruptRoutine, FALLING);
        }
     }
    apds.disableGestureSensor();
}

void handleGesture() {
    
   if ( apds.isGestureAvailable() ) {
    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_NEAR:
        Serial.println("NEAR");
        break;
      case DIR_FAR:
        Serial.println("FAR");
        break;
      default:
        Serial.println("NONE");
    }
  }
  else{Serial.println("Gesture not availeble!");}
}

Could someone explain me why Gesture is always anavailable in this sketch?
I cannot understand much the datasheet gesture engine tooā€¦ :confused:

Also, gesture routine in this sketch seems to be really slow and block somehow the loopā€¦

@Aloap If it helps attached code that does work with the sparkfun APDS-9960. I used the Particle library - Adafruit_APDS9960_Particle. It is pretty straight forward to understand. Using the define flags you can conditionally compile which sensor function required. BTW You have not set the interrupt pin with a PULLUP - that might be a reason it is not working.

/***************************************************************************
  This is a test for the APDS9960 digital proximity, ambient light, RGB, and gesture sensor

  This sketch puts the sensor in proximity mode and enables the interrupt
  to fire when proximity goes over a set value
  These sensors use I2C to communicate. The device's I2C address is 0x39
 ***************************************************************************/
//  APDS9960  Photon
//  VCC       3V3
//  GND       GND
//  SDA       SDA D0
//  SCL       SCL D1
//  INT       D2
#include "Adafruit_APDS9960_Particle.h"
// set only one flag true and rest false for that sensor mode
#define PROXIMITY false
#define GESTURE true
#define COLOUR false

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);
//the pin that the interrupt is attached to
int intPin = D2;

//create the APDS9960 object
Adafruit_APDS9960 apds;

void setup()
{
    Serial.begin(9600);
    while (!Serial.available()) delay(100);
    pinMode(intPin, INPUT_PULLUP);
    if (!apds.begin())
    {
        Serial.println("Failed to initialize device! Please check your wiring.");
    }
    else
    {
        Serial.println("Device initialized!");
    }
    #if GESTURE
    apds.enableProximity(true);     //enable proximity mode
    Serial.println("Proximity Detection Set: True");
    apds.enableGesture(true);     //gesture mode will be entered once proximity mode senses something close
    Serial.println("Gesture Detection Set: True");
    #elif PROXIMITY
    apds.setProximityInterruptThreshold(0, 120);    //set the interrupt threshold to fire when proximity reading goes above 120
    Serial.println("Proximity Threshold Set: True");
    apds.enableProximityInterrupt();    //enable the proximity interrupt
    #elif COLOUR
    apds.enableColor(true);     //enable color sensing mode
    Serial.println("Colour Sensing Mode: True");
    #endif
}

void loop()
{
    #if PROXIMITY
    if (!digitalRead(intPin))    //print the proximity reading when the interrupt pin goes low
    {
        Serial.println(apds.readProximity());
        apds.clearInterrupt();    //clear the interrupt
    }
    #elif GESTURE
    uint8_t gesture = apds.readGesture();    //read a gesture from the device
    if (gesture != 0)
    {
        Serial.print("Gesture Detected ");
        if (gesture == APDS9960_DOWN) Serial.println("v");
        if (gesture == APDS9960_UP) Serial.println("^");
        if (gesture == APDS9960_LEFT) Serial.println("<");
        if (gesture == APDS9960_RIGHT) Serial.println(">");
    }
    delay(200);
    #elif COLOUR
    //create some variables to store the color data in
    uint16_t r, g, b, c;
    //wait for color data to be ready
    while(!apds.colorDataReady())
    {
        delay(5);
    }
    //get the data and print the different channels
    apds.getColorData(&r, &g, &b, &c);
    Serial.print("red: ");
    Serial.print(r);

    Serial.print(" green: ");
    Serial.print(g);

    Serial.print(" blue: ");
    Serial.print(b);

    Serial.print(" clear: ");
    Serial.println(c);
    Serial.println();
    delay(500);
    #endif
}
1 Like

Thank you Armor! Iā€™ll try your tip. I didnā€™t think I could ever need of a pull-up in this case, but Iā€™ll try if it works better :slight_smile:

armor , could I ask you some explanations about your code?

You define: SYSTEM_THREAD(ENABLED);
in which part of the code a different thread is used? Is the loop continuing even during delay()?
Is Thread effective in AUTOMATIC MODE?

I cannot test your code because of it blocks somehow the Photon but Iā€™m trying to integrate it in my previous code and with the library Iā€™ve been using. I tryied the pullup advice, and it seems to work for the first readings of data, but after three/four readings, the loop stops to provide data (but even in the previous loops my gesture sensor never worked properly).
I guess I havenā€™t understand much the behaviour of this sensorā€¦ any help is welcome! :slight_smile:
Thank you.

Here you can find some info about SYSTEM_THREAD(ENABLED)
https://docs.particle.io/reference/firmware/photon/#system-thread

And itā€™s working the same way across all SYSTEM_MODE()s

I cannot make armor code workingā€¦ :frowning:
ScruffR, Iā€™ve been reading that part but Iā€™m not sure I can understand well the functionalities.
Setup() apart, what other functions of the code are involved in threading?
Iā€™ll try to get a better look to the documentationā€¦

@Aloap If it is working somewhat then you are making progress!

I bought the sparkfun board first ($12) and then spotted a copy on Aliexpress for $2. Both work for me but you need to be careful with pullup resistors on the SDA and SCL lines to 3V3 (not required with sparkfun board) and also the Vcc - sparkfun is 3V3 whereas the cheap copy is 5V as it has voltage regulator on board. Lastly, there are soldered jumper selection on the sparkfun board - make sure the board is connected correctly. Try referring to the sparkfun datasheet.

The SYSTEM_THREAD(ENABLED) and SYSTEM_MODE(MANUAL) are only there because I always use system threading and for this test a WiFi/Cloud connection is not needed with output to serial port.

Personally, I found that the sensing distance to be much less than the 20-25cm indicated and more like 10cm. Also, the sensor is affected by the lighting conditions when using gesture control and was not as reliable as I was looking for. Good luck.

@armor, yes, the sensor works properly if I use it just one feature at a time. I cannot use it the way Iā€™d likeā€¦ because I would like to do a proximity stop, a color measuring and a gesture detection, all in the same firmware and in the same time (except for delay the sensor need).
In this case, gesture never works and loop stops after some measurings I donā€™t know whyā€¦
No problem for voltage, Iā€™m using the sensor with Photon board.
I agree with you both about proximity efficienty and light sensing: the first is not accurate over 10 cm and even when the target is really near. After some point it always read 255.
The lighting read greys if it is not in optimal light condition. So, itā€™s pretty unusefull in most cases.
Gesture seems to work good. But I need to detect the color and the direction of something that passes through the sensor and it seems to be impossible with only one sensor like that. I donā€™t know if itā€™s my fault because I cannot program it in the right wayā€¦

I would suggest reading the specification sheet for the APDS-9960. I think you will find it can do proximity trigger and gesture but not distance measurement and gesture and colour at the same time or if it can, the driver software canā€™t handle that. I thought this sensor might provide ā€˜cheapā€™ gesture control but it appears to be too limited. I have used a Flick board with Raspberry Pi - these are really good for hand gesture - they use electric field detection but are too expensive for my application.

1 Like

I see, thank you for your advice! Iā€™Ć¬ guess I have to sacrifice gesture, and read just distance and color. Or maybe use two of this sensors, once for proximity and the other for gesture, using the pin interrupt of the proximity one to power on the second :thinking: