Bug Bounty: Electron not booting after battery discharges completely

@BDub Your link is the button we are using. Below is a quick hand sketch of how we have things setup. There would be no difference if the battery was opened on the positive side I don’t think. The firmware was interrupted when pressing the power off button but there should not be anything harmful, our firmware is pretty simple. We did try removing the battery and powering up via USB. We also tried powering up via USB while holding the mode button. But nothing worked.

Thanks for the schematic! I agree there's no difference but just wanted to be sure you didn't have something else still connected to the battery-.

Since you have the solar panel there as well, disconnecting the battery does not fully power off the device. You would need a DPST type switch with two independent sets of switches to break the battery and solar connections (typically the positive side).

I'm not sure what your application is, but could you get away with using the System.sleep(SLEEP_MODE_SOFTPOWEROFF); command which should consume about 130uA. That way your solar panel can still charge your battery when it's "off".

Did you also remove the solar panel before applying USB power? I ask because I'm wondering if power was completely removed first. Also to avoid backfeeding the solar panel when USB is connected, you could add a diode in series with the solar panel on VIN.

Unfortunately I'm not seeing anything that should cause your issue. At what point in the boot process did you remove power? It sounded like you were turning it on and off a time or two. Was everything up and running before you removed power or was it a very quick on/off cycle? I've been wanting to run an automated test where I apply and remove power with an increasing on time, and fixed off time. It would be nice to focus on a particular point in the boot sequence though because that would be a really long test otherwise if the increment in time was very short.

@BDub Thanks for the catch on the diode :slight_smile: Right now we use the following to determine our sleep cycle:

if(fuel.getSoC() < 30)  
    System.sleep(SLEEP_MODE_DEEP, 21600);
System.sleep(BTN, FALLING, 1200, SLEEP_NETWORK_STANDBY);

Although now you have me curious what is the difference in terms of current draw between SLEEP_MODE_SOFTPOWEROFF and SLEEP_MODE_DEEP?

Also, I have some more info for you. When we last successfully powered on our unit it did exactly what it was supposed to do: it connected to the cell (UK) network and reported its data string. One of the data points we collect is the battery SoC. So I can tell you our battery was at a SoC of 63.18. The battery is the default one that comes with the dev kit.

So our failure went like this:
Battery SoC=63.18, solar connected but inside so unknown I/V (minimal)
Unit powered ON with button.
Unit fully functional, firmware fully executed.
Unit put into sleep mode by: System.sleep(BTN, FALLING, 1200, SLEEP_NETWORK_STANDBY);
Button turned to OFF
Case Opened
Button turned to ON
Blue light of death

Thanks for the detailed failure case. I'm still not seeing anything in that sequence that stands out as a problem. Do you have a JTAG tool such as the STLinkV2? You can use that to grab a backup of your flash memory which would be helpful to us for analysis. You can also send me your device, and I can grab the info and restore it for you. PM me for an address to send it to if you'd like to go this route.

The difference is that SLEEP_MODE_SOFTPOWEROFF puts the Fuel Gauge to sleep as well. There is about an additional 200uA saved by putting the Fuel Gauge to sleep. It's unclear in the datasheet about this, but if you put the Fuel Gauge to sleep I don't think it will keep track of the battery SoC while it's sleeping and the battery is charging. So you'll want to issue a quickStart() command and wait 200ms to ensure it has completed, before reading the SoC. This will reset the filtered SoC value of the Fuel Gauge and effectively catch it up to the current value. It's important when doing this to not have much load on the battery (so the modem should be off) or you can get an error in the reading.

1 Like

Hey all, is this for real? I think I’ve got (scratch that, temporarily had) a dead Electron here as well. Same symptoms anyway, I think.

  1. Battery fully discharged (measures 3.417V).
  2. Plugged in USB, no charge light, but left it that way for like 30 minutes.
  3. Disconnected battery and USB.
  4. Plugged in fully charged other Electron battery (one that comes with the kit).
  5. Still appeared to not be working.
  6. On a whim I disconnected and reconnected the fully charged battery.
  7. Started working normally (RGB LED behaviors).
  8. Plugged in the depleted battery again for the heck of it.
  9. Still working normally.
  10. Plugged in USB, and charge light turned on.

I haven’t a clue what state it was in where it wouldn’t charge, and frankly I don’t understand what experiential conditions resulted in it coming back to an operational state… I’m not sure this report is advancing the plot at all, but I thought it unhelpful to keep it to myself.

@vicatcu I’ve had that happen to me before also and after power cycling a few times it came back to life just fine.

