I am wondering how I can use Ultra Low Power sleeping on a Boron unit while allowing the unit to wake up from its sleep cycle if I need to flash new code to the unit. I have a unit flashed with the code (listed below) and it works except that once this code is flashed to the Boron, whenever I try to flash new code to it via the Cloud, the flash starts, fails and responds with unresponsive. The unit then carries on running the code, but I cannot get it to accept a new flash.
Thank you to anyone taking the time to look at this.
// This #include statement was automatically added by the Particle IDE.
#include "SparkFun_TMP117_Registers.h"
// This #include statement was automatically added by the Particle IDE.
#include "SparkFun_TMP117.h"
// Used to establish serial communication on the I2C bus
#include <Wire.h>
// This #include statement was automatically added by the Particle IDE.
#include "OneWire.h"
// This #include statement was automatically added by the Particle IDE.
#include "DS18.h"
// Program code starts here
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
// This is the maximum amount of time to wait for the cloud to be connected in
// milliseconds. This should be at least 5 minutes. If you set this limit shorter,
// on Gen 2 devices the modem may not get power cycled which may help with reconnection.
const std::chrono::milliseconds connectMaxTime = 6min;
// This is the minimum amount of time to stay connected to the cloud. You can set this
// to zero and the device will sleep as fast as possible, however you may not get
// firmware updates and device diagnostics won't go out all of the time. Setting this
// to 10 seconds is typically a good value to use for getting updates.
const std::chrono::milliseconds cloudMinTime = 10s;
// How long to sleep
const std::chrono::milliseconds sleepTime = 10min;
// Maximum time to wait for publish to complete. It normally takes 20 seconds for Particle.publish
// to succeed or time out, but if cellular needs to reconnect, it could take longer, typically
// 80 seconds. This timeout should be longer than that and is just a safety net in case something
// goes wrong.
const std::chrono::milliseconds publishMaxTime = 3min;
// Maximum amount of time to wait for a user firmware download in milliseconds
// before giving up and just going back to sleep
const std::chrono::milliseconds firmwareUpdateMaxTime = 5min;
// These are the states in the finite state machine, handled in loop()
enum State {
STATE_WAIT_CONNECTED = 0,
STATE_PUBLISH,
STATE_PRE_SLEEP,
STATE_SLEEP,
STATE_FIRMWARE_UPDATE
};
State state = STATE_WAIT_CONNECTED;
unsigned long stateTime;
bool firmwareUpdateInProgress = false;
// TMP117 Setup
// The default address of the device is 0x48 = (GND)
TMP117 water_sensor; // Initalize sensor
// DS18B20 Setup
DS18 air_sensor(D8);
void readSensorAndPublish(); // forward declaration
void firmwareUpdateHandler(system_event_t event, int param); // forward declaration
void turnOnCharging(); // forward declaration
// setup() runs once, when the device is first turned on.
void setup() {
System.on(firmware_update, firmwareUpdateHandler);
// It's only necessary to turn cellular on and connect to the cloud. Stepping up
// one layer at a time with Cellular.connect() and wait for Cellular.ready() can
// be done but there's little advantage to doing so.
Cellular.on();
Particle.connect();
stateTime = millis();
// In some cases errant settings or misconfigurations can cause undesired behavior.
// The single line of code below will reset the PMIC to its default values.
System.setPowerConfiguration(SystemPowerConfiguration());
// The following code applies a custom power configuration to the PMIC
SystemPowerConfiguration powerConf;
powerConf.powerSourceMaxCurrent(900)
.powerSourceMinVoltage(5080)
.batteryChargeCurrent(900)
.batteryChargeVoltage(4210);
System.setPowerConfiguration(powerConf);
// Ensure that charging is enabled on the PMIC
turnOnCharging();
Wire.setSpeed(400000); // Set clock speed to be the fastest for better communication (fast mode)
Wire.begin();
water_sensor.begin();
}
void loop() {
switch(state) {
case STATE_WAIT_CONNECTED:
// Wait for the connection to the Particle cloud to complete
if (Particle.connected()) {
state = STATE_PUBLISH;
stateTime = millis();
}
else
if (millis() - stateTime >= connectMaxTime.count()) {
state = STATE_SLEEP;
}
break;
case STATE_PUBLISH:
readSensorAndPublish();
if (millis() - stateTime < cloudMinTime.count()) {
state = STATE_PRE_SLEEP;
}
else {
state = STATE_SLEEP;
}
break;
case STATE_PRE_SLEEP:
// This delay is used to make sure firmware updates can start and diagnostics go out
// It can be eliminated by setting cloudMinTime to 0 and sleep will occur as quickly
// as possible.
if (millis() - stateTime >= cloudMinTime.count()) {
state = STATE_SLEEP;
}
break;
case STATE_SLEEP:
if (firmwareUpdateInProgress) {
state = STATE_FIRMWARE_UPDATE;
stateTime = millis();
break;
}
{
SystemSleepConfiguration config;
// Gen 3 (nRF52840) does not suppport HIBERNATE with a time duration
// to wake up. This code uses ULP sleep instead.
config.mode(SystemSleepMode::ULTRA_LOW_POWER)
.duration(sleepTime)
.network(NETWORK_INTERFACE_CELLULAR);
System.sleep(config);
// One difference is that ULP continues execution. For simplicity,
// we just match the HIBERNATE behavior by resetting here.
System.reset();
}
break;
case STATE_FIRMWARE_UPDATE:
if (!firmwareUpdateInProgress) {
state = STATE_SLEEP;
}
else
if (millis() - stateTime >= firmwareUpdateMaxTime.count()) {
state = STATE_SLEEP;
}
break;
}
}
void readSensorAndPublish() {
String air_temperature = "Air Temperature not read";
String water_temperature = "Water Temperature not read";
String battery_charge = "Battery not read";
// water_sensor.dataReady() is a function to make sure that there is data ready
// only reads and publishes water temperature when data from TMP117 is ready
if (water_sensor.dataReady() == true)
{
// Read the water temperature in degrees C
float tmp117_reading = water_sensor.readTempC();
// Convert temperature reading to a string with 2 decimal places
water_temperature = String(tmp117_reading , 2);
// Trigger the webhook integration
Particle.publish("tmp117_water_temperature", water_temperature, PRIVATE | WITH_ACK);
}
// 1Wire Air Temperature reading
if (air_sensor.read()) {
// Read the air temperature in degrees C
float ds18b20_reading = air_sensor.celsius();
// Convert temperature reading to a string with 2 decimal places
air_temperature = String(ds18b20_reading, 2);
// Trigger the webhook integration
Particle.publish("ds18b20_air_temperature", air_temperature, PRIVATE | WITH_ACK);
}
// Read the battery charge status
float battery_soc = System.batteryCharge();
// Convert battery charge reading to a string with 0 decimal places
battery_charge = String(battery_soc, 0);
// Trigger the webhook integration
Particle.publish("battery_charge", battery_charge, PRIVATE | WITH_ACK);
}
void firmwareUpdateHandler(system_event_t event, int param) {
switch(param) {
case firmware_update_begin:
firmwareUpdateInProgress = true;
break;
case firmware_update_complete:
case firmware_update_failed:
firmwareUpdateInProgress = false;
break;
}
}
/* Currently (as of DeviceOS 2.0.1) the Power manager cannot enable or disable charging.
To do so one needs to call the old PMIC API.
To call the PMIC API it ideally needs to be wrapped inside a function to avoid
interference from the Power Manager.
*/
void turnOnCharging()
{
PMIC pmic(true); //Calling it with true locks it to the user code
pmic.enableCharging();
}