First post, so apologies for any etiquette errors on my part.
I'm implementing a simple clock/timer that gets NTP updates via WiFi. This works fine, except that if the WiFi connection fails (e.g. router restarted) there is no automatic reconnection. I can reconnect by calling WiFi.begin() again, but this blocks the main loop for about 10 seconds.
I tried using WiFiConnectionHandler, but it has essentially the same behaviour: the call to WiFiConnectionHandler.check() is normally only a few micros, but if the handler has to try and reconnect it also takes about 10 seconds.
Is there any way I can avoid this blocking? It's a clock, so it has other things to do - like refreshing the display - that can't really wait for this length of time.
Thanks in advance.
Using a multi-core MCU and tasking, you could put all blocking code on one CPU and all non-blocking code on the other. But here is the tricky part: the actual WiFi radio code at the OS level will already be doing that, so make sure you put your code on the same CPU.
Thanks for the suggestion. At this stage in my Arduino journey it's probably more than I can sensibly take on, but its definitely one for the todo list. It's time for me to go and read some code and docs!
Never checked before, but it is blocking on R4 with the WiFiS3 library. Almost all of the examples are wrong then: there's another 10-second delay after the begin call, with a misleading comment.
// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
R4 uses ESP32 for WiFi. On an actual ESP32 board, which uses its own WiFi library, begin is non-blocking, so the typical != WL_CONNECTED loop for those makes more sense.
Looking at the R4 code, it's right there at the end of begin
unsigned long start_time = millis();
while(millis() - start_time < _timeout){
if(status() == WL_CONNECTED) {
return WL_CONNECTED;
}
}
return WL_CONNECT_FAILED;
its own WL_CONNECTED loop. It depends on a timeout that defaults to
CWifi::CWifi() : _timeout(10000){
10 seconds. It's private, and not used for anything else. Therefore it can be set to zero; begin won't check the status even once.
That means you can make the WiFi connection part of the main loop. Something like
#include <WiFi.h>
#include "arduino_secrets.h"
bool wifiConnectING;
void setup() {
Serial.begin(115200);
#ifdef ARDUINO_WiFi_S3_H
WiFi.setTimeout(0); // make WiFi.begin non-blocking
#endif
// Instead of some arbitrary wait for Serial to be ready....
// Press Enter in Serial Monitor with New Line to get started
while (!Serial.available());
// Drain serial buffer just to be correct
while (Serial.available()) Serial.read();
}
void checkWifiLoop() {
auto now = millis();
static auto lastCheck = now;
if (now - lastCheck < 75) {
return;
}
lastCheck = now;
if (WiFi.status() != WL_CONNECTED) {
if (wifiConnectING) {
static unsigned dotsInARow;
if (++dotsInARow >= 60) {
dotsInARow = 0;
Serial.println();
}
Serial.print('.');
} else {
Serial.println("Connecting to WiFi...");
WiFi.begin(SECRET_SSID, SECRET_PASS);
wifiConnectING = true;
}
} else if (wifiConnectING) {
wifiConnectING = false;
Serial.println("Now connected to WiFi as: ");
Serial.println(WiFi.localIP());
}
}
void workLoop() {
static unsigned long counter;
static auto lastStart = millis();
if (++counter >= 8675309 / 4) {
counter = 0;
Serial.print("quarter-Jenny: ");
auto now = millis();
Serial.println(now - lastStart);
lastStart = now;
}
}
void loop() {
checkWifiLoop();
workLoop();
}
To more easily see the startup behavior, the sketch waits for Serial input.
The workLoop counts to an arbitrary number, printing the time it takes to get there. While the checkWifiLoop is also trying to connect, it takes longer to get the same amount of work done.
The checkWifiLoop might also need to give up after 10 seconds or whatever and call begin again; not clear if R4 will keep trying forever. Try it by keeping the router off for a minute.
Is there any chance that you change from the Uno R4 Wifi to a common ESP32 board? If you need wifi and NTP it will get much more easier to use only the ESP32 instead.
Setting the WiFi timeout to zero and managing the reconnect in the main loop basically works. The calls to WiFi.status() take less than 10 millis, and WiFi.begin() less than 15. However, during the disconnection a couple of the WiFi.status() calls took about 350 millis. This is manageable for my use case - I can just blank the clock for a brief period.
I also tried with WiFiConnectionHandler. This doesn't work at all with zero WiFi timeout, but will work with 1000 millis timeout. This has the advantage that calls to WiFiConnectionHandler.check() take less than 10 micros in normal operation, and the disadvantage that they take over 1000 millis if trying to reconnect.
Interestingly, in both cases, the disconnect was followed by a reconnect, then a second disconnect and a second reconnect. When I get time I'll investigate this.
Many thanks for your help.
Ah, I thought the latest and greatest board would also be the best. I'm learning quite a bit ![]()