I am currently doing a battery life test on the electron to see how long it can last if I put the device into sleep mode for 24 hours, have it turn on, sends one voltage reading and then go to sleep again. The device is not in sleep mode for an average of only 2mins, in a period of 24hrs. With the 2200mAh battery, the Electron battery is only lasting 2 days. In one day, the battery dropped from 3.9 to 3.6V. The power saving features I have implemented include:
Sleep mode for ~24hrs
Turned off RGB LED
I have attached my code below. I read the Electron datasheet to calculate battery life on paper and with a ~24hr sleep mode, the battery should be lasting 2 weeks but that is not the case at all. Is there something in particular in the code which is causing the consumption of so much power or is the Electron not that power efficient? There is currently no hardware connected to the electron apart for the battery.
BTW, that ystem.sleep(1000000); would be more than 11.5 days, but with any Cellular.on() command you “wake” the modem again, since your code keeps running in that “sleep” mode.
So the Cellular.on is only executed at 8 30am or when the battery is low. After that, I put the device into System.sleep. Since the system.sleep is in the void loop and it does not stop the execution of code, as long as the timer for system.sleep is greater than the time it takes for the loop to fully execute, should it matter if I set System.sleep to 86400 seconds as compared to 1000 seconds? From my understanding, wouldn’t the device remain in sleep unless told otherwise?
Also, @ScruffR I was looking through the documentation for the sleep function and I could not identify which sleep function I should be calling instead. I still want the code to continue to execute while sleeping so if there is another sleep function I should be calling to save battery, please let me know
I was seeing a 5+ day battery run time by waking up every 5 mins reporting battery SOC and Voltage to Ubidots.
I would use this code to sleep: System.sleep(D0, RISING, sleepInterval * 60, SLEEP_NETWORK_STANDBY);
There are other sleep options but if you just used that sleep mode and sleep every 24 hours you should be seeing weeks of battery life.
Here is the code I was using to push the battery data to Ubidots:
// This #include statement was automatically added by the Particle IDE.
#include "Ubidots/Ubidots.h"
#define TOKEN "YourTokenHere" // Put here your Ubidots TOKEN
Ubidots ubidots(TOKEN); // A data source with particle name will be created in your Ubidots account
int button = D0;
int ledPin = D7; // LED connected to D1
int sleepInterval = 60;
void setup(){
pinMode(button, INPUT_PULLDOWN); // sets pin as input
pinMode(ledPin, OUTPUT); // sets pin as output
//Serial.begin(115200);
ubidots.setDatasourceName("PinWakeTestCode2"); // Uncomment this line to change the data source Name.
}
void loop(){
FuelGauge fuel;
float value1 = fuel.getVCell();
float value2 = fuel.getSoC();
ubidots.add("Volts", value1); // Change for your variable name
ubidots.add("SOC", value2);
ubidots.sendAll();
digitalWrite(ledPin, HIGH); // sets the LED on
delay(500); // waits for a second
digitalWrite(ledPin, LOW); // sets the LED off
delay(500); // waits for a second
digitalWrite(ledPin, HIGH); // sets the LED on
delay(500); // waits for a second
digitalWrite(ledPin, LOW); // sets the LED off
delay(500); // waits for a second
digitalWrite(ledPin, HIGH); // sets the LED on
delay(500); // waits for a second
digitalWrite(ledPin, LOW); // sets the LED off
delay(500); // waits for a second
System.sleep(D0, RISING, sleepInterval * 60, SLEEP_NETWORK_STANDBY);
}
As said, the the device is not actually sleeping when it still executes code - and for what you seem to intend with your code doesn't need to run the code permanently either.
That's what the Stop Mode sleep (as suggested by RWB) would be for.
If you only executed that code every 15 minutes, what would you've missed that you needed to know?
You may want your code to be running permanently, but you also want your battery to last, so a compromise has to be found.
BTW, you should check Particle.connected() before doing anything that requires a connection (e.g. Particle.publish()).
You can also drop Cellular.connect() and Cellular.ready() if you are only interested in the cloud connection, since that implicitly requires the cellular connection anyway.
Also there's no need to call Particle.connect() twice in a row with delays (where 10sec is usually not enough for a cold connect anyway) between, this is just enough
Cellular.on();
Particle.connect();
waitUntil(Particle.connected); // use waitFor() if you want a timeout.
So right now I am just doing a battery life test on the electron. My end goal is that there will be 3-4 sensors connected to the electron and although the device will still be in sleep mode, I would like it to continue to collect readings and store them in an array. But you’re right that it won’t be collecting readings all the time so the Stop Mode sleep would work better. I have modified my code to use that sleep function and am testing out the battery life with that function. The device is sleeping for 40mins, wakes up, sends a couple of readings and goes back to sleep again. I changed the frequency from 24hr to 40mins to see if it at least at least @ScruffR and @RWB’s battery results.
Also, @ScruffR, I tried using waitUntil((Particle.connected); and remove the Cellular.connect() and Cellular.ready(). Unfortunately, since the Electron is disconnected from the Particle cloud for more than 23mins, it doesn’t connect to the Particle Cloud and send a reading to the console. So I had to put them back in. Here is the revised code.
If you wake up every 23 mins, then you will only use a 122-136 byte ping to keep the Particle Cloud connection alive. Everytime you wake up, and it’s been over 23 mins since your last Particle communication there is a cloud handshake that happens, and that cost you approx 4400 bytes of data every time.
So it may be better data wise to wake up every 20 mins vs. every 40 mins if you’re conscious about cellular data consumption.
I’m short on time, but I’m sure you can simplify your code some also and still accomplish the same thing.
Right now I am just pushing this to a google spreadsheet using IFTTT to keep track of values. Later on I might use a service like Ubidots. I am currently working on getting the sleep functionality working correctly to get it from a battery life of only 2 days to be 10+ days with publishing data every 40mins. I am going to concurrently focus on simplifying the code as well. I am assuming by simplifying it, the improvement in battery life would be incremental by maximum a few days and not an exponential increment. Correct me if I’m wrong.
If you push to Ubidots, then you can just have an email or text message trigger when the battery SOC or Voltage reaches any certain setpoint, and then you can remove that bit from your code.
You can see my Ubidots code in a previous post above if your interested in how I was getting the five-day run times.
My question for you is that you used System.sleep(D0, RISING, sleepInterval * 60, SLEEP_NETWORK_STANDBY); which apparently uses more battery than System.sleep(D0, RISING, sleepInterval * 60)'. Is there a reason you went for SLEEP_NETWORK_STANDBY? I got this from here.
The Electron maintains the cellular connection for the duration of the sleep when SLEEP_NETWORK_STANDBY is given as the last parameter value. On wakeup, the device is able to reconnect to the cloud much quicker, at the expense of increased power consumption.
That code was from when the Electron first came out.
Use BDubs latest recommended sleep code here and change the sleep time to what ever you want, the time is in seconds.
This assumes your using the latest 0.6.0_rc1 or 2 firmware which would give you the data savings if waking up within 23 mins of each data publish or ping. . :
That should not be the case, but after more than 23min a reconnect will take considerably longer and consume more data (and battery) than with regular wakes.
That might also answer your question about SLEEP_NETWORK_STANDBY. You may need more power while sleeping, but save more than that when waking up and being quicker in getting connected and going back to sleep.
But that's again a matter of finding the sweet spot between the two ways. The longer you sleep the sleeping loss increases while the reconnect gain decreases, to the point where you need to change strategy.
And if you are eventually completely going for a 3rd party service like ubidots, you may not even need Particle.connect() and the keep alive pings - Cellular.connect() might be enough.
This could help cutting battery and data comsumption again.
Back again! So I updated my code to use Deep Sleep instead of stop mode sleep and have been tracking battery usage over time. What I am seeing however is that my battery lifetime is still the same as the code I used for system.sleep. You can see my code below.
Question I have is will my battery life remain unaffected whether I use stop mode sleep or deep sleep because the largest battery happens whenever the Cellular module turns on? Or am I not using Deep Sleep in my code correctly? Any feedback would be appreciated.
// This #include statement was automatically added by the Particle IDE.
#include "Ubidots/Ubidots.h"
#define TOKEN "5owEJqJtPeiJrHPmxYkoAsrotVjsPp" // Put here your Ubidots TOKEN
#include "spark_wiring_power.h"
Ubidots ubidots(TOKEN);
//Declaring Global Variables
int status;
String message;
int sleepInterval = 720; //Specify minutes between each reading sent. 40mins for TH and 12hrs for EM
int initial_seconds;
float battery;
int hour;
int minute;
//int initial_seconds;
int hour1 = 0; //specifies which hour to send first reading at - 12am
int hour2 = 12; //specifies which hour to send second reading at - 12pm
PMIC pmic;
int batt_status;
float fuel()//checks battery voltage value
{
FuelGauge fuel;
message = String(fuel.getVCell()) + "V";
return fuel.getVCell();
}
void connect_status()//Send readings to Particle
{
waitUntil(Particle.connected);
while(status < 3)//send three readings
{
if(Cellular.ready() && Particle.connected() == true)
{
//delay(10000);
delay(500);
battery = fuel();
if(Particle.publish("Elec_Batt_Values", message) == true)
{
//delay(10000);
delay(500);
status++;
ubidots.add("EM-DeepSleep-Voltage", battery); // Change for your variable name
ubidots.sendAll();
delay(500);
//delay(5000);
}
else if(Particle.publish("Elec_Batt_Values", message) == false)
{
Cellular.on();
Cellular.connect();
delay(20000);
Particle.connect();
status= status-1;
delay(20000);
}
}
else
{
Cellular.on();
Cellular.connect();
delay(10000);
Particle.connect();
status=0;
delay(10000);
}
}
}
void setup()
{
Time.zone(-5);
RGB.control(false);
}
void loop() {
if(fuel() < 3.65)
{
pmic.enableCharging();
batt_status = 1;
}
else if (fuel() > 3.85)
{
pmic.disableCharging();
batt_status = 0;
}
status=0;
connect_status();
if(Time.hour() < hour2 && Time.hour() >= hour1) //if the hour is less than the second hour but greater than the first hour
{
hour = abs(hour2 - Time.hour()-1);
minute = abs(60 - Time.minute());
}
else if (Time.hour()>=hour2 && Time.hour() <= 23) //if the hour is greater than the second hour but less than midnight
{
hour = abs(23 - Time.hour() + hour1);
minute = abs(60 - Time.minute());
}
else if (Time.hour()>=0 && Time.hour()<hour1)
{
hour = hour1 - 1;
minute = abs(60 - Time.minute());
}
//message = String(hour) + "h " + String(minute)+"m";
//Particle.publish("Elec_Hour", message);
initial_seconds = hour*60*60 + 60*minute; //Regardless of when device is turned on, the second reading will always be at hour1 or hour2 (whichever is closer)
//delay(10000);
System.sleep(SLEEP_MODE_DEEP, initial_seconds);
}
The logic in this function is somewhat obscure, if I may say so
After waitUntil(Particle.connected) there is no way for the following if() to not be executed, so the else branch is superfluous.
Next I'm not sure what that if(Particle.publish()) ... else(Particle.publish()) combo should do.
What is if your first publish fails but the second suceedes? You'll have at least three publishes in 500ms. Is this intended?
I'm also not sure why you disable charging before the battery is full when you on the other hand want maximum battery life
I guess there is some more "technical" way to actually calculate the sleep time if you can explain what exactly you intend there.
Just to show my code that does run for 10 days (on a fully charged battery) with reading two temp sensors and publishing to ubidots every 15min
SYSTEM_MODE(MANUAL)
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY))
// Dashoard: https://app.ubidots.com/ubi/public/getdashboard/page/Ked8IDJliKBciQci4MuRh865ACA
#include "DS18B20/Particle-OneWire.h"
#include "DS18B20/DS18B20.h"
#include "PietteTech_DHT/PietteTech_DHT.h"
#include "Ubidots/Ubidots.h"
#define TOKEN "Put here your Ubidots TOKEN"
#define DATA_SOURCE_NAME "Particle"
#define DATA_SOURCE_TAG "Temps"
#define DHTTYPE DHT22 // Sensor type DHT11/21/22/AM2301/AM2302
#define DHTPIN A1 // Digital pin for communications
const uint32_t PUBLISH_MINUTES = 15;
const uint32_t SAMPLE_INTERVAL = 10000;
const uint32_t PUBLISH_INTERVAL = PUBLISH_MINUTES + 60000;
const int led = D7;
void dht_wrapper(); // must be declared before the lib initialization
PietteTech_DHT DHT(DHTPIN, DHTTYPE, dht_wrapper);
DS18B20 ds18b20(A0);
Ubidots ubidots(TOKEN);
FuelGauge fuel;
char szInfo[64];
retained double tempWater = 0;
retained double tempAir = 0;
retained double relHumidity = 0;
retained double dewPoint = 0;
retained double dewPointSlow = 0;
retained double SoC = 0;
retained int lastDay = 0;
void setup()
{
#if (PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION)
Cellular.on();
Cellular.connect();
#else
WiFi.on();
WiFi.connect();
#endif
ubidots.setDatasourceName(DATA_SOURCE_NAME);
ubidots.setDatasourceTag(DATA_SOURCE_TAG);
}
void loop()
{
if (Time.day() != lastDay)
{ // a new day calls for a sync
Particle.connect();
if(waitFor(Particle.connected, 5*60000))
{
Particle.syncTime();
for(uint32_t ms=millis(); millis()-ms < 1000; Particle.process());
Particle.disconnect();
lastDay = Time.day();
}
}
getTemp();
publishData();
}
void publishData()
{
if (!ds18b20.crcCheck()) return;
ubidots.add("WaterTemp" , tempWater );
ubidots.add("AirTemp" , tempAir );
ubidots.add("RelHumidity", relHumidity);
ubidots.add("DewPoint" , dewPoint );
ubidots.add("SoC" , SoC );
ubidots.sendAll();
sprintf(szInfo, "Water %.1f °C, Air %.1f °C, Humidity %.1f% %, DewPoint %.1f (%.1f) °C, SoC: %.1f% %", tempWater, tempAir, relHumidity, dewPoint, dewPointSlow, SoC);
Serial.println(szInfo);
int dt = (PUBLISH_MINUTES - Time.minute() % PUBLISH_MINUTES) * 60 - Time.second(); // wake at next time boundary
//System.sleep(SLEEP_MODE_DEEP, dt, SLEEP_NETWORK_STANDBY);
System.sleep(BTN, FALLING, dt, SLEEP_NETWORK_STANDBY); // use SETUP BUTTON (20) to wake
}
void getTemp()
{
if(!ds18b20.search())
{
int dsAttempts = 0;
double _tempWater;
ds18b20.resetsearch();
_tempWater = ds18b20.getTemperature();
while (!ds18b20.crcCheck() && dsAttempts < 4)
{
Serial.printf("Bad value (%d)", dsAttempts++);
if (dsAttempts == 3)
delay(1000);
ds18b20.resetsearch();
_tempWater = ds18b20.getTemperature();
}
Serial.println(_tempWater);
if(dsAttempts < 4)
{ // we have a valid reading
tempWater = (_tempWater + tempWater) / (tempWater ? 2.0 : 1.0);
}
}
switch (DHT.acquireAndWait())
{
case DHTLIB_OK:
Serial.println("DHT OK");
relHumidity = (DHT.getHumidity() + relHumidity ) / (relHumidity ? 2.0 : 1.0);
tempAir = (DHT.getCelsius() + tempAir ) / (tempAir ? 2.0 : 1.0);
dewPoint = (DHT.getDewPoint() + dewPoint ) / (dewPoint ? 2.0 : 1.0);
dewPointSlow = (DHT.getDewPointSlow() + dewPointSlow) / (dewPointSlow ? 2.0 : 1.0);
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.println("Error\n\r\tChecksum error");
break;
case DHTLIB_ERROR_ISR_TIMEOUT:
Serial.println("Error\n\r\tISR time out error");
break;
case DHTLIB_ERROR_RESPONSE_TIMEOUT:
Serial.println("Error\n\r\tResponse time out error");
break;
case DHTLIB_ERROR_DATA_TIMEOUT:
Serial.println("Error\n\r\tData time out error");
break;
case DHTLIB_ERROR_ACQUIRING:
Serial.println("Error\n\r\tAcquiring");
break;
case DHTLIB_ERROR_DELTA:
Serial.println("Error\n\r\tDelta time to small");
break;
case DHTLIB_ERROR_NOTSTARTED:
Serial.println("Error\n\r\tNot started");
break;
default:
Serial.println("Unknown error");
break;
}
SoC = fuel.getSoC();
}
void dht_wrapper()
{
DHT.isrCallback();
}
A lot of the logic which may seem redundant is there to ensure the device does connect. The reason for this is the device we are creating this code for will be shipped to a customer site one day and we want to ensure the device connects and publishes data on the Particle console. So yes, I agree a lot of the code does seem redundant but it is mostly there as a catch all situation.
I was disabling charging because I wanted to test whether a power bank connected to the Electron would power on when charging is enabled (from being originally disabled) but the tests I have conducted so far show it cannot. The power bank is a portable battery (similar to one here). These power banks turn off if they don’t detect a certain amount of current draw.
For sleep time, I have been working to ensure regardless of what time the device turns on, it always sends readings every 12 hours at hours specified by the user.
The device sends 3 readings to the particle console during which the device is powered on for less than a minute and goes back to Deep sleep. Why the battery is still not lasting long is a little confusing.
Oh and without the disableCharging function in the code, the battery is charged by the Electron to a voltage of 3.93 to ~4.0V. So when I run my test, the battery level is at around 3.93-4.0V.
What code do you mean?
I haven’t got anything other than the code that’s in the post above (although since then the includes have slightly changed, as has the Ubidots library syntax).