I have a thing that needs to operate whether it is connected to the cloud or not. The cloud is used for remote telemetry monitoring, but is not the thing's primary function.
I'm using an MKR WiFi1010 connected to a RoboClaw 2x15A. Normal loop execution takes about 10ms, but if I power on the device without an available WiFi connection (or USB), there is a long delay (seconds) between control inputs and system response. This was not an issue running the non-cloud version of the code.
Any suggestions to prevent cloud functions from signficantly slowing program execution?
I copied and pasted a fully functional sketch into the Arduino Cloud IoT template that was automatically generated after I created my thing. I moved the declarations, setup code, and loop code the appropriate sections of the sketch. I deleted some of my variable declarations from the sketch and moved those definitions to the thing setup page. The only thing that was different inside the main loop() literally was the ArduinoCloud.update() call. If I commented out ArduinoCloud.update(), everything worked as before. By process of elimination, ArduinoCloud.update() is the cause, at least at the level of my sketch.
As a reminder, the answer I need is essentially how to make IoT cloud interaction non-blocking with respect to the main loop(). If there are any connection manager subject matter experts who are aware of something obvious but perhaps undocumented (e.g. a connection attempt timeout setting or interrupt trigger) I would appreciate knowing about it.
Regarding my own attempts to fix the issue, this is what I've learned/tried:
The initial problem, which was repeatable, was that if WiFi.status() returned 6 (disconnected), execution would hang on the next call to ArduinoCloud.update(). It appeared that the connection manager was trying to reconnect to the cloud without first reconnecting to Wi-Fi. This was only an issue if a Wi-Fi connection was established and then lost.
Also, assuming Wi-Fi is available, I discovered that connecting to Wi-Fi and the cloud typically happen fairly quickly. The part that seems to be causing issues is establishing a thing connection.
Steps I took to rectify the situation:
Disable the watchdog to prevent constant rebooting if a connection could not be established.
Changed as many cloud variables as practical from update on change to update periodically to reduce the amount of data that needed to be sent.
Added logic to prevent connection/reconnection attempts unless the device is in an idle operating mode. This prevents connection attempts from initiating in the middle of other device activities, which could lead to damaging runaway conditions while execution is blocked by something inside ArduinoCloud.update().
Added a timer so that reconnection attempts only happen once every 5 seconds while the device is idle, and once every 60 seconds otherwise. "Otherwise" means anything other than "idle" within the last 60 seconds of up time. If connection manager activities are slow (multiple seconds inside ArduinoCloud.update()), this ensures that there is enough time between those activities for the device operator (a human) to initiate the desired action. Without this, execution spends most of its time inside ArduinoCloud.update() trying to establish a connection and the device is not serviceable.
Check connection states prior to calling ArduinoCloud.update(). If Wi-Fi is not connected (WiFi.status() == 3), completely block execution of ArduinoCloud.update(). Call WiFi.begin(SSID, PASS) and wait for a connection before trying again. If Wi-Fi is connected but there is no connection to the cloud, reduce the frequency of the ArduinoCloud.update() call from every loop to the timed and qualified connection logic described above.
As of 9/30/2022, this seemed to resolve my issues for the most part. Still not ideal, as I would like connection manager activities to be transparent to the device user, but serviceable. There were only occasional interruptions in device function, when a cloud or thing reconnection attempt coincided with someone trying to use the device. Cloud communication worked well, provided the device wasn't too far away from a router.
Today (10/12/2022)
I've started seeing intermittent freezing again. Trying to figure out what changed.
here is the thigny I use to maintain and keep a MQTT connection.
void MQTTkeepalive( void *pvParameters )
{
sema_MQTT_KeepAlive = xSemaphoreCreateBinary();
xSemaphoreGive( sema_MQTT_KeepAlive ); // found keep alive can mess with a publish, stop keep alive during publish
MQTTclient.setKeepAlive( 90 ); // setting keep alive to 90 seconds makes for a very reliable connection, must be set before the 1st connection is made.
TickType_t xLastWakeTime = xTaskGetTickCount();
const TickType_t xFrequency = 250; // 250mS
for (;;)
{
//check for a is-connected and if the WiFi 'thinks' its connected, found checking on both is more realible than just a single check
if ( (wifiClient.connected()) && (WiFi.status() == WL_CONNECTED) )
{
xSemaphoreTake( sema_MQTT_KeepAlive, portMAX_DELAY ); // whiles MQTTlient.loop() is running no other mqtt operations should be in process
MQTTclient.loop();
xSemaphoreGive( sema_MQTT_KeepAlive );
}
else {
log_i( "MQTT keep alive found MQTT status %s WiFi status %s", String(wifiClient.connected()), String(WiFi.status()) );
if ( !(wifiClient.connected()) || !(WiFi.status() == WL_CONNECTED) )
{
connectToWiFi();
}
connectToMQTT();
}
xLastWakeTime = xTaskGetTickCount();
vTaskDelayUntil( &xLastWakeTime, xFrequency );
}
vTaskDelete ( NULL );
}
////
void connectToMQTT()
{
// create client ID from mac address
byte mac[5];
int count = 0;
WiFi.macAddress(mac); // get mac address
String clientID = String(mac[0]) + String(mac[4]);
log_i( "connect to mqtt as client %s", clientID );
while ( !MQTTclient.connected() )
{
MQTTclient.disconnect();
MQTTclient.connect( clientID.c_str(), mqtt_username, mqtt_password );
vTaskDelay( 250 );
count++;
if ( count == 5 )
{
ESP.restart();
}
}
MQTTclient.setCallback( mqttCallback );
MQTTclient.subscribe( topicOK );
}
////
void connectToWiFi()
{
int TryCount = 0;
while ( WiFi.status() != WL_CONNECTED )
{
TryCount++;
WiFi.disconnect();
WiFi.begin( SSID, PASSWORD );
vTaskDelay( 4000 );
if ( TryCount == 10 )
{
ESP.restart();
}
}
WiFi.onEvent( WiFiEvent );
} // void connectToWiFi()
//////