[Photon] SLEEP_MODE_DEEP waking problems [Solved]

Solution: long story short, I was using different variable types (long, int), mixing them up and comparing them in ways that aren’t beneficial. @ScruffR posted his code and this works perfectly. So, for anyone interested in automated sleep time and not really in the mood to read the whole thread, check out that post!

Problem: Particle Photon is not waking up from a SLEEP_MODE_DEEP. Sleep time is 14 hours or 50400 seconds.

I’m working with the Particle Photon for a device that only needs to work during the day (it’s being deployed in a work environement). In the evenings, at night and during the weekends the device is not required to be on. However, these devices will work on a battery and are therefor completely independent of the electrical grid.

To make the battery last as long as possible, I’m putting it into a SLEEP_MODE_DEEP. Before going to sleep, the device calculates how long it needs to go to sleep (so it calculates the time between now and wakeup). This value is in seconds and it is always a correct value (i.e. 50400 seconds = 14 hours when the device needs to sleep at 18:00 and wakeup at 08:00).

The device goes to sleep mode at 18:00. It publishes the last values in its system. All’s good. But the next morning it simply has not turned on. Pushing the reset button and everything is fine again. But since this will be remotely deployed, I cannot let people push it every morning. It needs to do it every morning on its own.

When my Photon runs the first time, it always connects to the server and send a message it is online (which I also document), this way I know exactly when it wakes up. It is my understanding the Photon acts as if it’s been reset after a deep sleep, meaning: if my Photon wakes from deep sleep it should connect to the server and send a message it is online. But it does neither. It doesn’t wake up, it doesn’t send the first message. I’ve taken control of the LED (to lower power consumption) but I let it blink once per second at a really low value. Nothing after the supposed wake up time.

Below are partials of my code. For debugging I use Particle.publish to push values to the Particle server, which I document with IFTTT into a Google Drive document. Currently, I’ve changed my SYSTEM_MODE to SEMI_AUTOMATIC and I’m going to give control over the LED back to the Photon, for now, so I can see if that gives me some information.

Long story short: how can I make my deep sleep work, how can I make the Photon wake up? Are there problems with deep sleeps that last this long? What could be the problem? Thank you all for taking the time to respond, anything would help.


First run function of the Photon

  //This is executed when Photon turns on
  while(firstRun == true){
    photonWakeUp();
  }

Note: firstRun is declared in the code as true. When photonWakeUp() is finished, it changes firstRun to false. My understanding is that when the Photon wakes from a deep sleep it should start the code from the beginning and thus firstRun should be true again. To make sure I’ve even made it true in the photonSleep() function.


Wake up function of the Photon

//Initiated after the Photon wakes up.
//Initializes all the functions of the Photon for it to work.
void photonWakeUp(){
  printTimer = millis();        //giving a value to the printTimer
  digitalWrite(statePin, HIGH); //Turning the distance sensor on.
  WiFi.on();                    //Turning WiFi on...
  WiFi.connect();               //Making it connect...
  delay(cooldown);

  //Checking if it's connected
  if(Particle.connected()){
    delay(cooldown);
    detectTime();               //detect time
    delay(cooldown);
    eepromGet();                //get data from the EEPROM
    delay(cooldown);
    RGBcontrol();               //get control of the RGB, as it uses quite some power.
    debug();
    bleepTimer = millis();      //starting the timer for the LED bleeps.
    firstRun = false;           //and to exit the firstRun.
  }
  else{
    photonWakeUp();
  }
}

Note: I’ve implemented the cooldown to make sure the WiFi module processes all the information.


Photon Deep Sleep Function

//Used during the night, ensures the Photon goes to sleep to preserve power.
//Automatically detects if its weekend or not.
void photonSleep(){
  sleepCalculator();                          //Calculate to total sleep time.

  if(WiFi.ready()){
    if(Particle.connected()){
      debug();
    }
  }
  else{
    WiFi.on();
    WiFi.connect();
    photonSleep();
  }

  firstRun == true;                           //Resetting firstRun for the wakeup, not sure if this gets reset on its own.

  System.sleep(SLEEP_MODE_DEEP, sleepTime);   //Going in to deep sleep.
}

Note: debug simply publishes the sleep value that is calculated in in sleepCalculator() which is then stored in a Google Drive document. Furthermore, sleepCalculator() uses integers which are then stored in an unsigned long called sleepTime. Can I simply place integers in an unsigned long or do I need to convert it?

1 Like

I think this recursion in the else branch is potentially "dangerous". If you happen to have problems to connect, you'll eventually fill up your stack with recursive calls of photonWakeUp() running into a hard fault.

