@ScruffR or @avtolstoy,
I think I am getting closer to figuring out what the issue is.
There are three things needed to get the device into this state:
- The new Sleep 2.0 API
- The new Power Management API
- Sleep
I have simplified the code base to remove any hardware dependencies - all you need is a Boron and a Battery.
When the Boron boots up, it will publish the battery level every 30 minutes. You will see that the battery level changes as expected. However, when you put the device in to “lowPowerMode” by typing “Yes” at the console, the device will start to sleep at the next 30 minute interval and then wake to report the battery level every 30 minutes. Unless the device is reset, the battery level will not change.
/*
* Project Sleep-Battery-Test
* Description: Simple test for the Effect of Sleep on Battery SOC reporting
* Author: Chip McClelland
* Date: 4-6-20
*
* When the Boron boots up, it will publish the battery level every 30 minutes.
* You will see that the battery level changes as expected.
* However, when you put the device in to "lowPowerMode" by typing "Yes" at the console, the device will start to sleep at the next 30 minute interval
* and then wake to report the battery level every 30 minutes. Unless the device is reset, the battery level will not change.
*/
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)
SystemPowerConfiguration conf; // Initalize the new Power Management API to support changes in needed for Solar or DC Power below.
SystemSleepConfiguration config;
enum State { IDLE_STATE, NAPPING_STATE, REPORTING_STATE};
char stateNames[8][14] = {"Idle", "Napping", "Reporting"};
State state = IDLE_STATE;
//Program Variables
bool lowPowerMode = false; // Flag for wake / sleep cycle setting
float stateOfCharge = 0.00;
// Timing Variables
unsigned long reportingTimeStamp;
unsigned long reportingFrequencySec = 1800; // How often do we report
void setup() {
Particle.function("LowPowerMode",setLowPowerMode);
Particle.publish("Status","Setup Complete",PRIVATE);
// Comment out this section out and the problem goes away
conf.powerSourceMaxCurrent(1500) // default is 900mA this let's me charge faster
.powerSourceMinVoltage(4208) // This is the default value for the Boron
.batteryChargeCurrent(1024) // default is 2048mA (011000) = 512mA+1024mA+512mA)
.batteryChargeVoltage(4112) // default is 4.112V termination voltage
.feature(SystemPowerFeature::PMIC_DETECTION)
.feature(SystemPowerFeature::USE_VIN_SETTINGS_WITH_USB_HOST) ;
System.setPowerConfiguration(conf); // These settings are for the DC power mode
config.mode(SystemSleepMode::STOP)
.duration(reportingFrequencySec * 1000)
.flag(SystemSleepFlag::WAIT_CLOUD);
}
void loop() {
switch(state) {
case IDLE_STATE: // Where we spend most time - note, the order of these conditionals is important
if (Time.now() - reportingTimeStamp > reportingFrequencySec) state = REPORTING_STATE;
break;
case REPORTING_STATE: {
char data[64];
if (!Particle.connected()) connectToParticle(); // Only attempt to connect if not already New process to get connected
if (Particle.connected()) {
stateOfCharge = batteryMonitor.getSoC(); // Percentage of full charge
snprintf(data,sizeof(data),"State of Charge = %4.2f %%",stateOfCharge);
waitUntil(meterParticlePublish);
Particle.publish("Status", data, PRIVATE);
reportingTimeStamp = Time.now();
(lowPowerMode) ? state = NAPPING_STATE : state = IDLE_STATE;
}
} break;
case NAPPING_STATE: { // This state puts the device in low power mode quickly
if (Particle.connected())
waitUntil(meterParticlePublish);
Particle.publish("Status","Napping",PRIVATE);
disconnectFromParticle(); // If we are in connected mode we need to Disconnect from Particle
System.sleep(config); // Sensor will wake at the interval set by the reportingFrequencySec variable
state = IDLE_STATE; // Back to the IDLE_STATE after a nap - not enabling updates here as napping is typicallly disconnected
} break;
}
}
// Particle Function
bool setLowPowerMode(String command) // This is where we can put the device into low power mode if needed
{
if (command != "Yes" && command != "No") return false; // Before we begin, let's make sure we have a valid input
if (command == "Yes") { // Command calls for setting lowPowerMode
if (Particle.connected()) {
waitUntil(meterParticlePublish);
Particle.publish("Mode","Low Power", PRIVATE);
}
lowPowerMode = true;
}
else if (command == "No") { // Command calls for clearing lowPowerMode
if (Particle.connected()) {
waitUntil(meterParticlePublish);
Particle.publish("Mode","Normal Operations", PRIVATE);
}
lowPowerMode = false;
}
return true;
}
// Utility Functions
bool connectToParticle() { // Highly reliable way to get connected - and not block crucial functions while doing so
Cellular.on();
Particle.connect();
// wait for *up to* 5 minutes
for (int retry = 0; retry < 300 && !waitFor(Particle.connected,1000); retry++) {
// You can check on things here
Particle.process();
}
if (Particle.connected()) return true;
else return false; // return based on our connection status
}
bool disconnectFromParticle() // Ensures we disconnect cleanly from Particle
{
Particle.disconnect();
waitFor(notConnected, 15000); // make sure before turning off the cellular modem
Cellular.off();
delay(2000); // Bummer but only should happen once an hour
return true;
}
bool notConnected() { // Companion function for disconnectFromParticle
return !Particle.connected();
}
bool meterParticlePublish(void)
{
static unsigned long lastPublish=0; // Initialize and store value here
if(millis() - lastPublish >= 1000) { // Particle rate limits at 1 publish per second
lastPublish = millis();
return true;
}
else return false;
}
Something about calling the power management API is causing battery state of charge to not be updated.
Thanks,
Chip