Before clarifying some of the "sideshow" topics I want to address your immediate issues
That's most likely due to the fact that Software Timers run on a separate thread with very a limitted stack quota. Henc you should not call any "elaborate" functions and need to consider the call depth of your function and its functon calls.
The typical way to go about this would be to keep the hard work in the application thread (aka loop()
) and essentially let the timer callback only set a flag to tell loop()
that it's time to do something (some conditional checks and brief precessing is fine tho').
I'd have to double check, but for this scenario I'd rather opt for SYSTEM_MODE(MANUAL)
in conjunction with SYSTEM_THREAD(ENABLED)
to keep the system from getting "opinionated" about the need to reconnect.
Now on to the "sideshow" topics raised:
While it is true, that Particle.function()
s (same as Particle.variable()
and Particle.subscribe()
) need to be registered the latest a few seconds after a connection is established but there is absolultely no need to hold back from registering them before a connection is made.
The above function calls only tell the device which entities should be registered with the cloud once the cloud connection becomes available. So doing it at the earliest possible point in time (e.g. setup()
) is the safest bet.
The reason that such nuances may be missing in the docs is probably owed to the fact that the majority of the reference docs stem from a time before non-AUTOMATIC system modes were introduced or widely used and with AUTOMATIC mode there only is a time after the cloud connection was established because user code won't be executed without it
As a safe guard, you could setup a timer that checks for WiFi.listening()
and if your application currently wouldn't agree with that (e.g. by means of a dedicated flag) WiFi.listen(false)
can be called to end Listening Mode.
In order to get earliest info about loss of connection you could register a System.on()
event handler to be informed ASAP.
I'd rather wirte this
like that
WiFiAccessPoint credAP;
if (WiFi.getCredentials(&credAP, 1))
Log.info("Need to find SSID from credentials: %s", (const char*)credAP.ssid);
else
return false;
For one, this makes it clear that you are only interested in a single set of credentials and also ensures that you are dealing with the correct string pointer.
Without checking, WiFiAccessPoint::ssid
may well be a String
object and requesting a pointer to it may not actually give you the pointer to the string buffer but to the object itself.
So - since I tend to be too lazy to look it up - I'd opt for the safe bet which will work either way (that's also why I prefer using (const char*)someString
over someString.c_str()
as the former works for both - C strings and String
- while the latter only works with String
objects )
And finally, you should really check whether you actually got back a set of creds before trying to access the WiFiAccessPoint
fields.
Also when traversing your availableAPS
you may want to break
the loop once you found your desired AP.
BTW, scanning WiFi networks may take some time, so it shouldn't be done too frequently.
Finally, about this
in connection with
You can either follow the Particle.connect()
call with a waitFor()
or waitUntil()
or - if you want your code to keep running during the ongoing connection attempt, you'd set a timeout-guard for the retry (I'd personally not trust Particle.connecting()
for that as your might just slip between the change of state and get a false report).
This would be my take on a timeout-guard
const uint32_t msRetryConnect = 30000; // allow at least 30 sec between retries
uint32_t msOngoingConnect = 0;
...
// when not connected and last attempt was long gone or it's actually the first
if (!Particle.connected() && ((millis() - msOngoingConnect > msRetryConnect) || !msOngoingConnect)
{
msOngoingConnect = millis(); // set the timeout base
Particle.connect(); // initiate (re)connect
}
// carry on with business
...