@ScruffR,
Thank you. Makes sense. I have several of these sensors in the park already so, I have an idea of how many “events” I will see in an average day. The number of hikers changes all the time so the sensor will see (on average) a hiker every 6 minutes with as few as one an hour and as many as 60.
So, I think what makes sense is to nap as quickly as possible, wake within 3 seconds (the debounce between sensor events) during the day and take a deep sleep at night. I have modified the code to include the Cellular.off(); commands but, to be honest, I am not seeing much of a reduction. The Electron is using about 28mA while sleeping with the modem off, this seems high to me.
Any idea what I am missing to get a lower sleep power consumption rate? Modified code below with some additions from @RWB
/*
* 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 14
#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
Cellular.disconnect(); // Disconnect from the cellular network
Cellular.off(); // Turn off the cellular modem
Serial.println("Park closed go to sleep");
static long secondsToOpen = 600; // Test - sleep for 10 minutes
//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
Cellular.disconnect(); // Disconnect from the cellular network
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
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
}
Thank you,
Chip