Keep trying if 'onEnable' fails
bakadave opened this issue · 8 comments
I have implemented an MQTT client, when the task is enabled the onEnable
method connects to the broker and the callback method handles the rest. My problem is that if the connection is ever broken this will only try to reconnect once and then the task is disabled forever.
Ideally it would try to run the onEnable
method every 'x' seconds until it reconnects. What would be the best way to go about it in your opinion?
Task t_mqtt(TASK_IMMEDIATE, TASK_FOREVER, &mqtt_callback, &ts, false, &mqtt_connect);
bool mqtt_connect() {
return MQTTreconnect();
}
void mqtt_callback() {
if (!mqtt.connected())
t_mqtt.enable(); //run onEnable() method immediately and run this task afterwards
mqtt.loop();
}
P.S.: I just realised I could utilise the onDisable
method and maybe create a new task that re-enables t_mqtt after 'x' seconds. Still I'd prefer to do it without an extra task if possible.
On second thought the problem with the onDisable
approach is that if the onEnable
method fails on the first try, onDisable
is never called (at least according to the API documentation).
Hi @bakadave
I don't think onEnable
should be used this way.
Also, you can run your mqtt_callback on an interval, not immediately to free up cycles for other tasks.
So at every iteration, you check if you are still connected, and if not, reconnect. If you are - you service the MQTT client.
Why not this:
Task t_mqtt(10 * TASK_MILLISECOND, TASK_FOREVER, &mqtt_callback, &ts, false);
void mqtt_callback() {
if (!mqtt.connected()) {
MQTTreconnect();
t_mqtt.delay(500); // or however much time you want to give mqtt client to reconnect in the background
return;
}
mqtt.loop();
}
Hi @arkhipenko thank you for your feedback I understand this was not the way you intended for it to be used however I've come up with a method that is giving me satisfactory results. In this example I'll demostrate a roboust ethernet connection that handles disconnections and reconnections:
Task t_ethWD(1 * TASK_SECOND, TASK_FOREVER, ð_watchdog, &ts, true);
Task t_eth(500 * TASK_MILLISECOND, TASK_FOREVER, ð_callback, &ts, false, ð_onEnable, ð_onDisable);
/**
* @brief callback() method of the main Ethernet task
* If Ethernet connection is lost, it disables t_eth task which calls the onDisable() method and starts
* the watchdog.
*/
void eth_callback() {
if(Ethernet.linkStatus() == LinkOFF) {
t_eth.disable();
return;
}
int res = Ethernet.maintain();
if(res == DHCP_REBIND_FAILED || res == DHCP_RENEW_FAILED)
t_eth.disable();
}
/**
* @brief onEnable() method of the main Ethernet task
* If connection is successful it disables the Ethernet watchdog t_ethWD task.
*/
bool eth_onEnable() {
if(Ethernet.linkStatus() != LinkON)
return false;
ts.pause(); //pause TaskScheduler before blocking call
bool res = Ethernet.begin(mac);
ts.resume(); //resume TaskScheduler
if(res) {
t_ethWD.disable();
//start dependent tasks here
return true;
}
return false;
}
/**
* @brief onDisable() method of the main Ethernet task
* It re-enables the Ethernet watchdog t_ethWD task.
*/
void eth_onDisable() {
t_ethWD.enable();
//stop dependent tasks here
}
/**
* @brief callback() method of the Ethernet watchdog task
* Enables the main Ethernet task on startup or after connection is lost.
* Keeps trying to enable the main task every 5s until connection is reestabilished.
*/
void eth_watchdog() {
t_eth.enable();
}
Ethernet watchdog t_ethWD
is started after the Arduino setup()
function is done. The watchdog tries to enable the main ethernet task t_eth
every second. If the ethernet onEnable()
method returns true the watchdog is disabled and the ethernet callback maintains DHCP every 500ms. If at any point ethernet connection is lost the main ethernet task is disabled and the onDisable()
method starts the watchdog. This can be used to create a "task chain" if ethernet dependent tasks (like MQTT) are started when ethernet onEnable()
returns true and stopped from the ethernet onDisable()
method.
One thing this solution allows me to do is to not waste time connecting to MQTT when the ethernet is offline, also Ethernet.begin()
has a really long timeout and it caused other tasks to "pile up" and be called multiple times after the timeout expired.
I am glad you found a solution.
By the way, in
ts.pause(); //pause TaskScheduler before blocking call
bool res = Ethernet.begin(mac);
ts.resume(); //resume TaskScheduler
pause/resume are unnecessary IMHO. You are running in the same thread, so no task will run until you return anyways.
pause/resume is good when you return to the scheduler.
pause/resume are unnecessary IMHO. You are running in the same thread, so no task will run until you return anyways. pause/resume is good when you return to the scheduler.
Ethernet.begin(); can take multiple times longer than the period of many concurrently running tasks so if it runs all the way to timeout there were periodic tasks being called 2-3 times immediately after the thread finished.
Got it!
You can prevent it with scheduling options:
TASK_SCHEDULE - schedule is a priority, with "catch up" (default)
TASK_SCHEDULE_NC - schedule is a priority, without "catch up"
TASK_INTERVAL - interval is a priority, without "catch up"
Thank you will check it out.