BTW, WiFi.connect() will not actually perform a Particle.connect() so if that doesn't happen somewhere else (which would render WiFi.on() and WiFi.connect() superfluous), you'll definetly run into a stackoverflow IMHO.

And since I can't seem to see any indication of Particle.connect() in your code snippets, I'm not surprised that you don't see Particle.publish() working.

The least I can say this way is, that we don't see enough of your code :wink:

The weird thing is, it does publish before it goes to sleep. I’ve only now put my device into SEMI_AUTOMATIC, before I didn’t declare any of that so it was in AUTOMATIC to begin with.

Even now, as we speak, the device publishes. So I don’t have a problem with Particle.publish() or Particle.connect(). I have a problem with the SLEEP_MODE_DEEP not working properly: it just does not wake up, or well, has not woken up at the right time so far.

Anyway, I appreciate you took the time to read that wall of text. I do agree with the else{} part. I’ve been working in a stable WiFi environment, but if that’s not fully the case it should still be able to collect data. Thanks.

I can't say anything about that without seeing more of your code. I can just talk about what I see.

Could you at least consider the fact that WiFi.connect() and Particle.connected() (as used in your above code fragments) are not a matching pair?

If you replace WiFi.connect() with Particle.connect() (which implicitly does the former one anyhow) you could add a waitUntil(Particle.connected); and get rid of the recursive calls.
And the snippets would make more sense for others who might see this too.

I did not know about waitUntil(Particle.connected);, I also did not know that Particle.connect() already does WiFi.connect(), does it also do WiFi.on()? Going to check it out, because I’d like to improve the code if that would be better.

Regardless of that though, I have no problems with publishing. All my data is transferring from the Photon to the Particle cloud. My problem is that the device is not waking up. It was in AUTOMATIC mode, so it should connect to the internet first, but that didn’t happen. The device was just in sleep mode when I arrived to check it this morning. It was well over time as well.

Below is the full code, I’ve blanked some data like WiFi credentials for obvious reasons. Again, thanks for taking the time to look at this.

//   #####   #######  #######  #######  ###  #     #   #####    #####
//  #     #  #           #        #      #   ##    #  #     #  #     #
//  #        #           #        #      #   # #   #  #        #
//   #####   #####       #        #      #   #  #  #  #  ####   #####
//        #  #           #        #      #   #   # #  #     #        #
//  #     #  #           #        #      #   #    ##  #     #  #     #
//   #####   #######     #        #     ###  #     #   #####    #####

//BEFORE DEPLOYMENT CHANGE THE FOLLOWING VALUES TO THAT OF THE LOCATION!!!
//Location's WiFi information
const char* SSID_Location = "XXXXXXXX";  //WiFi network name
const char* PASS_Location = "XXXXXXXX";   //Password of the network.

//The location in our database, don't forget: create location in database.
const int location = 4; //this is a predefined location found in the database.

//At what time the device should go to sleep.
const float sleepHour = 18;
const float sleepMinute = 00;

//At what time the device should wake up.
const float wakeupHour = 7;
const float wakeupMinute = 30;

//MAKE SURE ALL THESE VARIABLES ARE SET PROPERLY BEFORE DEPLOYING THE DEVICE AT THE LOCATION.







//  #     #  ###  #######  ###
//  #  #  #   #   #         #
//  #  #  #   #   #         #
//  #  #  #   #   #####     #
//  #  #  #   #   #         #
//  #  #  #   #   #         #
//   ## ##   ###  #        ###

//WIFI CREDENTIALS for development
/*DO NOT CHANGE*/ const char* SSID = "XXXXXXXX";
/*DO NOT CHANGE*/ const char* PASS = "XXXXXXXX";
//Only change these values if they change at the location







//  #        ###  ######   ######      #     ######   ###  #######   #####
//  #         #   #     #  #     #    # #    #     #   #   #        #     #
//  #         #   #     #  #     #   #   #   #     #   #   #        #
//  #         #   ######   ######   #     #  ######    #   #####     #####
//  #         #   #     #  #   #    #######  #   #     #   #              #
//  #         #   #     #  #    #   #     #  #    #    #   #        #     #
//  #######  ###  ######   #     #  #     #  #     #  ###  #######   #####

#include <RunningMedian.h>  //Averages the sensorValues to prevent false positives.
//Library by Rob Tillaart, https://github.com/RobTillaart







//  #     #     #     ######   ###     #     ######   #        #######   #####
//  #     #    # #    #     #   #     # #    #     #  #        #        #     #
//  #     #   #   #   #     #   #    #   #   #     #  #        #        #
//  #     #  #     #  ######    #   #     #  ######   #        #####     #####
//   #   #   #######  #   #     #   #######  #     #  #        #              #
//    # #    #     #  #    #    #   #     #  #     #  #        #        #     #
//     #     #     #  #     #  ###  #     #  ######   #######  #######   #####


//TIME RELATED MEASUREMENTS / TIMED ACTIVITIES
int tHour;                  //stores the hours
int hourOffset = 2;         //Offset from server time to our time, also used to emulate night times.
int tMinute;                //stores the minutes
int mMinute = 60;           //used as a modulo to dictate time intervals (modulo is not necessary if you push only once per hour).
int tDay;                   //Used to know when it's weekend. 5, 6, 7 = Friday, Saturday, Sunday
int tSecond;                //stores the seconds
int cooldown = 750;         //Cooldown periods ensuring certain functions are done properly.

//Moment for debugging, make sure this falls within the active / non sleep periods.
int debugStartHour = 9;     //Starting hour of the debug interval
int debugStartMinute = 0;   //Starting minute of the debug interval
int debugEndHour = 9;       //Ending hour of the debug interval
int debugEndMinute = 30;    //Ending minute (exclusive) of the debug interval (if full hour, debugEndMinute should be 60, not 0)
bool debugState = false;    //Used for other functions to detect if the device is in debug mode.
unsigned long printTimer;   //Used to make the printing happen less often (once every interval)
int prInterval = 1000;      //Print interval

//Based on what time the device should go to sleep and wake up, the sleep time in seconds is calculated.
//DO NOT CHANGE THE FOLLOWING FORMULAS, THIS AUTOMATICALLY CALCULATES THE SLEEPTIME!
unsigned long sleepTime = 0;//Explicitely 0 to ensure that even if the calculation fails for some reason, the device boots uip immediately.
bool sleepState = false;    //Used to indicate if the device should go to sleep.

//PUBLISHSTRING used to Publish the location and value to the Particle Cloud.
char publishString[40];

//TRANSMISSION CONFIRMATIONS
int wifiState = 0;              //Used to know if WiFi is on, working, connected and if a dataPublish is possible.
unsigned long stateTimer;       //Used to make sure the wifiState has enough 'delay' to process all commands.

//DISTANCE SENSOR
int sensorPin = (A0);           //Pin that reads out the values of the distence sensor
int sensorValue;                //Raw sensor values
bool sensorState = false;       //Used to know if sensor is low, high or recently low, prevents multiple data additions to value. Range is 0, 1, 2.
uint16_t value = 0;             //Amount of people that walked by.
int statePin = D5;              //The pin which turns the sensor on or off (connected to low-end transistor)
int nulValue = 4098;            //nulValue is the lowest value of the distance sensor (in rest), all calculations are done based on this lowest value. This value starts high, to ensure the first readings are lower.
const int oST = 200;            //Original sensorThreshhold
int sensorThreshold = oST;      //Threshold of the sensor (above this value and somebody walked by).
const int sensorOffset = 100;   //When threshold is met, apply filter to ensure no flickering readings.
int sensorMedian;               //The actual average that's getting output from the value sensorValues RunningMedian.
int sampleSize = 100;           //The size of the samples to be used in the RunningMedian
RunningMedian sensorValues = RunningMedian(sampleSize);  //Creates an array to average the data which then gets stored in value.
unsigned long sensorTimer;      //Used to make sure there are not constant readings or value additions from the sensor.
int sensorTimeOffset = 500;     //The amount of time that has to pass between high and low reading of the sensor.
unsigned long correctionTimer;  //Timer used to correct the nulValue if the sensor is moved or doesn't detect values within a certain range anymore.
int correctionOffset = 10000;   //If the sensor isn't in the range of the nulValue for this amount of time, then the nulValue is increased.
int nulValueRange = 10;         //Sensor value has to be in this range the majority of time.
bool timerSet = false;          //Used to know if a timer has been set for correction

//FIRST RUN & EEPROM DATA
bool firstRun = true;           //Used to activate several parts of the Photon before continuing.
int eepromAddress[] = {0, 2};   //Address of the stored values in EEPROM (uint16_t = 2 bytes)
int eepromUnwritten = 65535;    //Value of eeprom for uint16_t when unwritten.
uint16_t eepromValue1;          //Value 1, compared to value 2.
uint16_t eepromValue2;          //Value 2, compared to value 1.
//Two values are used to ensure that there's *always* a backup value.
//If one fails, the other one can still be used (due to i.e. a power cut)

//ACTIVITY LED
int ledVal = 255;           //Strength of the LED.
unsigned long bleepTimer;   //Timer for when to bleep.
int bleepInterval = 1000;   //At what time to bleep the LED.
int bleepDuration = 100;    //How long the LED will bleep.

//MISCELLANEOUS
SYSTEM_MODE(SEMI_AUTOMATIC);






//   #####   #######  #######  #     #  ######
//  #     #  #           #     #     #  #     #
//  #        #           #     #     #  #     #
//   #####   #####       #     #     #  ######
//        #  #           #     #     #  #
//  #     #  #           #     #     #  #
//   #####   #######     #      #####   #

void setup(){
  //Setting up the credentials for WiFi
  //Multiple credentials for the deploy location and development location
  WiFi.setCredentials(SSID, PASS);
  WiFi.setCredentials(SSID_Location, PASS_Location);

  //Initiates the statePin as an output pin, to turn the sensor on or off.
  pinMode(statePin, OUTPUT);

  //Experimentation for deep sleep.
  WiFi.on();
  delay(cooldown);
  Particle.connect();

  //Initiates a serial communication.
  Serial.begin(9600);
}







//  #        #######  #######  ######
//  #        #     #  #     #  #     #
//  #        #     #  #     #  #     #
//  #        #     #  #     #  ######
//  #        #     #  #     #  #
//  #        #     #  #     #  #
//  #######  #######  #######  #

void loop(){
  //This is executed when Photon turns on (this includes after a deep sleep)
  while(firstRun == true){
    photonWakeUp();
  }

  //Functions used to gather data.
  detectTime();       //Detects time
  readSensor();       //Reads the distance sensor to detect passersby.

  //Functions that are based on time (hours/minutes, etc).
  timedActivities();  //Ensures time related activities are being executed, including dataPublish.

  //Based on the interal timer (millis), make a led blink to show activity.
  //Green is normal activity, blue is with WiFi on.
  ledBleep();

  //Function to debug based on the gathered data.
  serialPrint();      //Used to communicate data to a serial monitor, i.e. the computer.
}







//  #######  ###  #     #  #######  #####_
//     #      #   ##   ##  #        #     #
//     #      #   # # # #  #        #     #
//     #      #   #  #  #  #####    #     #
//     #      #   #     #  #        #     #
//     #      #   #     #  #        #     #
//     #     ###  #     #  #######  #####`

//     #      #####   #######  ###  #     #  ###  #######  ###  #######   #####
//    # #    #     #     #      #   #     #   #      #      #   #        #     #
//   #   #   #           #      #   #     #   #      #      #   #        #
//  #     #  #           #      #   #     #   #      #      #   #####     #####
//  #######  #           #      #    #   #    #      #      #   #              #
//  #     #  #     #     #      #     # #     #      #      #   #        #     #
//  #     #   #####      #     ###     #     ###     #     ###  #######   #####


//All activities related to timing and specific moments of when they need to be executed.
//Including the intervalled dataPublish.
void timedActivities(){
  //=========================================================================//
  //DEBUG WINDOW
  //The Photon is turned on to make it possible to debug, upload new code and so on.
  if(tHour >= debugStartHour && tHour <= debugEndHour && tMinute >= debugStartMinute && tMinute < debugEndMinute){
    if(WiFi.ready()){
      if(Particle.connected()){
        debugState = true;
      }
    }
    else{
      WiFi.on();
      WiFi.connect();
    }
  }
  else{
    debugState = false;
  }
  //=========================================================================//

  //=========================================================================//
  //SLEEPMODE
  //Checks if the device needs to go in sleep mode.
  //Before going to sleep, it first publishes the final data.
  if(tHour == sleepHour && tMinute >= sleepMinute){
    sleepState = true;
  }
  //After the hour of sleep.
  if(tHour > sleepHour){
    sleepState = true;
  }
  //Before the hour of wakeup.
  if(tHour < wakeupHour){
    sleepState = true;
  }
  //In the hour of wakeup, at the minute or before the minute - offset of wakeup.
  if(tHour == wakeupHour && tMinute < (wakeupMinute)){
    sleepState = true;
  }

  //If the day is Saturday (7) or Sunday (1).
  if(tDay == 1 || tDay == 7){
    sleepState = true;
  }

  //When in sleepState, publish the data, then go into sleepmode.
  //Makes sure the device isn't in debugState.
  if(sleepState == true && debugState == false){
    if(value != 0){
      tMinute = mMinute;
    }
    else{
      photonSleep();
    }
  }
  //=========================================================================//

  //=========================================================================//
  //DATAPUBLISH
  //At a specified interval a data transfer will be initiated.
  if(tMinute % mMinute == 0){
    switch (wifiState){
      case 0:
      if(debugState == false){
        WiFi.on();
        WiFi.connect();
      }
      wifiState++;
      break;

      case 1:
      if(WiFi.ready()){
        wifiState++;
        stateTimer = millis();
      }
      break;

      case 2:
      if(millis() > stateTimer + cooldown){
        Particle.process();
        wifiState++;
        stateTimer = millis();
      }
      break;

      case 3:
      if(millis() > stateTimer + cooldown){
        if(Particle.connected()){
          wifiState++;
          stateTimer = millis();
        }
      }
      break;

      case 4:
      if(millis() > stateTimer + cooldown){
        dataPublish();
        wifiState++;
      }
      break;

      case 5:
      if(debugState == false){
//        WiFi.off();
      }
      break;
    }
  }
  else {
    wifiState = 0;
    if(debugState == false){
//      WiFi.off();
    }
  }
  //=========================================================================//
}







