Quick summary of my hardware: I am running an Electron which is run strictly off two solar panels. Charge current is limited due to the size of the panel and obviously changes throughout the day depending on the solar output.
There is a certain case in which I cannot get the Electron to restart: when the battery level is too low and the solar panels cannot provide enough current to the PMIC to charge the battery. What I think is happening is the battery reaches a level to start the Electron, but once the 3G radio turns on it sources current from the battery, drains it below the threshold quickly, and shuts off the MCU. It cycles through this over and over again and we can’t get anywhere unless the battery is charged externally.
To fix this problem, should I can use some FuelGauge functions?
I think the correct course of action would be to first set the threshold alert:
fuel.setAlertThreshold(25) //Interrupt when battery level is at 25%
Or maybe a LOWBATT alert? But I don’t know how to access that interrupt? I see it in /system/src/main.cpp but not sure how to call it…
if (fuel.getAlert() == "LOWBATT")
Clear the interrupt fuel.clearAlert(); and run interrupt routine in main code
while (fuel.getSoC() < 40) { //Wait and put device in sleep until battery level is at sufficient level to run smoothly, 40%?
System.sleep();
}
See any issues with this performing this general method? I haven’t tried to implement yet but would like some thoughts if I’m using the correct functions. I considered using the PMIC functions, but those seem less intuitive.
@fishmastaflex I’m testing a Electron with a small 3w solar panel and I was seeing the same issue as you where the Electron would lock up if the battery went dead overnight.
So I wrote some code to put the Electron to deep sleep when the battery SOC hit 20% and had the Electron wake up every hour to see if the SOC was above 20% via solar chargin yet and if it was it started sending data again and if not then it just went back to sleep for 1 hour again.
@RWB Thanks! I appreciate the response. I don’t know how I missed that post - guess I wasn’t checking the right keywords.
I have checked both versions of code and your’s is certainly…less advanced than @BDub 's code! I do like simple solutions though, so I will try your solution first.
Looks like you are using SEMI_AUTOMATIC mode in the first line of code, so that would basically not turn on the cellular until battery charge is > 20%
Yea, I just looked over Bdub’s code, and it’s very complex and not really needed for my application I think unless your powering some loads over and above just the Electron and even if you were you could just raise the minimum SOC level for operation. It’s working perfectly for me so far and gives you days for the sun to recharge the battery if you have a few days of dark cloudy weather.
Yes, the SemiAutomatic just keeps the Electron from trying to connect the Particle could before running your main loop which you do not want to happen when your just waking up to check the SOC levels to see if you need to go back to sleep or not.
Cellular.connect will turn on the modem and connect to the cellular network for you.
Cellular.ready just lets you know you’re attached to the mobile network and network access is ready.
Particle.connect will connect you to the Particle Cloud which I’m not using in this test, so I don’t do it because usually Particle connect takes up to 60 seconds which eats up battery. When I push data to Ubidots via MQTT the data send happens in like 1 to 2 seconds and then goes back to sleep.
@RWB A bit delayed here as I am just implementing now. I am now using SEMI_AUTOMATIC mode along with cellular.off() inside the setup() routine. Seems to be working fine. However, I want to be certain that the cellular module doesn’t try to boot up and draw current form the battery during startup.
Where is the best place to put cellular.off() if I don’t want it to start and still use SEMI_AUTOMATIC mode? Inside setup() or inside STARTUP() ?
@fishmastaflex The modem will not power up if the SOC is below 20% in the code I’m using.
Here is my latest code you can try out:
SYSTEM_MODE(SEMI_AUTOMATIC);
//SYSTEM_THREAD(ENABLED);
// This #include statement was automatically added by the Particle IDE.
#include "Ubidots/Ubidots.h"
#define TOKEN "UbidotsTokenGoesHere" // 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.
}
}
@RWB Ah, I see what you are doing there. You are just using SLEEP_MODE_DEEP to turn off the cell modem, as deep sleep function does it for you. You do this in the main loop though…
What I find interesting is that you don’t start your code with the Cellular.off() routine as @ScruffR mentioned to do in the post further above. So how are you so sure that the cell modem is off?
EDIT: adding my setup code just in case anyone sees any issues?
void setup() {
delay(50);
Serial.begin(115200); //Initialize serial line
Cellular.off();
//Check for low battery and go into deep sleep if below 20%
FuelGauge fuel;
if(fuel.getSoC() < 20) {
Serial.println("Battery < 20%, entering deep sleep for 60s.");
System.sleep(SLEEP_MODE_DEEP, 60); //May need to increase this time.
}
//Connect to the cellular network and wait until connected.
Cellular.on();
while(Cellular.ready() == false) {
Serial.println("Connecting to cellular network...");
delay(5000);
}
Serial.println("Connected to cellular network.");
//Connect to the Particle Cloud and wait until connected.
Particle.connect();
while(Particle.connected() == false) {
Serial.println("Connecting to Particle cloud...");
delay(5000);
}
Serial.println("Connected to Particle cloud.");
//Get time from Particle Cloud and set the timezone offset.
Particle.syncTime();
Time.zone(TZ_OFFSET);
Serial.println(Time.timeStr());
}
Manually turning off the cellular modem is only required if your running system threaded mode. It’s automatically done by the firmware if your not running in system threaded mode using the latest firmware based on Bdubs feedback.