Appreciate the feedback regarding locked up Boron and related terminology. The lock-up happens by unplugging the USB during the delay. I have a power switch built in so it is easy enough to recycle but that’s not ideal.
I am also happy to share my firmware since so many techniques where adapted from the forum. Note that short of using the Adafruit TPL5111 the code below is demonstrating my lowest in-line sleep power consumption thus far. Suggestions for further improvements related to power reduction (or anything for that matter) are welcome!
#include "Particle.h"
#include <DS18B20.h>
#include <math.h>
#include <DiagnosticsHelperRK.h>
// DS18B20 set up
const int MAXRETRY = 4; // retry limit
DS18B20 ds18b20(D4, true); //Sets Pin D4 for Temp Sensor, D3 Low, D5 High
// LoopDelayDefault constant
const int LOOPDEFAULT = 3540000; // roughly 1 hour
String fstr;
String myID;
String firmware;
String vardata;
String sensordata;
double celsius;
double fahrenheit;
int loopcount;
bool isChargingEnabled;
int delaymode;
int loopdelay;
// Function prototypes
// Could be Particle functions
int chargeStatus(String arg1);
int powerSource(String arg1);
int chargEnabled(String arg1);
// Run application on its own thread
SYSTEM_THREAD(ENABLED);
// System setup
SYSTEM_MODE(SEMI_AUTOMATIC);
// Logging
SerialLogHandler logHandler(LOG_LEVEL_ALL);
// Disable battery charging
PMIC power;
// Battery
FuelGauge fuel;
// Cellular signal;
CellularSignal sig;
// Visual indicator that we are publishing
const int BLUELED = D7;
/*******************************************************************************
* Function Name : setup
* Description : None.
*******************************************************************************/
void setup() {
// Initialize vars
fstr = "";
myID = "";
firmware = "CloudAppV104";
sensordata = "";
vardata = "";
celsius;
fahrenheit;
isChargingEnabled = true;
delaymode = 0;
loopdelay = LOOPDEFAULT;
EEPROM.get(10, loopcount);
if(loopcount == 0xFFFFFFFF) {
// EEPROM was empty -> initialize value
loopcount = 0;
}
// Delay for serial
Serial.begin();
delay(5000);
// Set voltage target objective
power.setChargeVoltage(4208); // default was 4112
// Set needed pins
pinMode(BLUELED, OUTPUT);
pinMode(D3, OUTPUT);
pinMode(D5, OUTPUT);
// Onboard sensor (not configured as an array or bus)
digitalWrite(D3, LOW); // ground
digitalWrite(D5, HIGH); // voltage
// Connect to the cloud if not already
if (!Particle.connected()) {
Particle.connect();
delay(15000);
}
// Log Messages
Log.info("System version: %s", (const char*)System.version());
Log.info("System deviceID: %s", (const char*)System.deviceID());
// End of setup
Log.info("Setup end");
}
/*******************************************************************************
* Function Name : loop
* Description : None.
*******************************************************************************/
void loop() {
// Loop start
Log.info("Loop start");
// Are we connected?
if (Particle.connected()) {
Log.info("Already connected to the cloud");
} else {
Log.info("Connecting to the cloud");
Particle.connect();
delay(15000);
}
// Cellular signal
sig = Cellular.RSSI();
// Loopcount
loopcount++;
// Signed int - lets not overflow it
if (loopcount == 2147483647) loopcount = 0;
EEPROM.put(10, loopcount);
// Get the temperatures
getTemp();
// Adjust for testing
fahrenheit = fahrenheit + 0;
// Format the temperature
fstr = String::format("%04d",int(fahrenheit));
// If USB is connected then there is no point in disabling cellular, just
// use simple delay mechanism
if (powerSource("x") < 5)
{
if (delaymode==0) {
// if we were on delaymode 0 (battery) but are
// now plugged in, change to 1 (USB).
delaymode=1;
}
}
// If we are back on battery then flip back
// to the most efficient mode.
else delaymode = 0;
// Allow battery charging?
if ((celsius < 0) || (powerSource("x") > 4)) {
power.disableCharging();
isChargingEnabled=false;
} else {
power.enableCharging();
isChargingEnabled=true;
}
// START PUBLISHING
// START PUBLISHING
digitalWrite(BLUELED, HIGH);
vardata = String::format("F=%s L=%d D=%d M=%d P=%d E=%d C=%d",(const char*)firmware,loopcount,loopdelay,delaymode,powerSource("x"),isChargingEnabled,chargeStatus("x"));
sensordata = String::format("V1|%2.2f|%f|V=%f|S=%.02f%%|Q=%.02f%%|%s",celsius,fuel.getSoC(),fuel.getVCell(),sig.getStrength(),sig.getQuality(),(const char*)vardata);
// Trigger the webhook
if (Particle.connected()) {
Particle.publish("sensordata", sensordata, PRIVATE, NO_ACK);
// Write it to the log
Log.info(sensordata);
}
delay(5000);
digitalWrite(BLUELED, LOW);
// END PUBLISHING
// END PUBLISHING
// For testing
// delaymode = 0;
// delaymode 1 is the default when USB
// is connected. delaymode 0 when running on battery.
if (delaymode==1) {
// USB
Log.info("Delay (2 minutes) at %d past the hour", Time.minute());
delay(115000);
} else {
// Battery (delaymode 0)
// 02mAh with no pulse - BSF WITHOUT TPL5111
// ~1%/day on 6600mAh Li-ion battery
// how can we get this down even lower??
Log.info("Sleep with %d seconds at %d past the hour", (loopdelay/1000), Time.minute());
Cellular.off();
delay(5000);
System.sleep({}, {}, (loopdelay/1000));
Cellular.on();
Cellular.connect();
delay(5000);
}
// Loop end
Log.info("Loop end");
}
/*******************************************************************************
* Function Name : getTemp
* Description : None.
*******************************************************************************/
void getTemp() {
// Only fire up the probe when needed
digitalWrite(D5, HIGH);
delay(1000);
float _temp;
int i = 0;
do { _temp = ds18b20.getTemperature(); }
while (!ds18b20.crcCheck() && MAXRETRY > i++);
if (i < MAXRETRY) {
celsius = _temp;
fahrenheit = ds18b20.convertToFahrenheit(_temp);
} else {
celsius = fahrenheit = -99;
}
// Turn the probe off
digitalWrite(D5, LOW); // voltage
}
/*******************************************************************************
* Function Name : chargeStatus
* Description : is battery charging
*******************************************************************************/
int chargeStatus(String arg1) {
Log.info("Charging status requested: %d", (((power.getSystemStatus() >> 4) & 0x3)==2));
return (((power.getSystemStatus() >> 4) & 0x3)==2);
}
/*******************************************************************************
* Function Name : powerSource
* Description : what is our power source?
*******************************************************************************/
int powerSource(String arg1) {
// POWER_SOURCE_UNKNOWN = 0
// POWER_SOURCE_VIN = 1
// POWER_SOURCE_USB_HOST = 2
// POWER_SOURCE_USB_ADAPTER = 3
// POWER_SOURCE_USB_OTG = 4
// POWER_SOURCE_BATTERY = 5
int pwr = DiagnosticsHelper::getValue(DIAG_ID_SYSTEM_POWER_SOURCE);
Log.info("Power source is: %d", pwr);
return pwr;
}
/*******************************************************************************
* Function Name : chargEnabled
* Description : Is charger enabled
*******************************************************************************/
int chargEnabled(String arg1) {
Log.info("Is charging enabled requested: %d", isChargingEnabled);
return (int) isChargingEnabled;
}