849  Wi-Fi Implementation: Comprehensive Lab

849.1 Learning Objectives

By the end of this lab, you will be able to:

  • Build Production-Ready Wi-Fi Code: Implement robust connection management with exponential backoff
  • Monitor Signal Quality: Track RSSI values, calculate quality percentages, and implement signal alerts
  • Manage Power Modes: Configure and switch between modem sleep, light sleep, and active modes
  • Perform HTTP Communication: Execute GET and POST requests with proper error handling
  • Implement mDNS Discovery: Discover and advertise network services without hardcoded IPs
  • Use Event-Driven Architecture: Handle Wi-Fi events for responsive 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

849.2 Prerequisites

Before starting this lab, you should be familiar with:

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

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

849.3.2 Lab Simulation

TipInteractive 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:

849.3.3 Complete Lab Code

/*
 * =============================================================================
 * Wi-Fi IoT Lab: Comprehensive ESP32 Implementation
 * =============================================================================
 *
 * This lab demonstrates production-ready Wi-Fi IoT patterns including:
 * - Robust connection management with reconnection handling
 * - RSSI monitoring and signal quality assessment
 * - Power management modes (modem sleep, light sleep)
 * - HTTP/HTTPS client communication
 * - mDNS service discovery and advertisement
 *
 * Target Platform: ESP32 (ESP-IDF/Arduino framework)
 * Estimated Runtime: Continuous operation with periodic reporting
 *
 * Author: IoT Class Educational Series
 * License: Educational Use
 * =============================================================================
 */

#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
// =============================================================================

849.3.4 Challenge Exercises

After running the basic lab, try these challenges to deepen your understanding:

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

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

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

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

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

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

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

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

849.4 Resources

849.4.1 Internal Resources

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

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

849.4.4 Standards and Documentation

849.5 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

849.6 What’s Next

Continue your Wi-Fi learning with Wi-Fi Comprehensive Review, covering Wi-Fi standards evolution, Wi-Fi 6 features, WPA3 security, and channel planning for dense deployments.