It’s best to put the Electron to deep sleep well before the battery becomes completely empty, like 20-30% SOC. Then do not start running your normal code until it has been recharged up past the 20-30% SOC level to prevent the total power drop out that a dead battery provides.

@RWB so is something along the lines of https://github.com/technobly/electron-maintain-capacity/blob/master/firmware/electron-maintain-capacity.cpp required in every application? Kind of seems like it should be built into system firmware in that case, if not enabled by default, then opt-in / (or opt-out) through a method callable from setup()… just one guy’s opinion here, of course.

My application is (currently) always-on and connected to cellular. For this phase of development, I was satisfied with a concept of “just let the battery run down and go out to the field and recharge it when necessary”, e.g. 2 days of unsupervised operation is acceptable. As such, my firmware uses SYSTEM_MODE(AUTOMATIC). I also have SYSTEM_THREAD(ENABLED) so that my HTTPClient calls don’t block my loop. Now I’m thinking I have to modify my firmware to refactor into something like:

SYSTEM_MODE(MANUAL);
SYSTEM_THREAD(ENABLED);
const float capacity = 25.0; // i.e. %
...
void setup(){
  ...
  if(FuelGauge().getSoC() < capacity){
    System.sleep(SLEEP_MODE_SOFTPOWEROFF, 15*60*1000); // sleep for 15 minutes
    // as I understand it, in 15 minutes, the electron will 'wake up' 
    // into a reset condition and re-enter setup, where this check 
    // will occur again, and we won't get to connect unless charged
  }      
  Particle.connect();
}
void loop(){
  if (Particle.connected()){
    Particle.process();
  }
  ...
  if(FuelGauge().getSoC() < capacity){
    System.sleep(SLEEP_MODE_SOFTPOWEROFF, 15*60*1000); // sleep for 15 minutes
    // as I understand it, in 15 minutes, the electron will 'wake up' 
    // into a reset condition and re-enter setup
  }
}

… is that right? When I plug USB into my Electron and it’s in this sleep mode, will it charge the battery? There’s no evidence of this SLEEP_MODE_SOFTPOWEROFF mode in the docs yet… If I’m out of juice, I’m not concerned about retaining RAM, I really just want to shutdown until someone comes along and charges the electron, especially if not doing so has a chance of being harmful to the integrity of the firmware image or the battery.

Yes, it will charge when in sleep mode.

From what I heard Bdub say that sleep mode just turns off the Fuel Gauge.

If you do not have a solar panel connected then I do not think you will have the problems others have had since I think all of them had solar panels connected. I never killed any Electron even with having a solar panel connected and the battery going flat.

Just depends on what your doing. The Electron failing from low voltage does not happen to everybody or most people.

You can check out Bdub's code you linked to already which is more advanced than how I'm going about it. The code I'm testing to keep the Electron from draining empty with a solar panel attached is below if you want to check it out:

SYSTEM_MODE(SEMI_AUTOMATIC);
//SYSTEM_THREAD(ENABLED);
// This #include statement was automatically added by the Particle IDE.
#include "Ubidots/Ubidots.h"

#define TOKEN "Your Ubidots Token"  // Put here your Ubidots TOKEN
#define DATA_SOURCE_NAME "ElectronSleepNew"

SerialLogHandler logHandler(LOG_LEVEL_ALL);  //This serial prints system process via USB incase you need to debug any problems you may be having with the system.

Ubidots ubidots(TOKEN); // A data source with particle name will be created in your Ubidots account


int button = D0;         // Connect a Button to Pin D0 to Wake the Electron when in System Sleep mode. 
int ledPin = D7;         // LED connected to D1
int sleepInterval = 60;  // This is used below for sleep times and is equal to 60 seconds of time. 


void setup() {
 //Serial.begin(115200);
 pinMode(button, INPUT_PULLDOWN);  // Sets pin as input
 pinMode(ledPin, OUTPUT);          // Sets pin as output

 ubidots.setDatasourceName(DATA_SOURCE_NAME); //This name will automatically show up in Ubidots the first time you post data. 
 
 PMIC pmic; //Initalize the PMIC class so you can call the Power Management functions below. 
 pmic.setChargeCurrent(0,0,1,0,0,0); //Set charging current to 1024mA (512 + 512 offset)
 pmic.setInputVoltageLimit(4840);   //Set the lowest input voltage to 4.84 volts. This keeps my 5v solar panel from operating below 4.84 volts.  
}