//   #####   #######  #     #  #######  ######
//  #     #     #     #     #  #        #     #
//  #     #     #     #     #  #        #     #
//  #     #     #     #######  #####    ######
//  #     #     #     #     #  #        #   #
//  #     #     #     #     #  #        #    #
//   #####      #     #     #  #######  #     #

//  #######  #     #  #     #   #####   #######  ###  #######  #     #   #####
//  #        #     #  ##    #  #     #     #      #   #     #  ##    #  #     #
//  #        #     #  # #   #  #           #      #   #     #  # #   #  #
//  #####    #     #  #  #  #  #           #      #   #     #  #  #  #   #####
//  #        #     #  #   # #  #           #      #   #     #  #   # #        #
//  #        #     #  #    ##  #     #     #      #   #     #  #    ##  #     #
//  #         #####   #     #   #####      #     ###  #######  #     #   #####

//Publishes the location and the value to the Particle Cloud.
//The Particle Cloud uses a webhook to store this information in the database.
void dataPublish(){
  //Converts the location and value into a string with identifiers (var1, var2)
  sprintf(publishString, "{\"var1\": %u, \"var2\": %u}", location, value);
  //The string is published under the identifier "trap"
  //The Trapteller webhook is triggered by this identifier.
  Particle.publish("trap", publishString);
  //To make sure the publish is done properly, a cooldown is added.
  //When not using if/for/switch functions, make sure this cooldown is 1 second.
  delay(cooldown);
  value = 0;    //Resets the value after it has been sent.
  eepromPut();  //Resets the values of the eeprom.
}

//=========================================================================//

void debug(){
  sprintf(publishString, "{Dag: %u / Tijd: %u:%u:%u / Sleeptime: %u / Value: %u}", tDay, tHour, tMinute, tSecond, sleepTime, value);
  Particle.publish("debug", publishString);
  delay(cooldown);
}

//=========================================================================//

//Time is synced with the Particle cloud and the values are stored
void detectTime(){
  //Time correction because Particle Cloud is in another timezone than the Photon.
  tHour = Time.hour() + hourOffset;
  if(tHour > 24){
    tHour - 24;
  }
  tMinute = Time.minute();
  tSecond = Time.second();
  tDay = Time.weekday();
}

//=========================================================================//

//Getting previously stored values from the EEPROM.
//Used to check if there's still data from previous session.
//If this is the case, this data is placed in value so it gets published.
void eepromGet(){
  //Reads EEPROM from specific address.
  EEPROM.get(eepromAddress[0], eepromValue1);
  EEPROM.get(eepromAddress[1], eepromValue2);

  //Checks if the data received is from an empty EEPROM.
  //If this is the case, changes these values to 0.
  if(eepromValue1 == eepromUnwritten){
    eepromValue1 = 0;
  }
  if(eepromValue2 == eepromUnwritten){
    eepromValue2 = 0;
  }

  //Checks if both values are the same. If this is not the case, takes the highest value.
  //This is done because the device could have crashed while writing was still going on.
  if(eepromValue1 != eepromValue2){
    if(eepromValue1 > eepromValue2){
      eepromValue2 = eepromValue1;
    }
    if(eepromValue2 > eepromValue1){
      eepromValue1 = eepromValue2;
    }
  }

  value = eepromValue1;
}

//=========================================================================//

//Puts the current data of value into the correct EEPROM address.
void eepromPut(){
  for(int i = 0; i < 2; i++){
    EEPROM.put(eepromAddress[i], value);
  }
}

