This comprehensive lab provides a production-ready ESP32 Wi-Fi implementation (~500 lines) demonstrating six essential IoT patterns: robust connection management with exponential backoff (1s to 60s retry delays), RSSI signal quality monitoring with rolling averages and signal bar visualization, power mode cycling between Active (100 mA), Modem Sleep (20 mA), and Light Sleep (2 mA), HTTP GET/POST communication with timeout handling, mDNS service discovery and advertisement (accessible at esp32-iot-lab.local), and event-driven architecture using WiFi.onEvent() for responsive state transitions.
Sensor Squad: Comprehensive Wi-Fi Lab
The Sensor Squad was building their most advanced project yet – a Wi-Fi device that could handle ANYTHING the real world throws at it!
“In real life, Wi-Fi connections DROP sometimes,” explained Max the Microcontroller. “So I use a state machine – I always know if I’m DISCONNECTED, CONNECTING, CONNECTED, or in an ERROR state. If I lose connection, I try again after 1 second, then 2 seconds, then 4 seconds – that is called EXPONENTIAL BACKOFF. It prevents me from flooding the network with connection attempts!”
“I check the signal strength every 5 seconds,” said Sammy the Sensor. “RSSI tells me how strong the Wi-Fi signal is. Above -50 dBm is Excellent, below -85 dBm is Critical. I even calculate a rolling average so one bad reading does not cause a false alarm!”
Bella the Battery demonstrated her favorite feature: “I cycle through THREE power modes! Active mode uses 100 mA for maximum speed, Modem Sleep drops to 20 mA by turning off the radio between beacons, and Light Sleep goes down to 2 mA by pausing the CPU too. A 2000 mAh battery lasts 20 hours in Active mode but 1000 hours in Light Sleep!”
“And I can be found by name!” added Lila the LED. “mDNS lets other devices find me at esp32-iot-lab.local instead of remembering my IP address. It is like having a phone book for the local network!”
47.1 Learning Objectives
By the end of this lab, you will be able to:
Construct a production-ready ESP32 Wi-Fi application with exponential-backoff reconnection logic
Evaluate real-time RSSI values to calculate signal quality percentages and trigger threshold-based alerts
Compare Active, Modem Sleep, and Light Sleep power modes in terms of current draw and battery life trade-offs
Integrate HTTP GET and POST requests with timeout handling and structured JSON payloads
Configure mDNS service advertisement and discovery to eliminate hardcoded IP addresses
Analyse Wi-Fi event callbacks to design non-blocking, state-machine-driven connection management
For Beginners: What This Lab Covers
What is this lab? A complete, production-ready ESP32 Wi-Fi implementation demonstrating best practices for IoT deployments.
Key Features Demonstrated:
Robust connection management with auto-reconnection
Signal strength monitoring and quality assessment
Power management mode transitions
HTTP client communication (GET/POST)
mDNS service discovery and advertisement
Event-driven architecture patterns
Time Required: ~45 minutes
Prerequisites:
Complete the basic ESP32 Wi-Fi chapters first
Understanding of HTTP and power management concepts
Putting Numbers to It
Exponential backoff reconnection timing:
With exponential backoff, retry delays double on each failure until reaching a maximum:
\[T_n = \min(T_0 \times 2^n, T_{max})\]
Where \(T_0 = 1\) second (initial delay), \(T_{max} = 60\) seconds, \(n\) = attempt number
This hands-on lab provides a comprehensive, production-ready ESP32 implementation demonstrating essential Wi-Fi IoT concepts. You will explore connection management, signal quality monitoring, power optimization, HTTP communication, and service discovery in a single integrated application.
47.3.1 What You Will Learn
In this lab, you will gain practical experience with:
Robust Wi-Fi Connection Management: Implement connection handling with automatic reconnection, exponential backoff, and connection state monitoring
Signal Quality Assessment: Monitor RSSI values, calculate signal quality percentages, and implement signal strength alerts
Power Management Modes: Configure and switch between modem sleep, light sleep, and active modes based on application requirements
HTTP/HTTPS Client Communication: Perform GET and POST requests to REST APIs with proper error handling and timeout management
mDNS Service Discovery: Discover and advertise network services without hardcoded IP addresses
Event-Driven Architecture: Use ESP32’s Wi-Fi event system for responsive connection handling
47.3.2 Lab Simulation
Interactive Wi-Fi IoT Lab
What This Simulates: A comprehensive ESP32 Wi-Fi implementation demonstrating connection management, RSSI monitoring, power modes, HTTP communication, and mDNS service discovery.
How to Use:
Click Start Simulation to launch the ESP32
Watch the Serial Monitor for connection status, RSSI readings, and power mode transitions
Observe HTTP requests being made to simulated endpoints
See mDNS service registration and discovery
Modify the code to experiment with different configurations
Copy the code below into the Wokwi editor to run the complete lab:
47.3.3 Complete Lab Code
This comprehensive sketch (~900 lines) implements five production-ready Wi-Fi patterns. The key architectural sections are:
Section
Lines
Purpose
Configuration
~50
SSID, timeouts, RSSI thresholds
State Management
~50
Connection state machine, power modes, statistics struct
Utility Functions
~110
RSSI-to-quality conversion, signal assessment, stats reporting
Wi-Fi Event Handlers
~55
Connection/disconnection callbacks with auto-reconnect
Connection Management
~100
Connect with backoff, reconnection logic
RSSI Monitoring
~100
Periodic signal checks, rolling average, quality logging
HTTP Communication
~150
GET/POST requests with error handling and statistics
Power Management
~100
Modem sleep, light sleep, mode cycling
mDNS Discovery
~70
Service advertisement and discovery
Main Loop
~100
State machine orchestration
Expand Full Source Code (~900 lines)
/* * Wi-Fi IoT Lab: Comprehensive ESP32 Implementation * Demonstrates: connection management, RSSI monitoring, power modes, * HTTP communication, and mDNS discovery. */#include <WiFi.h>#include <HTTPClient.h>#include <ESPmDNS.h>#include <WiFiClient.h>#include <esp_wifi.h>#include <esp_sleep.h>// =============================================================================// CONFIGURATION SECTION// =============================================================================// Wi-Fi Credentials (in production, use secure provisioning)constchar* WIFI_SSID ="Wokwi-GUEST";constchar* WIFI_PASSWORD ="";// Device Configurationconstchar* DEVICE_NAME ="esp32-iot-lab";constchar* DEVICE_TYPE ="environmental-sensor";// HTTP Endpoints (simulated for lab purposes)constchar* HTTP_TEST_URL ="http://httpbin.org/get";constchar* HTTP_POST_URL ="http://httpbin.org/post";// Timing Configuration (milliseconds)constunsignedlong WIFI_CONNECT_TIMEOUT =30000;constunsignedlong RECONNECT_INITIAL_DELAY =1000;constunsignedlong RECONNECT_MAX_DELAY =60000;constunsignedlong RSSI_CHECK_INTERVAL =5000;constunsignedlong HTTP_REQUEST_INTERVAL =30000;constunsignedlong POWER_MODE_CYCLE_INTERVAL =60000;// RSSI Thresholds (dBm)constint RSSI_EXCELLENT =-50;constint RSSI_GOOD =-60;constint RSSI_FAIR =-70;constint RSSI_WEAK =-80;constint RSSI_CRITICAL =-85;// =============================================================================// STATE MANAGEMENT// =============================================================================// Connection State Machineenum WiFiState { WIFI_STATE_DISCONNECTED, WIFI_STATE_CONNECTING, WIFI_STATE_CONNECTED, WIFI_STATE_RECONNECTING, WIFI_STATE_ERROR};// Power Management Modesenum PowerMode { POWER_MODE_ACTIVE, POWER_MODE_MODEM_SLEEP, POWER_MODE_LIGHT_SLEEP};// Global State VariablesWiFiState currentWiFiState = WIFI_STATE_DISCONNECTED;PowerMode currentPowerMode = POWER_MODE_ACTIVE;unsignedlong reconnectDelay = RECONNECT_INITIAL_DELAY;unsignedlong lastReconnectAttempt =0;unsignedlong lastRssiCheck =0;unsignedlong lastHttpRequest =0;unsignedlong lastPowerModeCycle =0;int connectionAttempts =0;int totalReconnections =0;bool mDNSStarted =false;// Statistics Trackingstruct WiFiStatistics {unsignedlong totalConnectedTime;unsignedlong totalDisconnectedTime;unsignedlong connectionStartTime;int rssiReadings[10];int rssiReadingIndex;int minRssi;int maxRssi;float avgRssi;int httpSuccessCount;int httpFailureCount;int reconnectionCount;} stats;// =============================================================================// UTILITY FUNCTIONS// =============================================================================/** * Convert RSSI value to signal quality percentage * @paramrssi Signal strength in dBm * @return Quality percentage (0-100) */int rssiToQuality(int rssi){if(rssi >=-50)return100;if(rssi >=-60)return80+(rssi +60)*2;if(rssi >=-70)return60+(rssi +70)*2;if(rssi >=-80)return40+(rssi +80)*2;if(rssi >=-90)return20+(rssi +90)*2;return max(0,10+(rssi +100));}/** * Get human-readable signal quality description * @paramrssi Signal strength in dBm * @return Quality description string */constchar* getSignalQualityDescription(int rssi){if(rssi >= RSSI_EXCELLENT)return"Excellent";if(rssi >= RSSI_GOOD)return"Good";if(rssi >= RSSI_FAIR)return"Fair";if(rssi >= RSSI_WEAK)return"Weak";if(rssi >= RSSI_CRITICAL)return"Critical";return"Unusable";}/** * Get Wi-Fi state as string * @paramstate Current Wi-Fi state * @return State description string */constchar* getWiFiStateString(WiFiState state){switch(state){case WIFI_STATE_DISCONNECTED:return"DISCONNECTED";case WIFI_STATE_CONNECTING:return"CONNECTING";case WIFI_STATE_CONNECTED:return"CONNECTED";case WIFI_STATE_RECONNECTING:return"RECONNECTING";case WIFI_STATE_ERROR:return"ERROR";default:return"UNKNOWN";}}/** * Get power mode as string * @parammode Current power mode * @return Mode description string */constchar* getPowerModeString(PowerMode mode){switch(mode){case POWER_MODE_ACTIVE:return"ACTIVE";case POWER_MODE_MODEM_SLEEP:return"MODEM_SLEEP";case POWER_MODE_LIGHT_SLEEP:return"LIGHT_SLEEP";default:return"UNKNOWN";}}/** * Print formatted timestamp */void printTimestamp(){unsignedlong seconds = millis()/1000;unsignedlong minutes = seconds /60;unsignedlong hours = minutes /60; Serial.printf("[%02lu:%02lu:%02lu] ", hours %24, minutes %60, seconds %60);}/** * Update RSSI statistics * @paramrssi Current RSSI reading */void updateRssiStats(int rssi){ stats.rssiReadings[stats.rssiReadingIndex]= rssi; stats.rssiReadingIndex =(stats.rssiReadingIndex +1)%10;if(rssi < stats.minRssi) stats.minRssi = rssi;if(rssi > stats.maxRssi) stats.maxRssi = rssi;// Calculate rolling averagefloat sum =0;int count =0;for(int i =0; i <10; i++){if(stats.rssiReadings[i]!=0){ sum += stats.rssiReadings[i]; count++;}}if(count >0) stats.avgRssi = sum / count;}/** * Initialize statistics tracking */void initStats(){ stats.totalConnectedTime =0; stats.totalDisconnectedTime =0; stats.connectionStartTime =0; stats.rssiReadingIndex =0; stats.minRssi =0; stats.maxRssi =-100; stats.avgRssi =0; stats.httpSuccessCount =0; stats.httpFailureCount =0; stats.reconnectionCount =0;for(int i =0; i <10; i++) stats.rssiReadings[i]=0;}// =============================================================================// WIFI EVENT HANDLERS// =============================================================================/** * Wi-Fi event callback handler * Processes connection state changes and updates state machine */void onWiFiEvent(WiFiEvent_t event){ printTimestamp();switch(event){case ARDUINO_EVENT_WIFI_STA_START: Serial.println("Wi-Fi: Station mode started");break;case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.println("Wi-Fi: Connected to access point"); currentWiFiState = WIFI_STATE_CONNECTING;// Still waiting for IPbreak;case ARDUINO_EVENT_WIFI_STA_GOT_IP: Serial.print("Wi-Fi: Got IP address - "); Serial.println(WiFi.localIP()); currentWiFiState = WIFI_STATE_CONNECTED; reconnectDelay = RECONNECT_INITIAL_DELAY;// Reset backoff connectionAttempts =0; stats.connectionStartTime = millis();// Start mDNS after getting IPif(!mDNSStarted){ setupMDNS();}break;case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: Serial.println("Wi-Fi: Disconnected from access point");if(currentWiFiState == WIFI_STATE_CONNECTED){// Update connected time statistics stats.totalConnectedTime += millis()- stats.connectionStartTime; stats.reconnectionCount++;} currentWiFiState = WIFI_STATE_DISCONNECTED; mDNSStarted =false;break;case ARDUINO_EVENT_WIFI_STA_LOST_IP: Serial.println("Wi-Fi: Lost IP address"); currentWiFiState = WIFI_STATE_DISCONNECTED;break;default: Serial.printf("Wi-Fi: Event %d\n", event);break;}}// =============================================================================// WIFI CONNECTION MANAGEMENT// =============================================================================/** * Initialize Wi-Fi connection with robust error handling * @return true if connection initiated successfully */bool initWiFi(){ printTimestamp(); Serial.println("Wi-Fi: Initializing connection...");// Disconnect any existing connection WiFi.disconnect(true); delay(100);// Set Wi-Fi mode to Station WiFi.mode(WIFI_STA);// Register event handler WiFi.onEvent(onWiFiEvent);// Configure Wi-Fi for performance/power balance WiFi.setAutoReconnect(false);// We handle reconnection manually WiFi.persistent(false);// Don't save to NVS (battery saving)// Set transmit power (adjust based on distance to AP) WiFi.setTxPower(WIFI_POWER_19_5dBm);// Full power for reliable connection// Start connection currentWiFiState = WIFI_STATE_CONNECTING; connectionAttempts++; Serial.printf("Wi-Fi: Connecting to '%s' (attempt %d)...\n", WIFI_SSID, connectionAttempts); WiFi.begin(WIFI_SSID, WIFI_PASSWORD);returntrue;}/** * Check Wi-Fi connection status and handle reconnection * Implements exponential backoff for reconnection attempts */void handleWiFiConnection(){unsignedlong currentTime = millis();switch(currentWiFiState){case WIFI_STATE_DISCONNECTED:case WIFI_STATE_RECONNECTING:// Check if it's time for a reconnection attemptif(currentTime - lastReconnectAttempt >= reconnectDelay){ lastReconnectAttempt = currentTime; printTimestamp(); Serial.printf("Wi-Fi: Reconnection attempt (delay: %lu ms)\n", reconnectDelay);// Attempt reconnection WiFi.disconnect(true); delay(100); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); currentWiFiState = WIFI_STATE_RECONNECTING; connectionAttempts++; totalReconnections++;// Exponential backoff with maximum limit reconnectDelay = min(reconnectDelay *2, RECONNECT_MAX_DELAY);}break;case WIFI_STATE_CONNECTING:// Check for connection timeoutif(WiFi.status()!= WL_CONNECTED){if(connectionAttempts >0&& currentTime - lastReconnectAttempt > WIFI_CONNECT_TIMEOUT){ printTimestamp(); Serial.println("Wi-Fi: Connection timeout, will retry..."); currentWiFiState = WIFI_STATE_DISCONNECTED;}}break;case WIFI_STATE_CONNECTED:// Verify connection is still validif(WiFi.status()!= WL_CONNECTED){ printTimestamp(); Serial.println("Wi-Fi: Connection lost unexpectedly"); currentWiFiState = WIFI_STATE_DISCONNECTED;}break;case WIFI_STATE_ERROR:// Attempt recovery after delayif(currentTime - lastReconnectAttempt >= RECONNECT_MAX_DELAY){ printTimestamp(); Serial.println("Wi-Fi: Attempting recovery from error state"); initWiFi();}break;}}// =============================================================================// RSSI MONITORING AND SIGNAL QUALITY// =============================================================================/** * Monitor and report Wi-Fi signal strength * Includes quality assessment and trend analysis */void monitorRSSI(){if(currentWiFiState != WIFI_STATE_CONNECTED)return;unsignedlong currentTime = millis();if(currentTime - lastRssiCheck < RSSI_CHECK_INTERVAL)return; lastRssiCheck = currentTime;int rssi = WiFi.RSSI();int quality = rssiToQuality(rssi);constchar* qualityDesc = getSignalQualityDescription(rssi); updateRssiStats(rssi); printTimestamp(); Serial.println("=== RSSI Report ==="); Serial.printf(" Current RSSI: %d dBm\n", rssi); Serial.printf(" Signal Quality: %d%% (%s)\n", quality, qualityDesc); Serial.printf(" Channel: %d\n", WiFi.channel()); Serial.printf(" BSSID: %s\n", WiFi.BSSIDstr().c_str());// Print statistics if we have enough dataif(stats.maxRssi >-100){ Serial.printf(" Min/Avg/Max RSSI: %d / %.1f / %d dBm\n", stats.minRssi, stats.avgRssi, stats.maxRssi);}// Signal strength warningsif(rssi < RSSI_CRITICAL){ Serial.println(" WARNING: Signal critically weak - connection may drop!");}elseif(rssi < RSSI_WEAK){ Serial.println(" NOTICE: Signal weak - consider moving device closer to AP");}// Visual signal bar Serial.print(" Signal Bars: ");int bars = map(constrain(rssi,-90,-30),-90,-30,0,5);for(int i =0; i <5; i++){ Serial.print(i < bars ?"|":".");} Serial.println(); Serial.println();}// =============================================================================// POWER MANAGEMENT// =============================================================================/** * Set Wi-Fi power management mode * @parammode Target power mode */void setPowerMode(PowerMode mode){ printTimestamp(); Serial.printf("Power: Switching from %s to %s\n", getPowerModeString(currentPowerMode), getPowerModeString(mode));switch(mode){case POWER_MODE_ACTIVE:// Maximum performance, highest power consumption WiFi.setSleep(false); esp_wifi_set_ps(WIFI_PS_NONE); Serial.println(" - Sleep disabled"); Serial.println(" - Maximum performance mode"); Serial.println(" - Typical current: 80-120 mA");break;case POWER_MODE_MODEM_SLEEP:// Radio turns off between DTIM beacons WiFi.setSleep(true); esp_wifi_set_ps(WIFI_PS_MIN_MODEM); Serial.println(" - Modem sleep enabled"); Serial.println(" - Radio sleeps between beacons"); Serial.println(" - Typical current: 15-25 mA");break;case POWER_MODE_LIGHT_SLEEP:// CPU can also sleep, wakes on Wi-Fi activity WiFi.setSleep(true); esp_wifi_set_ps(WIFI_PS_MAX_MODEM); Serial.println(" - Maximum power saving enabled"); Serial.println(" - CPU can enter light sleep"); Serial.println(" - Typical current: 0.8-3 mA"); Serial.println(" - Note: Increased latency expected");break;} currentPowerMode = mode; Serial.println();}/** * Demonstrate power mode cycling * Useful for understanding power consumption differences */void cyclePowerModes(){if(currentWiFiState != WIFI_STATE_CONNECTED)return;unsignedlong currentTime = millis();if(currentTime - lastPowerModeCycle < POWER_MODE_CYCLE_INTERVAL)return; lastPowerModeCycle = currentTime;// Cycle through power modes for demonstrationswitch(currentPowerMode){case POWER_MODE_ACTIVE: setPowerMode(POWER_MODE_MODEM_SLEEP);break;case POWER_MODE_MODEM_SLEEP: setPowerMode(POWER_MODE_LIGHT_SLEEP);break;case POWER_MODE_LIGHT_SLEEP: setPowerMode(POWER_MODE_ACTIVE);break;}}/** * Get current power consumption estimate * @return Estimated current in mA */float estimatePowerConsumption(){if(currentWiFiState != WIFI_STATE_CONNECTED){return0.01;// Deep sleep equivalent}switch(currentPowerMode){case POWER_MODE_ACTIVE:return100.0;// Average of TX/RXcase POWER_MODE_MODEM_SLEEP:return20.0;case POWER_MODE_LIGHT_SLEEP:return2.0;default:return50.0;}}/** * Calculate estimated battery life * @parambatteryCapacity_mAh Battery capacity in mAh * @return Estimated battery life in hours */float estimateBatteryLife(float batteryCapacity_mAh){float currentDraw = estimatePowerConsumption();if(currentDraw <=0)return0;return batteryCapacity_mAh / currentDraw;}// =============================================================================// HTTP CLIENT COMMUNICATION// =============================================================================/** * Perform HTTP GET request * @paramurl Target URL * @return Response code or negative error code */int performHttpGet(constchar* url){if(currentWiFiState != WIFI_STATE_CONNECTED){ printTimestamp(); Serial.println("HTTP GET: Cannot perform request - not connected");return-1;} printTimestamp(); Serial.printf("HTTP GET: Requesting %s\n", url); HTTPClient http; http.begin(url); http.setTimeout(10000);// 10 second timeout// Add headers http.addHeader("User-Agent","ESP32-IoT-Lab/1.0"); http.addHeader("Accept","application/json");unsignedlong startTime = millis();int httpCode = http.GET();unsignedlong duration = millis()- startTime;if(httpCode >0){ Serial.printf("HTTP GET: Response code %d (took %lu ms)\n", httpCode, duration);if(httpCode == HTTP_CODE_OK){ String payload = http.getString(); Serial.printf("HTTP GET: Response length: %d bytes\n", payload.length());// Print first 200 characters of responseif(payload.length()>200){ Serial.println("HTTP GET: Response preview (first 200 chars):"); Serial.println(payload.substring(0,200)+"...");}else{ Serial.println("HTTP GET: Response:"); Serial.println(payload);} stats.httpSuccessCount++;}}else{ Serial.printf("HTTP GET: Failed with error: %s\n", http.errorToString(httpCode).c_str()); stats.httpFailureCount++;} http.end(); Serial.println();return httpCode;}/** * Perform HTTP POST request with JSON payload * @paramurl Target URL * @paramjsonPayload JSON data to send * @return Response code or negative error code */int performHttpPost(constchar* url,const String& jsonPayload){if(currentWiFiState != WIFI_STATE_CONNECTED){ printTimestamp(); Serial.println("HTTP POST: Cannot perform request - not connected");return-1;} printTimestamp(); Serial.printf("HTTP POST: Sending to %s\n", url); Serial.printf("HTTP POST: Payload: %s\n", jsonPayload.c_str()); HTTPClient http; http.begin(url); http.setTimeout(10000);// Set content type for JSON http.addHeader("Content-Type","application/json"); http.addHeader("User-Agent","ESP32-IoT-Lab/1.0");unsignedlong startTime = millis();int httpCode = http.POST(jsonPayload);unsignedlong duration = millis()- startTime;if(httpCode >0){ Serial.printf("HTTP POST: Response code %d (took %lu ms)\n", httpCode, duration);if(httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_CREATED){ String response = http.getString(); Serial.printf("HTTP POST: Response length: %d bytes\n", response.length()); stats.httpSuccessCount++;}}else{ Serial.printf("HTTP POST: Failed with error: %s\n", http.errorToString(httpCode).c_str()); stats.httpFailureCount++;} http.end(); Serial.println();return httpCode;}/** * Send sensor data via HTTP POST * Demonstrates typical IoT data transmission pattern */void sendSensorData(){// Simulate sensor readingsfloat temperature =22.5+ random(-50,50)/10.0;float humidity =45.0+ random(-100,100)/10.0;int rssi = WiFi.RSSI();// Build JSON payload String payload ="{"; payload +="\"device\":\""+ String(DEVICE_NAME)+"\","; payload +="\"type\":\""+ String(DEVICE_TYPE)+"\","; payload +="\"temperature\":"+ String(temperature,1)+","; payload +="\"humidity\":"+ String(humidity,1)+","; payload +="\"rssi\":"+ String(rssi)+","; payload +="\"uptime\":"+ String(millis()/1000)+","; payload +="\"power_mode\":\""+ String(getPowerModeString(currentPowerMode))+"\""; payload +="}"; performHttpPost(HTTP_POST_URL, payload);}/** * Periodic HTTP operations handler */void handleHttpOperations(){if(currentWiFiState != WIFI_STATE_CONNECTED)return;unsignedlong currentTime = millis();if(currentTime - lastHttpRequest < HTTP_REQUEST_INTERVAL)return; lastHttpRequest = currentTime; printTimestamp(); Serial.println("=== HTTP Operations ===");// Alternate between GET and POST for demonstrationstaticbool doPost =false;if(doPost){ sendSensorData();}else{ performHttpGet(HTTP_TEST_URL);} doPost =!doPost;}// =============================================================================// MDNS SERVICE DISCOVERY// =============================================================================/** * Initialize mDNS service for local network discovery */void setupMDNS(){if(currentWiFiState != WIFI_STATE_CONNECTED)return; printTimestamp(); Serial.println("mDNS: Starting service...");// Start mDNS responderif(MDNS.begin(DEVICE_NAME)){ Serial.printf("mDNS: Hostname registered: %s.local\n", DEVICE_NAME);// Advertise HTTP service MDNS.addService("http","tcp",80); Serial.println("mDNS: HTTP service advertised on port 80");// Advertise custom IoT service MDNS.addService("iot-sensor","tcp",8080); MDNS.addServiceTxt("iot-sensor","tcp","type", DEVICE_TYPE); MDNS.addServiceTxt("iot-sensor","tcp","version","1.0"); Serial.println("mDNS: Custom IoT service advertised on port 8080"); mDNSStarted =true; Serial.printf("mDNS: Device discoverable at http://%s.local/\n", DEVICE_NAME);}else{ Serial.println("mDNS: Failed to start responder");} Serial.println();}/** * Discover services on the local network * @paramserviceType Service type to search for (e.g., "http", "mqtt") * @paramprotocol Protocol (usually "tcp") */void discoverServices(constchar* serviceType,constchar* protocol){if(currentWiFiState != WIFI_STATE_CONNECTED)return; printTimestamp(); Serial.printf("mDNS: Searching for _%s._%s services...\n", serviceType, protocol);int serviceCount = MDNS.queryService(serviceType, protocol);if(serviceCount ==0){ Serial.println("mDNS: No services found");}else{ Serial.printf("mDNS: Found %d service(s):\n", serviceCount);for(int i =0; i < serviceCount; i++){ Serial.printf(" %d. %s (%s:%d)\n", i +1, MDNS.hostname(i).c_str(), MDNS.IP(i).toString().c_str(), MDNS.port(i));}} Serial.println();}/** * Demonstrate mDNS discovery capabilities */void demonstrateMDNSDiscovery(){if(!mDNSStarted)return; printTimestamp(); Serial.println("=== mDNS Service Discovery Demo ===");// Search for common IoT services discoverServices("http","tcp"); discoverServices("mqtt","tcp"); discoverServices("iot-sensor","tcp");}// =============================================================================// STATUS REPORTING// =============================================================================/** * Print comprehensive system status */void printSystemStatus(){ printTimestamp(); Serial.println("========================================"); Serial.println(" Wi-Fi IoT Lab Status Report "); Serial.println("========================================");// Connection Status Serial.println("\n--- Connection Status ---"); Serial.printf(" State: %s\n", getWiFiStateString(currentWiFiState)); Serial.printf(" Wi-Fi Status: %d\n", WiFi.status());if(currentWiFiState == WIFI_STATE_CONNECTED){ Serial.printf(" SSID: %s\n", WiFi.SSID().c_str()); Serial.printf(" IP: %s\n", WiFi.localIP().toString().c_str()); Serial.printf(" RSSI: %d dBm (%s)\n", WiFi.RSSI(), getSignalQualityDescription(WiFi.RSSI())); Serial.printf(" Channel: %d\n", WiFi.channel());}// Power Status Serial.println("\n--- Power Management ---"); Serial.printf(" Mode: %s\n", getPowerModeString(currentPowerMode)); Serial.printf(" Est. Current: %.1f mA\n", estimatePowerConsumption()); Serial.printf(" Battery Life (2000mAh): %.1f hours\n", estimateBatteryLife(2000));// Statistics Serial.println("\n--- Session Statistics ---"); Serial.printf(" Uptime: %lu seconds\n", millis()/1000); Serial.printf(" Connection Attempts: %d\n", connectionAttempts); Serial.printf(" Total Reconnections: %d\n", stats.reconnectionCount); Serial.printf(" HTTP Success/Failure: %d/%d\n", stats.httpSuccessCount, stats.httpFailureCount);if(stats.maxRssi >-100){ Serial.printf(" RSSI Range: %d to %d dBm (avg: %.1f)\n", stats.minRssi, stats.maxRssi, stats.avgRssi);}// mDNS Status Serial.println("\n--- mDNS Service ---"); Serial.printf(" Status: %s\n", mDNSStarted ?"Active":"Inactive");if(mDNSStarted){ Serial.printf(" Hostname: %s.local\n", DEVICE_NAME);} Serial.println("\n========================================\n");}// =============================================================================// MAIN SETUP AND LOOP// =============================================================================void setup(){// Initialize serial communication Serial.begin(115200); delay(1000); Serial.println(); Serial.println("========================================"); Serial.println(" Wi-Fi IoT Lab - ESP32 Implementation "); Serial.println("========================================"); Serial.println(); Serial.println("This lab demonstrates:"); Serial.println(" - Robust Wi-Fi connection management"); Serial.println(" - RSSI monitoring and signal quality"); Serial.println(" - Power management modes"); Serial.println(" - HTTP/HTTPS client communication"); Serial.println(" - mDNS service discovery"); Serial.println(); Serial.println("Starting in 3 seconds..."); Serial.println(); delay(3000);// Initialize statistics initStats();// Initialize Wi-Fi connection initWiFi();// Set initial power mode setPowerMode(POWER_MODE_MODEM_SLEEP);// Record initial reconnect time lastReconnectAttempt = millis();}void loop(){// Handle Wi-Fi connection state handleWiFiConnection();// Only perform operations when connectedif(currentWiFiState == WIFI_STATE_CONNECTED){// Monitor RSSI periodically monitorRSSI();// Handle HTTP operations handleHttpOperations();// Cycle through power modes for demonstration cyclePowerModes();}// Print status report every 2 minutesstaticunsignedlong lastStatusReport =0;if(millis()- lastStatusReport >=120000){ lastStatusReport = millis(); printSystemStatus();// Demonstrate mDNS discoveryif(mDNSStarted){ demonstrateMDNSDiscovery();}}// Small delay to prevent busy-waiting delay(100);}// =============================================================================// END OF WIFI IOT LAB// =============================================================================
47.3.4 Challenge Exercises
After running the basic lab, try these challenges to deepen your understanding:
Challenge 1: Connection Quality Threshold
Objective: Implement automatic power mode switching based on signal quality.
Task: Modify the code to:
Switch to POWER_MODE_ACTIVE when RSSI drops below -75 dBm (weak signal needs more reliable transmission)
Switch to POWER_MODE_LIGHT_SLEEP when RSSI is above -50 dBm (strong signal allows power saving)
Add hysteresis to prevent rapid switching (use a threshold band of 5 dBm)
Hint: Create a new function adaptivePowerManagement() that checks RSSI and adjusts power mode accordingly.
Challenge 2: Multi-Network Failover
Objective: Implement automatic failover to backup Wi-Fi networks.
Task: Extend the code to:
Store credentials for 3 different Wi-Fi networks
Attempt connection to each network in order when disconnected
Track which network provides best signal quality
Automatically prefer the strongest network
Hint: Use an array of structs to store SSID/password pairs with priority ranking.
Challenge 3: Data Batching for Power Efficiency
Objective: Reduce power consumption by batching sensor readings.
Task: Modify the HTTP posting logic to:
Collect 10 sensor readings before transmitting
Store readings in a circular buffer
Send all readings in a single HTTP POST as JSON array
Calculate and display the power savings compared to individual transmissions
Hint: Estimate power savings: saved_energy = (n-1) * connection_overhead_energy where n is batch size.
Challenge 4: mDNS Service Consumer
Objective: Discover and connect to an MQTT broker via mDNS.
Task: Extend the mDNS functionality to:
Search for _mqtt._tcp services on the network
Automatically connect to discovered MQTT broker
Subscribe to a topic and publish sensor data
Handle broker unavailability gracefully
Hint: Use the MDNS.queryService() result to get broker IP and port dynamically.
Challenge 5: Deep Sleep Integration
Objective: Implement ultra-low-power operation with deep sleep.
Task: Create a variant that:
Wakes from deep sleep every 5 minutes
Connects to Wi-Fi, sends data, disconnects
Uses RTC memory to persist statistics across sleep cycles
Estimates and displays actual battery life based on measured active time
Hint: Use RTC_DATA_ATTR to preserve variables across deep sleep cycles.
47.3.5 Expected Outcomes
After completing this lab, you should observe:
Behavior
Expected Output
Connection Sequence
Wi-Fi connects within 5-10 seconds, IP address assigned via DHCP
RSSI Monitoring
Signal strength updates every 5 seconds with quality assessment
Power Mode Cycling
Transitions between Active, Modem Sleep, and Light Sleep modes
HTTP Requests
GET/POST operations complete with response codes and timing
mDNS Discovery
Device discoverable at esp32-iot-lab.local, services advertised
Reconnection
Automatic reconnection with exponential backoff if connection drops
Statistics
Running totals of connection events, HTTP success/failure rates
47.3.6 Key Learning Points
Event-Driven Architecture: Using WiFi.onEvent() provides responsive connection state handling without polling
Exponential Backoff: The reconnection delay doubles each attempt (1s, 2s, 4s, 8s…) up to a maximum, reducing network congestion during outages
Modem Sleep: ~20 mA, some latency increase, good balance
Light Sleep: ~2 mA, higher latency, best for battery devices
mDNS Benefits: Eliminates hardcoded IP addresses, enables dynamic service discovery, simplifies IoT network management
HTTP Best Practices: Always set timeouts, use appropriate headers, handle errors gracefully, consider payload size impact on power
47.3.7 Troubleshooting Guide
Issue
Possible Cause
Solution
Connection timeout
Wrong credentials or AP not in range
Verify SSID/password, check AP status
RSSI shows -127 dBm
Not connected or driver issue
Ensure connected before reading RSSI
HTTP requests fail
No internet or firewall blocking
Check gateway connectivity, try local endpoint
mDNS not working
mDNS disabled on network
Check router settings, some networks block mDNS
High power consumption
Sleep mode not activating
Verify WiFi.setSleep(true) is called
Frequent reconnections
Weak signal or interference
Move closer to AP, change Wi-Fi channel
Worked Example: Adaptive Power Management Based on Signal Quality
Scenario: An ESP32 environmental sensor monitors air quality in a mobile robot warehouse. As the robot moves, signal strength varies from -45 dBm (near AP) to -82 dBm (far corner). Implement adaptive power management that balances reliability and battery life.
Given:
Robot moves through warehouse on predictable routes
Battery: 5000 mAh lithium (rechargeable, but optimize to extend shift length)
RSSI range: -45 dBm (excellent) to -82 dBm (critical)
Power modes:
Active mode: 100 mA (maximum reliability, no sleep)
Modem sleep: 20 mA (radio sleeps between beacons)
Light sleep: 2 mA (CPU can sleep, wakes on Wi-Fi)
Data transmission: 200-byte payload every 10 seconds
Design Goals:
Maintain <1% packet loss across all locations
Maximize battery life without sacrificing reliability
Adapt power mode based on real-time signal quality
Implementation – The adaptive power manager uses three key components:
Rolling RSSI average (10-sample buffer) to smooth noisy signal readings
Hysteresis band (5 dBm) to prevent rapid mode switching at thresholds
Minimum change interval (30 seconds) to prevent thrashing
Full Adaptive Power Manager (~100 lines, expand to view)
PowerMode determineOptimalMode(int avgRSSI, PowerMode current){if(avgRSSI >= RSSI_EXCELLENT)return MODE_LIGHT_SLEEP;if(avgRSSI >= RSSI_GOOD){// Hysteresis: don't jump to light sleep from active too eagerlyif(current == MODE_ACTIVE && avgRSSI < RSSI_EXCELLENT +5)return MODE_MODEM_SLEEP;return MODE_LIGHT_SLEEP;}if(avgRSSI >= RSSI_FAIR)return MODE_MODEM_SLEEP;return MODE_ACTIVE;// Weak/critical: maximum reliability}void adaptivePowerManagement(){// Update 10-sample rolling RSSI buffer rssiReadings[rssiIndex]= WiFi.RSSI(); rssiIndex =(rssiIndex +1)%10;int avgRSSI = getAverageRSSI(); PowerMode optimal = determineOptimalMode(avgRSSI, currentMode);// Only switch if mode differs AND 30s since last changeif(optimal != currentMode &&(millis()- lastModeChange)>=30000){ currentMode = optimal; applyPowerMode(currentMode); lastModeChange = millis();}}void loop(){ adaptivePowerManagement(); sendAirQualityData(); delay(10000);}
Measured Results:
Robot Location
RSSI
Power Mode
Packet Loss
Current Draw
Notes
Near AP (Charging)
-47 dBm
Light Sleep
0%
2 mA
50x battery savings vs active
Main Aisle
-62 dBm
Light Sleep
0.1%
2 mA
Still excellent signal
Side Corridor
-73 dBm
Modem Sleep
0.3%
20 mA
Fair signal, moderate power
Far Corner
-78 dBm
Active
0.8%
100 mA
Weak signal, max reliability
Dead Zone Edge
-83 dBm
Active
2.5%
100 mA
Critical, needs AP addition
Energy Savings Calculation:
Typical 8-hour shift route distribution: - 40% near AP (-50 dBm avg): 3.2 hours @ 2 mA = 6.4 mAh - 35% main aisles (-65 dBm avg): 2.8 hours @ 2 mA = 5.6 mAh - 20% side areas (-73 dBm avg): 1.6 hours @ 20 mA = 32 mAh - 5% far corners (-78 dBm avg): 0.4 hours @ 100 mA = 40 mAh - Total: 84 mAh per 8-hour shift
Without adaptive power management (always active): - 8 hours @ 100 mA = 800 mAh per shift
Battery life improvement: 9.5x (800 ÷ 84)
Shifts per charge: 5000 mAh ÷ 84 mAh = 59 shifts (12 weeks between charges)
Key Features:
Rolling Average RSSI: Prevents rapid mode switching from noise spikes
Hysteresis Band: 5 dBm deadband prevents oscillation at threshold boundaries
Key Insight: Adaptive power management provides 10x battery savings by matching power mode to signal conditions. The critical design elements are: (1) rolling average to smooth RSSI noise, (2) hysteresis to prevent mode oscillation, (3) minimum change interval to limit transition overhead. For mobile IoT, this “sense-and-adapt” pattern is essential - static power configurations waste energy in strong-signal areas and fail in weak-signal zones. The 30-second mode change interval ensures the device spends enough time in each mode to validate the decision before switching again.
How It Works: Event-Driven Wi-Fi Connection Management
ESP32’s Wi-Fi stack operates asynchronously using events instead of blocking function calls. When you call WiFi.begin(), the connection happens in the background while your code continues running. The ESP32 firmware generates events like SYSTEM_EVENT_STA_GOT_IP (connected), SYSTEM_EVENT_STA_DISCONNECTED (lost connection), and SYSTEM_EVENT_SCAN_DONE (scan complete).
You register event handlers - callback functions that execute when specific events occur. When Wi-Fi disconnects, your handler sets a flag, starts a reconnection timer with exponential backoff, and allows the main loop to continue serving cached data or entering low-power mode.
This non-blocking architecture prevents Wi-Fi issues from freezing your entire device. A blocking WiFi.waitForConnectResult() would stall sensor readings and user interactions during outages. Event-driven design keeps the device responsive: if Wi-Fi is down, it logs locally and retries in the background; if Wi-Fi returns, it flushes buffered data. This pattern is essential for production IoT devices operating in unreliable network conditions.
Concept Relationships
Concept
Relates To
Why It Matters
Event-Driven Architecture
Async callbacks, Non-blocking I/O
Prevents Wi-Fi operations from freezing the device
1. Not Implementing Wi-Fi Reconnection With Backoff
IoT devices that immediately retry Wi-Fi connection after disconnection can create connection storms when an AP reboots with many devices simultaneously reconnecting. Implement exponential backoff with jitter — wait 1s, then 2s, then 4s up to a maximum of 60s between reconnection attempts.
2. Storing Wi-Fi Credentials in Plain Text
Hard-coding Wi-Fi credentials in firmware or storing them in unencrypted flash allows anyone with physical access to the device to extract credentials. Use NVS (Non-Volatile Storage) with flash encryption enabled, or implement Wi-Fi provisioning via a secure channel.
3. Ignoring TCP Keep-Alive for Long-Idle Connections
MQTT and HTTP connections can be silently dropped by NAT routers after idle periods of 5-60 minutes. Without TCP keep-alive or MQTT ping, the client thinks it is connected while the broker has already dropped the session. Configure TCP keep-alive and MQTT keep-alive intervals shorter than expected NAT timeout.
4. Not Validating TLS Certificates in Production
Development IoT code often disables certificate validation for simplicity. Deploying with certificate validation disabled enables man-in-the-middle attacks where attackers intercept credentials and sensor data. Always enable and verify TLS certificates in production firmware.
🏷️ Label the Diagram
💻 Code Challenge
47.7 Summary
This comprehensive lab demonstrated production-ready Wi-Fi IoT implementation patterns:
Robust Connection Management: Event-driven architecture with automatic reconnection and exponential backoff
Signal Quality Monitoring: RSSI tracking, quality assessment, and signal strength alerts
Power Management: Configuring modem sleep, light sleep, and active modes for power optimization
HTTP Communication: GET/POST requests with proper error handling and timeout management
mDNS Service Discovery: Advertising and discovering network services without hardcoded IPs