[Photon] SLEEP_MODE_DEEP waking problems [Solved]

Old code that I know 100% sure was working properly for the serial communication:

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

//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 = 3;

//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";







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

#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 tMinute;                //stores the minutes
int tDay;                   //Used to know when it's weekend. 5, 6, 7 = Friday, Saturday, Sunday
int mMinute = 60;            //used as a modulo to dictate time intervals (modulo is not necessary if you push only once per hour).
int tSecond;                //stores the seconds
int dSecond = 60;           //used to store when a data transfer can be initiated
int sOffset = 5;            //after dSecond + sOffset the data transfer will begin
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 = 15;    //Ending minute (exclusive) of the debug interval
bool debugMode = 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 = 100;       //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!
const long sleepTime = int((((24 - sleepHour) - (sleepMinute / 60) + wakeupHour + (wakeupMinute / 60)) * 3600) - 120);
const long sleepTimeWeekend = int(sleepTime + 172800); //sleepTime + 2 weekend days of 2 * 24 * * 60 * 60 seconds.


//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 oST = 1000;             //Original sensorThreshhold
int sensorThreshold = oST;  //Threshold of the sensor (above this value and somebody walked by).
int sensorOffset = 300;     //When threshold is met, apply filter to ensure no flickering readings.
unsigned long sensorTimer;  //Used to make sure there are not constant readings or value additions from the sensor.
int sensorTimeOffset = 333; //The amount of time that has to pass between high and low reading of the sensor.
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.

//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)
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)







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

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);

  //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.

  //Function doing something with the gathered data.
  timedActivities();  //Ensures time related activities are being executed, including dataPublish.

  //Function to debug baed 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()){
      debugMode = true;
    }
    else{
      WiFi.on();
      WiFi.connect();
    }
  }
  else{
    debugMode = false;
  }
  //=========================================================================//

  //=========================================================================//
  //SLEEPMODE
  //At a specified moment, the Photon goes into a timed deep sleep mode.
  //This sleepTime is automatically calculated based on the settings.
  //But first, it checks if there's still data that hasn't been pushed.
  //Sleep mode otherwise flushes this data, so it must be sent before sleep.
  if(tHour == sleepHour && tMinute == sleepMinute){
    if(value != 0){
      tMinute = mMinute = 0;
    }
    else{
      photonSleep();
    }
  }
  //=========================================================================//

  //=========================================================================//
  //DATAPUBLISH
  //At a specified interval a data transfer will be initiated.
  if(tMinute % mMinute == 0){
    switch (wifiState){
      case 0:
      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(debugMode == false){
        WiFi.off();
      }
      break;
    }
  }
  else {
    wifiState = 0;
    if(debugMode == 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.
}

//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() + 2;
  if(tHour > 24){
    tHour - 24;
  }
  tMinute = Time.minute();
  tSecond = Time.second();
  tDay = Time.weekday();
}

//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);
  if(tDay != 6){
    System.sleep(SLEEP_MODE_DEEP, sleepTime);
  }
  else if (tDay == 6){
    System.sleep(SLEEP_MODE_DEEP, sleepTimeWeekend);
  }
}

//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...

  //Getting previously stored values from the EEPROM.
  //Used to check if there's still data from previous session.
  EEPROM.get(eepromAddress[0], eepromValue1);
  EEPROM.get(eepromAddress[1], eepromValue2);

  //Checking if it's connected, to...
  if(Particle.connected()){
    delay(cooldown);
    detectTime();               //detect time
    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 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++;
    sensorState = true;
    sensorTimer = millis();
  }

  //Detects if people troll the device by swaying their 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;
  }

  /*value = (analogRead(sensorPin) % 7);
  if(value == 0){
  value++;
  }*/
}