//=========================================================================//


//ledBleep is used to indicate some activity on the device.
//Photon's original LED breathing/signals are turned off to save power.
//When deployed, this code CAN be disabled.
//LED color explanation: RED = debug / BLUE = WiFi ready (outside debug) / GREEN = active, no wifi or debug
void ledBleep(){
  if(millis() >= bleepTimer + bleepDuration && millis() < bleepTimer + bleepInterval){
    RGB.color(0, 0, 0);
  }

  if(millis() >= bleepTimer + bleepInterval){
    if(debugState == true){
      RGB.color(ledVal, 0, 0);
    }
    else if(WiFi.ready()){
      RGB.color(0, 0, ledVal);
    }
    else{
      RGB.color(0, ledVal, 0);
    }
    bleepTimer = millis();
  }
}

//=========================================================================//

//Used during the night, ensures the Photon goes to sleep to preserve power.
//Automatically detects if its weekend or not.
void photonSleep(){
  digitalWrite(statePin, LOW);                //Turn off the distance sensor.
  sleepCalculator();                          //Calculate the total sleep time.

  if(WiFi.ready()){
    if(Particle.connected()){
      debug();
    }
  }
  else{
    WiFi.on();
    WiFi.connect();
    photonSleep();
  }

  firstRun == true;                           //Resetting firstRun for the wakeup, not sure if this gets reset on its own.

  System.sleep(SLEEP_MODE_DEEP, sleepTime);   //Going in to deep sleep.

  System.reset(); //Not sure if this is needed, if this function continues after the deep sleep I might as well call it to be sure.
}

//=========================================================================//

//Initiated after the Photon wakes up.
//Initializes all the functions of the Photon for it to work.
void photonWakeUp(){
  printTimer = millis();        //giving a value to the printTimer
  digitalWrite(statePin, HIGH); //Turning the distance sensor on.
  WiFi.on();                    //Turning WiFi on...
  WiFi.connect();               //Making it connect...
  delay(cooldown);

  //Checking if it's connected
  if(Particle.connected()){
    delay(cooldown);
    detectTime();               //detect time
    delay(cooldown);
    eepromGet();                //get data from the EEPROM
    delay(cooldown);
//    RGBcontrol();               //get control of the RGB, as it uses quite some power.
    debug();
    bleepTimer = millis();      //starting the timer for the LED bleeps.
    firstRun = false;           //and to exit the firstRun.
  }
  else{
    photonWakeUp();
  }
}

//=========================================================================//

//Reads the distance sensor to detect someone passing by.
//Applying a filter to ensure that jittering values don't go above or under the threshold rapidly.
void readSensor(){
  //Reading the sensor and adding it to the sensorMedian.
  sensorValue = analogRead(sensorPin);
  sensorValues.add(sensorValue);
  sensorMedian = int(sensorValues.getMedian());

  //Detects if the current sensorMedian is lower than the lowest value (this is the point where offSet should start)
  //Removes the nulValue from the sensorMedian, ensuring all the readings have a baseline of nulValue.
  if(sensorMedian < nulValue){
    nulValue = sensorMedian;
  }
  sensorMedian -= nulValue;

  if(sensorMedian > nulValueRange){
    if(timerSet == false){
      correctionTimer = millis();
      timerSet = true;
    }
    if(timerSet == true){
      if(millis() > correctionTimer + correctionOffset){
        nulValue++;
      }
    }
  }
  else{
    timerSet = false;
  }

  //Detects if the average of the sensorValue is over the threshold while the sensorState was false.
  //If this is the case, the threshold is lowered (to ensure a better reading and no flickering)
  //And value is increased by one (since one person passed by), at this point the sensorState is true, because someone is passing by.
  if(sensorMedian > sensorThreshold && sensorState == false){
    sensorThreshold -= sensorOffset;
    value++;
    eepromPut();
    sensorState = true;
    sensorTimer = millis();
  }

  //Prevents too many data inputs (i.e. by swaying someone's hands in front of it)
  if(sensorMedian > sensorThreshold && sensorState == true && (millis() < (sensorTimer + sensorTimeOffset))){
    sensorTimer = millis();
  }

  //Since the threshold has been lowered, the sensorValue first has to drop
  //Then, if this happens and a short timer has passed, the sensorState is put to false indicating someone has passed.
  if(sensorMedian < sensorThreshold && sensorState == true && (millis() > (sensorTimer + sensorTimeOffset))){
    sensorThreshold = oST;
    sensorState = false;
  }
}

//=========================================================================//

//Get control of the Photon RGB LED, turning it off to preserve power.
void RGBcontrol(){
  RGB.control(true);
  RGB.color(0,0,0);
}

//=========================================================================//

//Prints desired data to the console for debugging.
void serialPrint(){
  //Wait for interval to pass.
  if(millis() > printTimer + prInterval){
    //Print time
    if(tHour < 10){
      Serial.print("0");
    }
    Serial.print(tHour);
    Serial.print(":");
    if(tMinute < 10){
      Serial.print("0");
    }
    Serial.print(tMinute);
    Serial.print(":");
    if(tSecond < 10){
      Serial.print("0");
    }
    Serial.print(tSecond);

    //Print other desired values
    Serial.print("value");
    Serial.print("\t");
    Serial.print(value);

    //Line Break
    Serial.println();

    //Resetting the printTimer.
    printTimer = millis();
  }
}

//=========================================================================//

//Calculates the total time to sleep until the next wakeup.
void sleepCalculator(){
  unsigned int calcEvening;
  unsigned int calcNight;
  unsigned int calcWeekend;

  //Detect if its currently after sleepHour/Minute but before midnight or before wakeupHour/Minute and after midnight.
  //Based on that, calculate the calcEvening and calcNight values.
  if(tHour == sleepHour && tMinute >= sleepMinute){
    calcEvening = (((24 - tHour) * 60) - tMinute) * 60;
    calcNight   = (wakeupHour * 60 + wakeupMinute) * 60;
  }
  else if(tHour > sleepHour){
    calcEvening = (((24 - tHour) * 60) - tMinute) * 60;
    calcNight   = (wakeupHour * 60 + wakeupMinute) * 60;
  }
  else if(tHour < wakeupHour){
    calcEvening = 0;
    calcNight   = ((wakeupHour - tHour) * 60 - tMinute + wakeupMinute) * 60;
  }
  else if(tHour == wakeupHour && tMinute < wakeupMinute){
    calcEvening = 0;
    calcNight = (wakeupMinute - tMinute) * 60;
  }

  //Based on the day, add extra time for weekend sleep.
  if(tDay == 6){
    if(tHour == sleepHour && tMinute >= sleepMinute){
      calcWeekend = 172800;
    }
    if(tHour > sleepHour){
      calcWeekend = 172800;
    }
    if(tHour < wakeupHour){
      calcWeekend = 0;
    }
    if(tHour == wakeupHour && tMinute >= sleepMinute){
      calcWeekend = 0;
    }
  }
  else if(tDay == 7){
    calcWeekend = 86400;
  }
  else{
    calcWeekend = 0;
  }

  sleepTime = (long) (calcEvening + calcNight + calcWeekend);

  if(sleepTime > 259200){
    sleepCalculator();
  }
}

That's what we are for :wink:
But no, WiFi.on() is not part of Particle.connect() - at least as far as I know.

Sometimes several factors work in combination to cause an issue, so getting rid of all seemingly unrelated but potentially contributing issues might help to solve the problem or at least help locate the actual cause.

One hypothesis could be that something like an almost full stack (due to recusions) does not allow another command (e.g. System.sleep()) to execute properly (e.g. set the RTC wake time).

Alright, I’m going to look into waitUntil() function, but this does pause all the code right? Because ideally I want to keep on reading the sensor. And is waitFor() basically a waitUntil() but with a timer attached to it so you don’t wait too long?

@Len, I don’t see any setting of SYSTEM_MODE which is AUTOMATIC by default. This may explain why publishes seem to work when they shouldn’t since the system is reconnecting on its own. You should consider using SYSTEM_MODE(SEMIT_AUTOMATIC) and then control wifi and cloud connectivity. Looking at your code quickly I can make a bunch of recommendations but not till I get home. One important one is that you set credentials on every bootup which will quickly lead to flash/eeporm wear. You may want to look at doing that only once (see WiFi.hasCredentials()). :wink:

1 Like

Yup!

If you don't want to block your code you can always use a loop instead of recursions - recursions are usually no good idea on embedded systems unless the recursion itself is part of the actual logic.

Something like this might be better suited

  WiFi.on();                    //Turning WiFi on...
  Particle.connect();           //Making it connect...

  for(uint32_t ms = millis(); millis() - ms < 60000 && !Particle.connected();Particle.process())
  {
    // do anything like reading sensors as long it doesn't require WiFi/cloud 
  }

  if(!Particle.connected())    // still not connected after timeout
  {
    // do what's required
  }
  else
  {
    // now we a good to do anything
  }

Thank you, both!

