In the previous section, we learned about NTP network time synchronization. The code can successfully retrieve network time. But have you thought about: what happens if WiFi suddenly disconnects?
The previous section's code assumes that once WiFi is connected, it stays connected. But in reality, WiFi connections drop for various reasons. Once disconnected, the code would freeze or error out, unable to recover. Stability optimization allows devices to automatically recover from network issues, enabling long-term stable operation.
Let's compare the previous code with this section's code to see what hidden issues existed:
| Previous Problem | Possible Consequence | This Section's Solution |
|---|---|---|
| No timeout protection during WiFi connection | Forever stuck in while loop on wrong password | Added 10-second timeout mechanism |
| No periodic WiFi status check | Completely unaware of disconnection, keeps erroring | Check every 2 seconds, auto-reconnect |
| No reconnection mechanism | Cannot recover after router restart | Auto-call reconnect on disconnection detection |
| No state management | Still tries NTP requests when disconnected, wasting resources | Use wifiConnected flag to control task execution |
| Cause | Example |
|---|---|
| Router restarts | Power outage, firmware update, scheduled reboot |
| Signal interference | Microwave ovens, Bluetooth devices, neighboring WiFi |
| Distance issues | Device too far from router, moving to another room |
| DHCP lease expiration | Router periodically reassigns IP addresses |
| Firmware bugs | UNO R4 WiFi sometimes reports "connected" with IP 0.0.0.0 |
The NTP-related functions used in this section are the same as the previous section and won't be repeated here. Focus is on stability-related functions:
| Function | Description | Role in Stability |
|---|---|---|
WiFi.begin(ssid, password) |
Connect to specified WiFi network | Initiate new connection |
WiFi.disconnect() |
Disconnect current WiFi connection | Clear old state before reconnecting |
WiFi.status() |
Return connection status, WL_CONNECTED means connected |
Detect disconnection |
WiFi.localIP() |
Return local IP address | Double-check (prevent firmware bug) |
millis() returns the number of milliseconds since the program started, and is key to implementing non-blocking scheduling. We use it to periodically check WiFi status instead of using delay() which blocks the program:
unsigned long lastCheck = 0;
void loop() {
if (millis() - lastCheck > 2000) { // Execute every 2 seconds
checkWiFiStatus(); // Check WiFi status
lastCheck = millis(); // Update timestamp
}
}
const char* ssid = "YourWiFiName"; // Must be 2.4GHz, avoid Chinese characters and special symbols
const char* password = "YourWiFiPassword"; // Case-sensitive
const long TIME_ZONE = 8 * 3600; // Beijing Time UTC+8
This section's code adds the following stability features on top of the previous NTP code:
| Addition | Location | Purpose |
|---|---|---|
wifiConnected flag |
Global variable | Track connection status, control task execution |
lastWifiCheck variable |
Global variable | Record last check time, enable periodic monitoring |
reconnectCount variable |
Global variable | Count reconnection attempts, for debugging |
connectWiFi() function |
New function | Encapsulate connection logic with timeout protection |
checkWiFiStatus() function |
New function | Periodic status check, auto-reconnect |
| Timeout counter | Inside connectWiFi() |
Prevent getting stuck during connection |
| Status output logic | Inside loop() |
Display different info based on state |
unsigned long lastWifiCheck = 0; // Records last WiFi status check time
bool wifiConnected = false; // WiFi connection status flag (core!)
int reconnectCount = 0; // Reconnection counter (for debugging)
Why use the wifiConnected flag?
This is the core of the entire stability system. It allows other parts of the code to quickly determine if WiFi is currently connected without calling WiFi.status() every time. When the connection drops or recovers, this flag is updated, controlling whether other tasks in loop() execute.
setup() - Initializationvoid setup() {
Serial.begin(9600);
while (!Serial);
Serial.println("=== Arduino UNO R4 WiFi Stability Optimization ===");
Serial.println("System starting, initializing WiFi...");
connectWiFi(); // First WiFi connection on boot (encapsulated as function)
Udp.begin(localPort);
Serial.println("UDP Initialized (For NTP Example)\n");
}
Compared to the previous section, the WiFi connection logic is encapsulated into the connectWiFi() function, allowing reuse in multiple places (both initialization and reconnection call it).
loop() - Main Loop (New WiFi Monitoring)void loop() {
// Task 1: WiFi Monitoring (every 2 seconds) — NEW!
if (millis() - lastWifiCheck > 2000) {
checkWiFiStatus();
lastWifiCheck = millis();
}
// Task 2: NTP Time Sync (every 15 seconds, only when WiFi connected) — Added condition
if (wifiConnected && millis() - lastNtpUpdate > 15000) {
getNtpTime();
lastNtpUpdate = millis();
}
// Task 3: Status Output — NEW!
if (currentUnixTime > 0) {
unsigned long now = currentUnixTime + (millis() - lastNtpUpdate) / 1000;
printTime(now);
} else if (wifiConnected) {
Serial.println("WiFi Connected - Waiting for NTP Sync...");
} else {
Serial.println("WiFi Disconnected - Waiting for Reconnection...");
}
}
Key differences from previous section:
checkWiFiStatus() every 2 seconds to monitor WiFi healthwifiConnected && condition: No NTP requests when disconnected, avoiding wasted effortconnectWiFi() - Connection with Timeout (Core Function 1)void connectWiFi() {
WiFi.disconnect(); // Clear previous connection
delay(100);
WiFi.begin(ssid, password); // Start connection
int timeout = 0;
while (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0)) {
delay(500);
Serial.print(".");
timeout++;
if (timeout > 20) { // 10s timeout (20 × 500ms)
Serial.println("\nWiFi Connection Timeout (10s), will retry...");
reconnectCount++;
wifiConnected = false;
return; // Give up this attempt and return
}
}
wifiConnected = true;
Serial.println("\n✅ WiFi Connected Successfully!");
Serial.print("📶 Device IP Address: ");
Serial.println(WiFi.localIP());
reconnectCount = 0;
}
Comparison with previous section's connection logic:
| Previous Section | This Section |
|---|---|
Connected directly in setup() |
Encapsulated as reusable function |
| No timeout protection, could hang forever | Auto-gives up after 10 seconds |
| No follow-up handling on failure | Sets wifiConnected = false, waits for retry |
| No clearing of old connection | WiFi.disconnect() before connecting |
Four steps in detail:
Step 1: Clear old connection
WiFi.disconnect(); // Disconnect first to ensure clean start
delay(100); // Wait 100ms for disconnect to complete
WiFi.begin(ssid, password); // Start new connection
Step 2: Wait for connection (double-check)
while (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0))
Both conditions must be satisfied to exit the loop:
WiFi.status() == WL_CONNECTED: WiFi status shows connectedWiFi.localIP() != 0.0.0.0: IP address is not 0.0.0.0Why double-check? UNO R4 WiFi has a firmware bug where WiFi.status() returns connected, but the IP address is still 0.0.0.0, meaning no actual internet access.
Step 3: Timeout protection
if (timeout > 20) { // 20 × 500ms = 10 seconds
wifiConnected = false;
return; // Give up this attempt and return
}
Without timeout protection, the while loop would run forever when the password is wrong or the network is unavailable.
Step 4: Success handling
wifiConnected = true; // Update status flag
reconnectCount = 0; // Reset reconnection counter
checkWiFiStatus() - Status Check and Auto-Reconnect (Core Function 2)void checkWiFiStatus() {
if (WiFi.status() != WL_CONNECTED || WiFi.localIP() == IPAddress(0, 0, 0, 0)) {
Serial.println("\n⚠ WiFi Error, starting auto-reconnect...");
wifiConnected = false;
connectWiFi(); // Call connection function to attempt reconnection
} else {
if (!wifiConnected) { // Was disconnected, now recovered
Serial.println("\n✅ WiFi Restored, resuming operation!");
wifiConnected = true;
}
}
}
This is the core of the entire stability optimization—the "watchdog", called every 2 seconds in the main loop:
wifiConnected = false → call connectWiFi() to reconnectwifiConnected = trueThis is the function that enables the device to "self-heal"—detecting disconnection and immediately attempting to reconnect.
getNtpTime(), printTime(), and isLeap() are identical to the previous section and won't be re-explained. The only difference is getNtpTime() now has success/failure indicators:
Serial.println("✅ NTP Time Sync Successful"); // On success
Serial.println("⚠ NTP Server No Response"); // On failure
Normal operation:
=== Arduino UNO R4 WiFi Stability Optimization ===
System starting, initializing WiFi...
...
✅ WiFi Connected Successfully!
📶 Device IP Address: 192.168.1.105
UDP Initialized (For NTP Example)
✅ NTP Time Sync Successful
2024-05-20 14:30:05
2024-05-20 14:30:06
WiFi disconnects and auto-recovers:
⚠ WiFi Error (Disconnected/Invalid IP), starting auto-reconnect...
.....
✅ WiFi Connected Successfully!
📶 Device IP Address: 192.168.1.105
✅ WiFi Restored, resuming operation!
✅ NTP Time Sync Successful
2024-05-20 14:35:12
Q1: Why does the "WiFi Connection Timeout" message keep appearing?
A: WiFi name or password is wrong, or connecting to 5G WiFi. The UNO R4 WiFi only supports 2.4G. Please check:
Q2: Why does the IP address show 0.0.0.0?
A: This is a known UNO R4 WiFi firmware bug. WiFi.status() shows connected, but IP is still 0.0.0.0. This code handles it through double-checking (both WiFi.status() and WiFi.localIP()).
Q3: Why do we need WiFi.disconnect() before WiFi.begin()?
A: In reconnection scenarios, disconnecting first clears any residual connection state, ensuring the new connection starts clean. This avoids the "appears connected but actually not working" problem.
Q4: Will millis() overflow?
A: millis() returns unsigned long, which overflows to zero after approximately 50 days. But this project's scheduled tasks are unaffected because the millis() - lastCheck difference is still calculated correctly within the unsigned long range.
Q5: What happens after a timeout?
A: After timeout, connectWiFi() returns immediately, wifiConnected stays false. Task 2 (NTP sync) won't execute in the main loop, Task 3 shows "WiFi Disconnected - Waiting for Reconnection...". After 2 seconds, checkWiFiStatus() will attempt reconnection again.
| Problem | Possible Cause | Solution |
|---|---|---|
| WiFi connection timeout | Wrong password/5G WiFi | Verify credentials, ensure 2.4GHz |
| Frequent disconnections | Weak signal | Move board closer to router |
| No reconnection after timeout | Check interval too long | Confirm lastWifiCheck interval is 2000ms |
| NTP no response | No internet/firewall | Check network, try different NTP server |
By default, checked every 2 seconds. To reduce resource usage:
if (millis() - lastWifiCheck > 5000) { // Change to check every 5 seconds
Default connection timeout is 10 seconds. For poor network environments:
if (timeout > 40) { // Change to 20-second timeout (40 × 500ms)
if (wifiConnected && millis() - lastNtpUpdate > 60000) { // Change to sync every 1 minute
const char* ntpServer = "cn.pool.ntp.org"; // China NTP server
When network latency is high, 150ms may not be enough:
delay(300); // Increase from 150ms to 300ms
Sound an alert when WiFi disconnects:
// Add in checkWiFiStatus() when disconnection is detected
tone(8, 1000, 200); // Pin 8, 1000Hz, duration 200ms
Record connection/disconnection events for later analysis:
#include <SD.h>
File logFile = SD.open("wifi_log.txt", FILE_WRITE);
if (logFile) {
logFile.print(millis());
logFile.println(" - WiFi Disconnected");
logFile.close();
}
Display WiFi status and time in real-time without needing a computer:
display.clearDisplay();
display.setCursor(0, 0);
display.print("WiFi: ");
display.println(wifiConnected ? "OK" : "FAIL");
display.println(timeString);
display.display();
Avoid infinite reconnection when network is completely unavailable:
if (reconnectCount > 10) {
Serial.println("Too many reconnections, entering sleep mode...");
delay(60000); // Wait 1 minute before trying again
reconnectCount = 0;
}
Auto-switch when primary server doesn't respond:
const char* ntpServers[] = {"pool.ntp.org", "cn.pool.ntp.org", "time.nist.gov"};
int currentServer = 0;
// Switch on no response: currentServer = (currentServer + 1) % 3;
This stability framework can serve as the foundation for any WiFi-based IoT project: