External Watchdog and Sleep - Advice needed

Hey, crazy busy at the moment but I’ll take a look ASAP.

@RWB

Thank you - I know you have a lot of experience here. Here is the latest code with the delays that @ScruffR recommended. I am still not seeing the low energy consumption in sleep I had hoped for and the DEEP sleep does not seem to make much of a difference.

/*
* Project Cellular-Logger-PIR-SOLAR
* Description: Cellular Connected Data Logger
* Author: Chip McClelland
* Date:8 October 2017
*/


// Easy place to change global numbers
#define SOFTWARERELEASENUMBER "0.15"
#define PARKCLOSES 18
#define PARKOPENS 7

// Prototypes and System Mode calls
SYSTEM_MODE(SEMI_AUTOMATIC);    // This will enable user code to start executing automatically.
SYSTEM_THREAD(ENABLED);         // Means my code will not be held up by Particle processes.
FuelGauge batteryMonitor;       // Prototype for the fuel gauge (included in Particle core library)
PMIC pmic;                      //Initalize the PMIC class so you can call the Power Management functions below.


// State Maching Variables
enum State { INITIALIZATION_STATE, ERROR_STATE, IDLE_STATE, SLEEPING_STATE, NAPPING_STATE, REPORTING_STATE, RESP_WAIT_STATE };
State state = INITIALIZATION_STATE;

// Pin Constants
const int intPin = D3;                      // Acclerometer interrupt pin
const int blueLED = D7;                     // This LED is on the Electron itself
const int userSwitch = D5;                  // User switch with a pull-up resistor

// Timing Variables
unsigned long resetWaitTimeStamp = 0;       // Starts the reset wait clock
unsigned long resetWaitTime = 30000;        // Will wait this lonk before resetting.
unsigned long sleepDelay = 60000;           // Amount of time to stay awake after an event - too short and could cost power
unsigned long lastEvent = 0;                // Keeps track of the last time there was an event
bool waiting = false;
int currentPeriod = 0;                      // Change length of period for testing 2 places in main loop

// Program Variables
int hourlyPersonCount = 0;
bool ledState = LOW;                        // variable used to store the last LED status, to toggle the light
const char* releaseNumber = SOFTWARERELEASENUMBER;  // Displays the release on the menu

// PIR Sensor variables
volatile bool sensorDetect = false;         // This is the flag that an interrupt is triggered

// Battery monitor
int stateOfCharge = 0;                      // stores battery charge level value

void setup()
{
  Particle.connect();                         // Connect to Particle on bootup - will disonnect on nap or sleep
  Serial.begin(9600);                         // Serial for debugging, will come out later
  pmic.begin();                               // For power management
  Serial.println("");                         // Header information
  Serial.print(F("Electron-Sleep-Test-PIR - release "));
  Serial.println(releaseNumber);

  pinMode(intPin,INPUT);                      // PIR interrupt pinMode
  pinMode(blueLED, OUTPUT);                   // declare the Blue LED Pin as an output

  attachInterrupt(intPin,sensorISR,RISING);   // Will know when the PIR sensor is triggered

  Particle.variable("Release",releaseNumber); // Make sure we know what version of software is running
  Particle.variable("stateOfChg", stateOfCharge); // Track Battery Level

  Time.zone(-4);                              // Set time zone to Eastern USA daylight saving time

  //pmic.setChargeCurrent(0,0,1,0,0,0);         //Set charging current to 1024mA (512 + 512 offset) thank you @RWB for these two lines
  pmic.setInputVoltageLimit(4840);            //Set the lowest input voltage to 4.84 volts. This keeps my 5v solar panel from operating below 4.84 volts.
  stateOfCharge = int(batteryMonitor.getSoC()); // Percentage of full charg

  state = IDLE_STATE;                         // Idle and look for events to change the state
}

void loop()
{
  switch(state) {
  case IDLE_STATE:
    if (Time.hour() != currentPeriod)                       // Spring into action each hour on the hour
    {
      currentPeriod = Time.hour();                          // Set the new current period
      state = REPORTING_STATE;                              // We want to report on the hour
      break;
    }
    if (sensorDetect) recordCount();                        // The ISR had raised the sensor flag
    if (millis() >= (lastEvent + sleepDelay)) state = NAPPING_STATE;  // Too long since last sensor flag - time to nap
    if (Time.hour() >= PARKCLOSES) state = SLEEPING_STATE;  // The park is closed, time to sleep
    break;

  case SLEEPING_STATE:
    Particle.disconnect();                                   // Disconnect from Particle in prep for sleep
    delay(3000);
    Cellular.disconnect();                                   // Disconnect from the cellular network
    delay(3000);

    Cellular.off();                                          // Turn off the cellular modem    digitalWrite(blueLED,LOW);    // Turn off the on-board light
    digitalWrite(blueLED,LOW);                               // Turn off the LED
    Serial.println("Park closed go to sleep");
    static long secondsToOpen = ((24 - Time.hour())+PARKOPENS)*3600;  // Set the alarm (in seconds) till park opens again
    System.sleep(SLEEP_MODE_DEEP,secondsToOpen);              // Sleep till morning
    state = REPORTING_STATE;                                  // Report when we wake from sleep
    break;

  case NAPPING_STATE:
    Particle.disconnect();                                   // Disconnect from Particle in prep for sleep
    delay(3000);
    Cellular.disconnect();                                   // Disconnect from the cellular network
    delay(3000);
    Cellular.off();                                          // Turn off the cellular modem    digitalWrite(blueLED,LOW);    // Turn off the on-board light
    sensorDetect = true;                                     // Woke up so there must have been an event
    lastEvent = millis();                                    // Reset millis so we don't wake and then nap again
    digitalWrite(blueLED,LOW);                               // Turn off the LED
    Serial.print("Going to sleep ...");
    static int secondsToHour = (60 - Time.minute())*60;      // Time till the top of the hour
    System.sleep(intPin,RISING,secondsToHour);               // Sensor will wake us with an interrupt
    attachInterrupt(intPin,sensorISR,RISING);                // Sensor interrupt from low to high
    sleepDelay = 10000;                                      // Sets the sleep delay to 10 seconds after a nap
    state = IDLE_STATE;                                      // Back to the IDLE_STATE after a nap
    break;

  case REPORTING_STATE:
    static bool success = false;                             // Was data received
    Cellular.on();                                           // turn on the Modem
    Cellular.connect();                                      // Connect to the cellular network
    Particle.connect();                                      // Connect to Particle
    success = Particle.publish("State","Reporting");         // Sample message.
    sleepDelay = 60000;     // Sets the sleep delay to 60 seconds after reporting to give time to flash if needed
    if (success) state = IDLE_STATE;                         // Success - go to IDLE_STATE
    else state = ERROR_STATE;                                // Failure - go to ERROR_STATE
    break;

  case ERROR_STATE:                                          // To be enhanced - where we deal with errors
    if (!waiting)                                            // Will use this flag to wiat 30 seconds before reset
    {
      waiting = true;
      resetWaitTimeStamp = millis();
      Particle.publish("State","Resetting in 30 sec");
    }
    if (millis() >= (resetWaitTimeStamp + resetWaitTime)) System.reset();   // Today, only way out is reset
    break;
  }
}

void recordCount()                                          // Handles counting when the sensor triggers
{
  lastEvent = millis();                                     // Important to keep from napping too soon
  sensorDetect = false;                                     // Reset the flag
  Serial.println("It is a tap - counting");
  hourlyPersonCount++;                                      // Increment the PersonCount
  Serial.print("Hourly: ");                                 // Serial reporting for debugging
  Serial.print(hourlyPersonCount);
  Serial.print("  Time: ");
  Serial.println(Time.timeStr());                           // Prints time string example: Wed May 21 01:08:47 2014
  ledState = !ledState;                                     // toggle the status of the LEDPIN:
  digitalWrite(blueLED, ledState);                          // update the LED pin itself
}

void sensorISR()
{
  sensorDetect = true;                                      // sets the sensor flag for the main loop
}

Understand you are busy so, whenever you have a chance.

Thanks,

Chip

What is the sleep current your seeing? 18mA?

That’s just for the Electron right, no other equipment in the mix pulling power at the same time?

@RWB,

I am seeing 28mA but that is for the Electron and the carrier board. The board should not draw more than a couple mA but, I need to test that.

BTW, I used the line that is now commented out to change the current charge register. Unfortunately, the max current is now about 170mA so, I need to reset it once I know what the register values are. This is not on the Particle reference docs so, was wondering if you knew where the PMIC registers are documented.

Thanks,

Chip

@ScruffR or @RWB

I have upgraded to 0.7.0 but I suspect that I am still having issues with the power on and off process for the cellular modem. I am in Semi-Automatic mode so:

  • Power on is : Cellular.on() then Cellular.connect() and finally Particle.connect()
  • Power off is : Particle.disconnect(), Cellular.disconnect() and finally Cellular.off()

The Power On sequence was causing problems so, I added a couple check steps (trying not to use delay())

    Cellular.on();                                           // turn on the Modem
    Cellular.connect();                                      // Connect to the cellular network
    while(!Cellular.ready())
    Particle.connect();                                      // Connect to Particle
    while(!Particle.connected());
    success = Particle.publish("State","Reporting");         // Sample message.

I am not sure how I can tell if the Power Off sequence is working.

I have searched the forums and don’t see much example code. I also think that these commands may function differently with the 0.7.0 release (I am running 0.7.0 rc3) and I don’t know where to get the specifics.

Any help?

Thanks,

Chip

I currently don’t have time to pull out the Electron and hook it up my power supply to test current pull under the different settings.

The latest firmware was supposed to get rid of having to shut down the modem via the code below:

 //The 6 lines of code below are needed to turn off the Modem before sleeping if your using SYSTEM_THREAD(ENABLED); with the current 0.6.0 firmware. It's a AT Command problem currently. 
  Cellular.on();
  delay(10000);
  Cellular.command("AT+CPWROFF\r\n");
  delay(2000);
  FuelGauge().sleep();
  delay(2000);
  
  
  digitalWrite(ledPin, HIGH);   // Sets the LED on
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, LOW);    // Sets the LED off
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, HIGH);   // Sets the LED on
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, LOW);    // Sets the LED off
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, HIGH);   // Sets the LED on
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, LOW);    // Sets the LED off
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, HIGH);   // Sets the LED on
  delay(150);                   // Waits for a second
  digitalWrite(ledPin, LOW);    // Sets the LED off
  
  System.sleep(SLEEP_MODE_DEEP, 3600);  //Put the Electron into Deep Sleep for 1 Hour. 

I remember it took a few seconds to see the sleep power drop off after the cellular modem actually went to sleep.

The code above is not supposed to be needed with the new firmware but you can give it a try regardless.

I did not usually put the fuel gauge to sleep because of the wild SOC reading swings you would get on wakeup every time but you can try that also to see what difference it makes.

When I had the Electron outside running off the small 3w solar panel I would put it to sleep for 2 mins before waking up to send battery status info again. I would sleep using this code:

   System.sleep(D0, RISING,sleepInterval * 2, SLEEP_NETWORK_STANDBY); //Put the Electron into Sleep Mode for 2 Mins + leave the Modem in Sleep Standby mode so when you wake up the modem is ready to send data vs a full reconnection process.  

And then when I went to deep sleep for 1 hour when the battery was below 20% I would use the first batch of code above.

The using this PMIC code below in Setup the battery would charge at the higher charge currents but it only works if the charging is happening when the change charging current increase code is ran. It will keep charging at the higher currents as long as the Vin see’s voltage from the solar panel which will happen during the daylight hours. But the PMIC defaults to 50mA of charging current again once the Electron is powered down.

So once the higher charge current code is ran it will enable higher current charging but once device goes into deep sleep it reverts back to 50mA charge current max. When it wakes up and the setup code runs again the charging current will go back up to where ever you set it.

The registers for the PMIC is in the TI Datasheet for that chip.

 //pmic.setChargeCurrent(0,0,1,0,0,0);         //Set charging current to 1024mA (512 + 512 offset) thank you @RWB for these two lines
  pmic.setInputVoltageLimit(4840);            //Set the lowest input voltage to 4.84 volts. This keeps my 5v solar panel from operating below 4.84 volts.

I’m have not done any sleep power testing with the latest firmware.

@RWB,

Thank you for the response but, one thing you said is bothering me. Part of what makes the whole sleep thing work is that the solar panel can get ahead of consumption and charge the battery. Are you saying that when the Electron is sleeping it cannot charge the battery at more than a 50mA rate? If so, that is a show stopper. Isn’t it?

Chip

My test system would wake up every hour to check to see if the SOC had risen above 20%.

Every time the device woke up the PMIC increase charging current code would run and as long as the solar panel was providing voltage to the PMIC input it would maintain the higher charging current all day long.

So as long as you wake up early in the day the PMIC increase charging current code will run while the solar panel is generating power which will keep the PMIC operating under that higher charging current setting. Once power to the PMIC is cut it resets to the 50mA charging current.

So no it’s not a show stopper.

@RWB,

Thank you for the clarification. I will follow the same approach and wake on the hour during the day.

Chip