@peekay123, I’m interested in your recommendations. This is basically my first Photon project and it is already clear that there are many functions I can explore to make my code better. About the credentials, that’s good to know. Correct me if I’m wrong, but if I flash my Photon with code that only adds credentials, those credentials will always be available? Like: I develop at location 1 and the device is deployed at location 2. But when I want to deploy it at location 3, I simply flash those on the device and we’re good to go for location 3, correct? If I remember correctly, the Photon saves the last 5 WiFi credentials that were flashed to the device.

@ScruffR that seems a good solution, since I have a sensorRead() function specifically written to read out the data and store it. Will try it out.

Many thanks again.

Also, are there any problems with my deep sleep code? I’ll experiment with your additions now and check back tomorrow if the long deep sleep (during the night) works. Currently, I’m testing out 10 minute deep sleep periods and that works without a problem.

I’ll keep you posted.

@peekay123, there is a bit further down, just above setup() :wink:

That's correct.

For timed activities, I'd rather avoid the "complicated" conditions when using times split up to hours/minutes/seconds, but rather go for the smallest unit and have all times unified in one value (hour3600 + minute60 + second). That makes the conditions easier and less prone to conditional "gaps".

e.g. this might not do what you expect if you set the minutes to non-hour boundaries

  if(tHour >= debugStartHour && tHour <= debugEndHour && tMinute >= debugStartMinute && tMinute < debugEndMinute)

There is a Time.zone() setting that caters for that.

1 Like

@Len, another approach to setting wifi credentials is to use SoftAP to provide a web page to the local users to set the wifi credentials themselves. You simply ship the Photon is all credentials wiped and when the Photon boots it will come up in listening mode and active SoftAP. The example code in the docs are all you need. I use this approach myself!

1 Like

I’d like to add that I only added SEMI_AUTOMATIC today, I’m not sure if it works but so far everything is doing fine.

About the multiple if()-statements, I did this on purpose because there were so many conditions, I still need to shorten it so I just went for something that I knew would work. The sleepTime calculation is happening perfectly.

My workday is almost over, so I’m going over this again tomorrow. Thanks again.

So, in regards to the problems with deep sleep. This morning the device didn’t wake up, again. I tested it yesterday and deep sleep of 10 minutes is not a problem at all. However, it went into deep sleep for 13,5 hours last night (or 48600 seconds). Currently it’s 8:55, it should’ve woken up at 7:30. No lights on the Photon are blinking, there’s no activity whatsoever.

How is this possible? Why is it seemingly locked in deep sleep? Currently I’m waiting if the Photon will wake up at all. Not sure if or when this is going to happen.

About your forloop, I’ve never seen it being used like that so I’m interested in what is going on.

for(uint32_t ms = millis(); millis() - ms < 60000 && !Particle.connected();Particle.process())

The first part is clear to me, a simple millis() timer, but I’m used to seeing forloops like this for(int i = 0; i < 5; i++). How does your for loop work…?

A for(pre;condition;post) loop is actually nothing else than a while() loop that looks like this

  pre;
  while(condition)
  {
    ...
    post;
  }

It is usually used for counting loops, but that’s not its sole purpose.

So my wait loop is noting else than

{ // limited scope for ms
  uint32_t ms = millis();
  while(millis() - ms < 60000 && !Particle.connected())
  {
    Particle.process();
  }
}

just shorter (and more neardy :sunglasses:)

1 Like

Cool stuff and it does indeed look nicer, i like it.

But do you have any idea of the following that I posted above? I just resetted the Photon because it just took too long and I lost hope of it waking up on its own. I'm a bit confused as to why this happens. Does deep sleep have a maximum value? Because shorter tests work perfectly, but 13.5 hours does not.

Thanks again for all your insights, it's fun to learn new ways of programming :smile:

How have you got the Photon powered?
Do you post the calculated wake time/sleep dutration before going to sleep?
Are you using Time.zone() now for the time zone offset?

There should not be any other limit for the wake timeout other than the datatype used for the parameter which should be billions of seconds :wink:

Time.zone() alright, found something new again haha. I now manually add an offset to the time I get from the Particle Cloud.

The sleep and wake up time are declared in the code, before the device goes to sleep it publishes the value of the sleeptime. I have calculated that this is done correct every single time.

The Photon is currently powered by a 5V adapter (1 or 1.5A), so there’s sufficient power going to the Photon.

Does System.sleep() need a long? In my code I use integers which I then make into a long, could be a do something wrong there.

sleepTime = (long) (calcEvening + calcNight + calcWeekend);

I’ve declared sleepTime as an unsigned long.