void loop() {
    
FuelGauge fuel; // Initalize the Fuel Gauge so we can call the fuel gauge functions below. 
 
    
if(fuel.getSoC() > 20) // If the battery SOC is above 20% then we will turn on the modem and then send the sensor data. 
  {
   
   float value1 = fuel.getVCell();
   float value2 = fuel.getSoC();
   
  ubidots.add("Volts", value1);  // Change for your variable name
  ubidots.add("SOC", value2);    

  Cellular.connect();  // This command turns on the Cellular Modem and tells it to connect to the cellular network. 
  
   if (!waitFor(Cellular.ready, 600000)) { //If the cellular modem does not successfuly connect to the cellular network in 10 mins then go back to sleep via the sleep command below. After 5 mins of not successfuly connecting the modem will reset.  
    
    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.  
    
}  
  
     ubidots.sendAll(); // Send fuel gauge data to your Ubidots account. 

     digitalWrite(ledPin, HIGH);   // Sets the LED on
     delay(250);                   // waits for a second
     digitalWrite(ledPin, LOW);    // Sets the LED off
     delay(250);                   // waits for a second
     digitalWrite(ledPin, HIGH);   // Sets the LED on
     delay(250);                   // waits for a second
     digitalWrite(ledPin, LOW);    // Sets the LED off
  
     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.  
    
  }
  else //If the battery SOC is below 20% then we will flash the LED 4 times so we know. Then put the device into deep sleep for 1 hour and check SOC again. 
  {
      
  //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. 
  
  }
}    
1 Like

@RWB thanks, some of your comments there are gems… in order to shut down cellular with SYSTEM_THREAD(ENABLED) those lines are still needed? Is there some analogy / workaround associated with turning cellular back on?

Correct :smiley:

Cellular.connect(); 

Turns the cellular modem back on automatically and attempts to connect to the cellular network.

Just checking back here, given that I’m not using solar panels, should I or should I not implement this 20% SoC shutdown strategy (is it necessary / what are the repercussions of not implementing it)? I wouldn’t want to give up 20% of of my operational time without a good reason.

I think I actually did this yesterday too. Electrons will turn on if theres enough charge and no battery. Could point to a brownout issue.

If your not solar charging then you could set the cutoff to 10% SOC and then extend the deep sleep time to days instead of waking up every hour to check the SOC level. This should prevent the Electron from ever getting into the low power state that seems to be causing some Electrons to fail.

If you’re a gambling man, then you could just take your chances and not use the code at all, but it’s possible that you may run into a dead Electron one of the times the battery shuts off from low voltage.

If you ever charge via Solar, then this is highly recommended.

@RWB one other quick clarification. Surely these lines are not needed / relevant to SYSTEM_THREAD(ENABLED), right? That would be some voodoo if that mattered.

  //delay(2000);
  //FuelGauge().sleep();
  //delay(2000);

… and do you actually have to call Cellular.on and wait 10 seconds before issuing the AT command to shut down? Cra. Where does this come from, can I read more in another thread you can ref?

@vicatcu

No, those lines of code are not required when using System_Thread(enabled);

They are used to put the fuel gauge to sleep which will save you 130uA but will cause your SOC readings to jump when you wake up so I just leave the fuel gauge ON in my applications to get steady fuel gauge SOC reading when waking up to check SOC readings every hour.

See this thread below for a explanation on the 10 second delay when turning off the cellular modem beofre sleeping.

https://github.com/spark/firmware/issues/1176

SLEEP_MODE_SOFTPOWEROFF turns off the Fuel Gauge in addition to what SLEEP_MODE_DEEP does in turning off the cellular modem and putting the MCU in standby mode (aka lowest power total, next to removing the battery completely and running the RTC on VBAT only that is).

I haven't done a long study of multiple units on this yet, but the Fuel Gauge left on seemed to draw about 200uA for me.

1 Like

I think I'm having some issue with the charging rate defaulting to 50mA when in SLEEP_MODE_DEEP.

Can you be more specific on this particular issue, which system mode it might occur in, which sleep mode, and which firmware version fixes it?

Thanks!

@fishmastaflex I can tell you that with the latest 0.6.1_rc.1 firmware along with using this code in Setup so it runs every time the Electron comes out of these 2 sleep modes: System.sleep(D0, RISING,sleepInterval * 2, SLEEP_NETWORK_STANDBY); & System.sleep(SLEEP_MODE_DEEP, 3600);

PMIC pmic; //Initalize the PMIC class so you can call the Power Management functions below. 
pmic.setChargeCurrent(0,0,1,0,0,0); //Set charging current to 1024mA (512 + 512 offset) 

I’m seeing much better charging rates doing this.

@RWB, do you mean 0.6.1-rc.1?

Yes, your right, its:

I’ll edit my previous post for clarity :smiley: