47  Wi-Fi Comprehensive Lab

In 60 Seconds

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.

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

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

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

  • Attempt 1: \(\min(1 \times 2^0, 60) = 1\ \text{s}\)
  • Attempt 2: \(\min(1 \times 2^1, 60) = 2\ \text{s}\)
  • Attempt 3: \(\min(1 \times 2^2, 60) = 4\ \text{s}\)
  • Attempt 4: \(\min(1 \times 2^3, 60) = 8\ \text{s}\)
  • Attempt 5: \(\min(1 \times 2^4, 60) = 16\ \text{s}\)
  • Attempt 6: \(\min(1 \times 2^5, 60) = 32\ \text{s}\)
  • Attempt 7+: \(\min(1 \times 2^6, 60) = 60\ \text{s}\) (capped at max)

Total reconnection time for 10 failed attempts: \[\sum_{n=0}^{9} \min(2^n, 60) = 1 + 2 + 4 + 8 + 16 + 32 + 60 \times 4 = 303\ \text{seconds} \approx 5\ \text{minutes}\]

Without backoff (1s fixed retry): \(10 \times 1 = 10\ \text{s}\) (30× faster but floods network)

47.2 Prerequisites

Before starting this lab, you should be familiar with:

Key Concepts

  • ESP32 Wi-Fi Stack: Built-in 802.11b/g/n Wi-Fi in ESP32 with STA, AP, and STA+AP modes; supports WPA2 and WPA3
  • Wi-Fi Event Handler: ESP-IDF event-driven model for handling Wi-Fi events (connected, disconnected, got IP) asynchronously
  • MQTT over Wi-Fi: Message Queuing Telemetry Transport protocol for IoT cloud communication; uses TCP port 1883 (or 8883 for TLS)
  • HTTPS/TLS for IoT: Encrypted HTTP communication with certificate verification; essential for secure cloud API communication
  • Modem Sleep Mode: ESP32 power saving mode where Wi-Fi radio sleeps between DTIM beacons; reduces power without disconnecting
  • Light Sleep Mode: Deeper ESP32 sleep maintaining Wi-Fi connection via power save mode; higher power savings than modem sleep
  • Wi-Fi Provisioning: Method for configuring SSID/password in a new IoT device (AP mode, Bluetooth provisioning, SmartConfig)
  • Reconnection Logic: Exponential backoff reconnection strategy for handling Wi-Fi disconnections in deployed IoT devices

47.3 Wi-Fi IoT Lab: Comprehensive ESP32 Implementation

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:

  1. Robust Wi-Fi Connection Management: Implement connection handling with automatic reconnection, exponential backoff, and connection state monitoring
  2. Signal Quality Assessment: Monitor RSSI values, calculate signal quality percentages, and implement signal strength alerts
  3. Power Management Modes: Configure and switch between modem sleep, light sleep, and active modes based on application requirements
  4. HTTP/HTTPS Client Communication: Perform GET and POST requests to REST APIs with proper error handling and timeout management
  5. mDNS Service Discovery: Discover and advertise network services without hardcoded IP addresses
  6. 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:

  1. Click Start Simulation to launch the ESP32
  2. Watch the Serial Monitor for connection status, RSSI readings, and power mode transitions
  3. Observe HTTP requests being made to simulated endpoints
  4. See mDNS service registration and discovery
  5. 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
/*
 * 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)
const char* WIFI_SSID = "Wokwi-GUEST";
const char* WIFI_PASSWORD = "";

// Device Configuration
const char* DEVICE_NAME = "esp32-iot-lab";
const char* DEVICE_TYPE = "environmental-sensor";

// HTTP Endpoints (simulated for lab purposes)
const char* HTTP_TEST_URL = "http://httpbin.org/get";
const char* HTTP_POST_URL = "http://httpbin.org/post";

// Timing Configuration (milliseconds)
const unsigned long WIFI_CONNECT_TIMEOUT = 30000;
const unsigned long RECONNECT_INITIAL_DELAY = 1000;
const unsigned long RECONNECT_MAX_DELAY = 60000;
const unsigned long RSSI_CHECK_INTERVAL = 5000;
const unsigned long HTTP_REQUEST_INTERVAL = 30000;
const unsigned long POWER_MODE_CYCLE_INTERVAL = 60000;

// RSSI Thresholds (dBm)
const int RSSI_EXCELLENT = -50;
const int RSSI_GOOD = -60;
const int RSSI_FAIR = -70;
const int RSSI_WEAK = -80;
const int RSSI_CRITICAL = -85;

// =============================================================================
// STATE MANAGEMENT
// =============================================================================

// Connection State Machine
enum WiFiState {
  WIFI_STATE_DISCONNECTED,
  WIFI_STATE_CONNECTING,
  WIFI_STATE_CONNECTED,
  WIFI_STATE_RECONNECTING,
  WIFI_STATE_ERROR
};

// Power Management Modes
enum PowerMode {
  POWER_MODE_ACTIVE,
  POWER_MODE_MODEM_SLEEP,
  POWER_MODE_LIGHT_SLEEP
};

// Global State Variables
WiFiState currentWiFiState = WIFI_STATE_DISCONNECTED;
PowerMode currentPowerMode = POWER_MODE_ACTIVE;
unsigned long reconnectDelay = RECONNECT_INITIAL_DELAY;
unsigned long lastReconnectAttempt = 0;
unsigned long lastRssiCheck = 0;
unsigned long lastHttpRequest = 0;
unsigned long lastPowerModeCycle = 0;
int connectionAttempts = 0;
int totalReconnections = 0;
bool mDNSStarted = false;

// Statistics Tracking
struct WiFiStatistics {
  unsigned long totalConnectedTime;
  unsigned long totalDisconnectedTime;
  unsigned long 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
 * @param rssi Signal strength in dBm
 * @return Quality percentage (0-100)
 */
int rssiToQuality(int rssi) {
  if (rssi >= -50) return 100;
  if (rssi >= -60) return 80 + (rssi + 60) * 2;
  if (rssi >= -70) return 60 + (rssi + 70) * 2;
  if (rssi >= -80) return 40 + (rssi + 80) * 2;
  if (rssi >= -90) return 20 + (rssi + 90) * 2;
  return max(0, 10 + (rssi + 100));
}

/**
 * Get human-readable signal quality description
 * @param rssi Signal strength in dBm
 * @return Quality description string
 */
const char* 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
 * @param state Current Wi-Fi state
 * @return State description string
 */
const char* 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
 * @param mode Current power mode
 * @return Mode description string
 */
const char* 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() {
  unsigned long seconds = millis() / 1000;
  unsigned long minutes = seconds / 60;
  unsigned long hours = minutes / 60;
  Serial.printf("[%02lu:%02lu:%02lu] ", hours % 24, minutes % 60, seconds % 60);
}

/**
 * Update RSSI statistics
 * @param rssi 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 average
  float 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 IP
      break;

    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 IP
      if (!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);

  return true;
}

/**
 * Check Wi-Fi connection status and handle reconnection
 * Implements exponential backoff for reconnection attempts
 */
void handleWiFiConnection() {
  unsigned long currentTime = millis();

  switch (currentWiFiState) {
    case WIFI_STATE_DISCONNECTED:
    case WIFI_STATE_RECONNECTING:
      // Check if it's time for a reconnection attempt
      if (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 timeout
      if (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 valid
      if (WiFi.status() != WL_CONNECTED) {
        printTimestamp();
        Serial.println("Wi-Fi: Connection lost unexpectedly");
        currentWiFiState = WIFI_STATE_DISCONNECTED;
      }
      break;

    case WIFI_STATE_ERROR:
      // Attempt recovery after delay
      if (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;

  unsigned long currentTime = millis();
  if (currentTime - lastRssiCheck < RSSI_CHECK_INTERVAL) return;
  lastRssiCheck = currentTime;

  int rssi = WiFi.RSSI();
  int quality = rssiToQuality(rssi);
  const char* 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 data
  if (stats.maxRssi > -100) {
    Serial.printf("  Min/Avg/Max RSSI: %d / %.1f / %d dBm\n",
                  stats.minRssi, stats.avgRssi, stats.maxRssi);
  }

  // Signal strength warnings
  if (rssi < RSSI_CRITICAL) {
    Serial.println("  WARNING: Signal critically weak - connection may drop!");
  } else if (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
 * @param mode 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;

  unsigned long currentTime = millis();
  if (currentTime - lastPowerModeCycle < POWER_MODE_CYCLE_INTERVAL) return;
  lastPowerModeCycle = currentTime;

  // Cycle through power modes for demonstration
  switch (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) {
    return 0.01; // Deep sleep equivalent
  }

  switch (currentPowerMode) {
    case POWER_MODE_ACTIVE:
      return 100.0; // Average of TX/RX
    case POWER_MODE_MODEM_SLEEP:
      return 20.0;
    case POWER_MODE_LIGHT_SLEEP:
      return 2.0;
    default:
      return 50.0;
  }
}

/**
 * Calculate estimated battery life
 * @param batteryCapacity_mAh Battery capacity in mAh
 * @return Estimated battery life in hours
 */
float estimateBatteryLife(float batteryCapacity_mAh) {
  float currentDraw = estimatePowerConsumption();
  if (currentDraw <= 0) return 0;
  return batteryCapacity_mAh / currentDraw;
}

// =============================================================================
// HTTP CLIENT COMMUNICATION
// =============================================================================

/**
 * Perform HTTP GET request
 * @param url Target URL
 * @return Response code or negative error code
 */
int performHttpGet(const char* 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");

  unsigned long startTime = millis();
  int httpCode = http.GET();
  unsigned long 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 response
      if (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
 * @param url Target URL
 * @param jsonPayload JSON data to send
 * @return Response code or negative error code
 */
int performHttpPost(const char* 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");

  unsigned long startTime = millis();
  int httpCode = http.POST(jsonPayload);
  unsigned long 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 readings
  float 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;

  unsigned long currentTime = millis();
  if (currentTime - lastHttpRequest < HTTP_REQUEST_INTERVAL) return;
  lastHttpRequest = currentTime;

  printTimestamp();
  Serial.println("=== HTTP Operations ===");

  // Alternate between GET and POST for demonstration
  static bool 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 responder
  if (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
 * @param serviceType Service type to search for (e.g., "http", "mqtt")
 * @param protocol Protocol (usually "tcp")
 */
void discoverServices(const char* serviceType, const char* 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 connected
  if (currentWiFiState == WIFI_STATE_CONNECTED) {
    // Monitor RSSI periodically
    monitorRSSI();

    // Handle HTTP operations
    handleHttpOperations();

    // Cycle through power modes for demonstration
    cyclePowerModes();
  }

  // Print status report every 2 minutes
  static unsigned long lastStatusReport = 0;
  if (millis() - lastStatusReport >= 120000) {
    lastStatusReport = millis();
    printSystemStatus();

    // Demonstrate mDNS discovery
    if (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

  1. Event-Driven Architecture: Using WiFi.onEvent() provides responsive connection state handling without polling

  2. Exponential Backoff: The reconnection delay doubles each attempt (1s, 2s, 4s, 8s…) up to a maximum, reducing network congestion during outages

  3. Power Mode Trade-offs:

    • Active: ~100 mA, lowest latency, highest reliability
    • Modem Sleep: ~20 mA, some latency increase, good balance
    • Light Sleep: ~2 mA, higher latency, best for battery devices
  4. mDNS Benefits: Eliminates hardcoded IP addresses, enables dynamic service discovery, simplifies IoT network management

  5. 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

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:

  1. Maintain <1% packet loss across all locations
  2. Maximize battery life without sacrificing reliability
  3. Adapt power mode based on real-time signal quality

Implementation – The adaptive power manager uses three key components:

  1. Rolling RSSI average (10-sample buffer) to smooth noisy signal readings
  2. Hysteresis band (5 dBm) to prevent rapid mode switching at thresholds
  3. Minimum change interval (30 seconds) to prevent thrashing
// RSSI thresholds determine power mode selection
const int RSSI_EXCELLENT = -50;  // -> Light Sleep (2 mA)
const int RSSI_GOOD = -60;      // -> Light Sleep with hysteresis
const int RSSI_FAIR = -70;      // -> Modem Sleep (20 mA)
const int RSSI_WEAK = -80;      // -> Active mode (100 mA)

enum PowerMode { MODE_ACTIVE, MODE_MODEM_SLEEP, MODE_LIGHT_SLEEP };

void applyPowerMode(PowerMode mode) {
  switch (mode) {
    case MODE_ACTIVE:      esp_wifi_set_ps(WIFI_PS_NONE);      break;
    case MODE_MODEM_SLEEP: esp_wifi_set_ps(WIFI_PS_MIN_MODEM); break;
    case MODE_LIGHT_SLEEP: esp_wifi_set_ps(WIFI_PS_MAX_MODEM); break;
  }
}
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 eagerly
    if (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 change
  if (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:

  1. Rolling Average RSSI: Prevents rapid mode switching from noise spikes
  2. Hysteresis Band: 5 dBm deadband prevents oscillation at threshold boundaries
  3. Minimum Mode Change Interval: 30-second cooldown prevents thrashing
  4. Mode Transition Logic: Gradual step-down (Active → Modem → Light) prevents abrupt reliability changes
  5. Logging and Monitoring: Tracks mode changes for debugging and optimization

Advanced Optimization: Predictive Power Management

If robot follows predictable routes, use historical RSSI data:

struct LocationProfile {
  float latitude;
  float longitude;
  int avgRSSI;
  PowerMode recommendedMode;
};

LocationProfile locationHistory[100];

PowerMode predictOptimalMode(float lat, float lon) {
  // Find nearest historical location
  int nearestIndex = findNearestLocation(lat, lon);
  if (nearestIndex >= 0) {
    return locationHistory[nearestIndex].recommendedMode;
  }
  // Fallback to reactive mode
  return determineOptimalMode(WiFi.RSSI(), currentMode);
}

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.

47.4 Interactive: ESP32 Power Mode Calculator

47.5 Resources

47.5.1 Internal Resources

47.5.2 Tools and Utilities

  • Wi-Fi Analyzer (Android/iOS) - Channel analysis
  • Wireshark - Packet capture and analysis
  • Fing - Network scanner, device discovery
  • iPerf - Network speed testing
  • ESP32 Provisioning App - Wi-Fi setup for ESP32

47.5.3 Hardware Platforms

  • ESP32 - Common Wi-Fi SoC for IoT
  • ESP8266 - Low-cost Wi-Fi module
  • Raspberry Pi - Wi-Fi-enabled SBC
  • Arduino Wi-Fi - Arduino with onboard Wi-Fi
  • CC3200 - TI Wi-Fi microcontroller

47.5.4 Standards and Documentation

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 Relates To Why It Matters
Event-Driven Architecture Async callbacks, Non-blocking I/O Prevents Wi-Fi operations from freezing the device
Exponential Backoff Reconnection strategy, Network flooding prevention Reduces connection attempts from 180/min to 7/3min during outages
mDNS Service Discovery Zero-configuration networking, Local service discovery Devices find each other by name without DNS/DHCP config
RSSI Monitoring Signal quality, Roaming decisions, AP selection Triggers roaming before connection quality degrades
Connection Statistics Reliability metrics, Debugging, Uptime tracking Identifies patterns in disconnection events

47.6 See Also

Common Pitfalls

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.

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.

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.

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.

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
  • Statistics Tracking: Monitoring connection events, HTTP success rates, and RSSI trends

47.8 Knowledge Check

47.9 What’s Next

If you want to… Read this
Review ESP32 Wi-Fi basics Wi-Fi Implementation: ESP32 Basics
Learn HTTP and WebSocket implementation Wi-Fi HTTP & WebSocket
Optimize power consumption Wi-Fi Power Optimization
Study security and provisioning Wi-Fi Security and Provisioning
Explore all Wi-Fi IoT implementations Wi-Fi for IoT: Implementations