//Prints desired data to the console for debugging.
void serialPrint(){
  if(millis() > printTimer + prInterval){
    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);
    Serial.print("\t");
    Serial.print("wifiState:");
    Serial.print(wifiState);
    Serial.print("\t");
    Serial.print("Debug:");
    Serial.print(debugMode);
    Serial.print("\t");
    Serial.print("Connected:");
    Serial.print(Particle.connected());
    Serial.print("\t");
    Serial.print("Value:");
    Serial.print(value);
    Serial.print("\t");
    Serial.print("Sensor:");
    Serial.print(sensorMedian);
    Serial.print("\t");
    Serial.print("EEPROM:");
    Serial.print(eepromValue1);
    Serial.print("\t");
    Serial.print(eepromValue2);
    /*Serial.print("\t");
    Serial.print("Day:");
    Serial.print(tDay);
    Serial.print("\t");
    Serial.print("sleepTime:");
    Serial.print(sleepTime);
    Serial.print("/");
    Serial.print(sleepTimeWeekend);*/

    //Line Break
    Serial.println();

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

The code was written on 0.4.6 if I remember correctly. My device is currently running 0.5.2.
Is there a way to downgrade to 0.4.6 if necessary?

You can downgrade, but I wouldn’t see any reason - there must be another cause than the system firmware.

That’s what I was thinking as well, until I flashed it with the old version of the code that worked. Maybe newer firmware requires something different or more when opening serial, I do not know. All I know is that this code worked on 0.4.6.

You definetly need to clean up some of that code :wink:

e.g.

  if(WiFi.ready()){                             //Turns on the WiFi if it's currently not on.
    WiFi.on();
  }

Also don’t wear out the credentials flash by always writing them on each wake.
Use Time.zone()
Try out my sleepCalculator() and apply that logic used there to all your time range checks.
Keep WiFi control and System.sleep() commands in one place.
Replace all WiFi.connect() with Particle.connect() (unless you actually need WiFi but no cloud)
Don’t mix data types like here

  if(millis() > printTimer + prInterval)

Don’t compare millis() that way, but rather this way

  if(millis() - printTimer > prInterval)

to prevent incompatibilities between unsigned and signed calculations and comparisons.

1 Like

I think I’m going to rewrite the whole lot after all your advice, ha ha. Obviously, the print works again, I don’t know why it worked in the old code (I assure you it did) but this is a good exercise for me. Really, thanks for all the insights you’re giving me :smile:

Yeah! It seems to be fixed and the deep sleep is working correct :smile:
Now, a test during the night but I don’t doubt it works.

Thank you for your help and insights :slight_smile:

2 Likes

Hi Len, if you don’t mind, can you post your latest code? I need to do the same with a photon and your code will help me a lot!
Thanks
Gustavo.

Check out the post by @ScruffR.

And then, when you want your device to sleep, you simply call it like so:

System.sleep(SLEEP_MODE_DEEP, sleepCalculator())

This returns uint32_t deltaTime to your System.sleep() based on the wakeTime and sleepTime.

However, keep in mind that sleepCalculator() assumes the device goes to sleep before midnight and wakes up after midnight.

1 Like

I’m not sure if pushing the returned value directly into the sleep call will be good.
You might need to check, but I think if the sleep time is 0 it actually means indefinetly :wink:

4 Likes

Oh, that is good to know haha. Also, the device woke up this morning so all is working :slight_smile:

2 Likes

Hello Len!

Me and my classmates are working on a project that is basically a electronic post-it note for our professors to use on their doors at school to leave notes for other faculty members or students. We have came up with a code that allows you to send an electronic-ink screen a message using the particle app or the console online. We are now attempting to do what you are doing and have the photon wake up at a certain time in the morning, and go to sleep for the duration of the night, plus weekends and such.

I have read through this thread multiple times for help and I have read @scruffR 's latest post about how to get the device to wake up and sleep at a certain time, but I am wondering what your code looks like for the sleep mode. Are you using deep sleep mode? I have called the System.sleep(SLEEP_MODE_DEEP, sleepCalculator())
after the wakeup and sleep calcs but it just seems to shut the photon completely down. I am wondering if i need to call this before all of that or not. Is there anyway you could send me the section of the code which deals with putting the photon to sleep? Please let me know, this would be very very helpful for us! thank you.

  • Paul

Hey @p-kollat ,

As soon as you call System.sleep with SLEEP_MODE_DEEP it goes into deep sleep mode for the amount of time returned by sleepCalculator(). So you should only call that function when your device is ready to go to sleep for the night. With that call in particular the device will restart code execution when it wakes up. It will not resume where it left off. In your case you can just check the time in your main loop and only call the sleep when you’ve hit the time you define as end-of-day. sleepCalculator() in this example is a little overloaded in that regard meaning, as used, it will still go to sleep if it’s not time to sleep. For more info on sleep modes I put together a YouTube video that explains the different modes.