@rickkas7 ,
Thank you for taking a look. To answer your first question, I handle timeouts to reconnect with a non-blocking state called CONNECTING_STATE in my state machine. In this state, I device whether a connection is needed and if so, if it is needed at this time. It also allows for a user over-ride using a button on the carrier so that a device can always be brought on-line manually. Finally, it records the details of a successful or unsuccessful connection.
// Before Setup()
unsigned long connectMaxTimeSec = 11 * 60; // Timeout for trying to connect to Particle cloud in seconds
// Main loop Finite State machine
case CONNECTING_STATE:{ // Will connect - or not and head back to the Idle state
static State retainedOldState; // Keep track for where to go next (depends on whether we were called from Reporting)
static unsigned long connectionStartTimeStamp; // Time in Millis that helps us know how long it took to connect
if (state != oldState) { // Non-blocking function - these are first time items
retainedOldState = oldState; // Keep track for where to go next
sysStatus.lastConnectionDuration = 0; // Will exit with 0 if we do not connect or are connected or the connection time if we do
publishStateTransition();
// Let's make sure we need to connect
if (sysStatus.connectedStatus && Particle.connected()) {
Log.info("Connecting state but already connected");
(retainedOldState = REPORTING_STATE) ? state = RESP_WAIT_STATE : state = IDLE_STATE;
break;
}
// If we are in a low battery state - we are not going to connect unless we are over-riding with user switch (active low)
if (sysStatus.lowBatteryMode && digitalRead(userSwitch)) {
Log.info("Connecting state but low battery mode");
state = IDLE_STATE;
break;
}
// If we are in low power mode, we may bail if battery is too low and we need to reduce reporting frequency
if (sysStatus.lowPowerMode && digitalRead(userSwitch)) { // Low power mode and user switch not pressed
if (sysStatus.stateOfCharge <= 50 && (Time.hour() % 4)) { // If the battery level is <50%, only connect every fourth hour
Log.info("Connecting but <50%% charge - four hour schedule");
state = IDLE_STATE; // Will send us to connecting state - and it will send us back here
break;
} // Leave this state and go connect - will return only if we are successful in connecting
else if (sysStatus.stateOfCharge <= 65 && (Time.hour() % 2)) { // If the battery level is 50% - 65%, only connect every other hour
Log.info("Connecting but 50-65%% charge - two hour schedule");
state = IDLE_STATE; // Will send us to connecting state - and it will send us back here
break; // Leave this state and go connect - will return only if we are successful in connecting
}
}
// OK, let's do this thing!
connectionStartTimeStamp = millis(); // Have to use millis as the clock will get reset on connect
Cellular.on(); // Needed until they fix this: https://github.com/particle-iot/device-os/issues/1631
Particle.connect(); // Told the Particle to connect, now we need to wait
}
sysStatus.lastConnectionDuration = int((millis() - connectionStartTimeStamp)/1000);
if (Particle.connected()) {
sysStatus.connectedStatus = true;
sysStatus.lastConnection = Time.now(); // This is the last time we attempted to connect
recordConnectionDetails(); // Record outcome of connection attempt
Log.info("Cloud connection successful");
attachInterrupt(userSwitch, userSwitchISR,FALLING); // Attach interrupt for the user switch to enable verbose counts
if (retainedOldState == REPORTING_STATE) state = RESP_WAIT_STATE;
else state = IDLE_STATE;
}
else if (sysStatus.lastConnectionDuration > connectMaxTimeSec) {
current.alerts = 2; // Connection timed out alert
sysStatus.connectedStatus = false;
recordConnectionDetails(); // Record outcome of connection attempt
Log.info("cloud connection unsuccessful");
disconnectFromParticle(); // Make sure the modem is turned off
if (sysStatus.solarPowerMode) setLowPowerMode("1"); // If we cannot connect, there is no point to stayng out of low power mode
if ((Time.now() - sysStatus.lastConnection) > 3 * 3600L) { // Only sends to ERROR_STATE if it has been over three hours - this ties to reporting and low battery state
state = ERROR_STATE;
resetTimeStamp = millis();
break;
}
else state = IDLE_STATE;
}
} break;
What I am unclear about is how my code would know a reconnection attempt is underway as a reconnection does not invoke my CONNECTING_STATE.
I would assume that, if I could detect a reconnection attempt, I could call my disconnectFromParticle function (below) after it went for more than 11 minutes. Would this work?
bool disconnectFromParticle() // Ensures we disconnect cleanly from Particle
// Updated based onthis thread: https://community.particle.io/t/waitfor-particle-connected-timeout-does-not-time-out/59181
{
Log.info("In the disconnect from Particle function");
Particle.disconnect();
waitForNot(Particle.connected, 15000); // make sure before turning off the cellular modem
Cellular.disconnect(); // Disconnect from the cellular network
Cellular.off(); // Turn off the cellular modem
waitFor(Cellular.isOff, 30000); // As per TAN004: https://support.particle.io/hc/en-us/articles/1260802113569-TAN004-Power-off-Recommendations-for-SARA-R410M-Equipped-Devices
sysStatus.connectedStatus = false;
systemStatusWriteNeeded = true;
detachInterrupt(userSwitch); // Stop watching the userSwitch as we will no longer be connected
return true;
}
That said, would changing the System Mode to “Manual” solve any of this? In manual mode, will the system initiate a reconnect attempt if the current session stops working?
As for the firmware update case, I have done a large number of these using the Intelligent OTA process. From my experience, this process works very reliably and, when it fails, the devices continue to operate so I believe this is less of an issue.
As always, any comments for suggestions are welcome.
Thanks, Chip