48  Wi-Fi Hands-On Labs and Exercises

In 60 Seconds

This hands-on lab chapter guides you through building a complete Wi-Fi weather station using an ESP32 and Wokwi simulator. You will connect to Wi-Fi, read DHT22 sensor data, serve readings via an HTTP web server with auto-refresh, and expose a JSON API endpoint. Additional exercises cover channel analysis (finding least congested 2.4 GHz channels), Wi-Fi security auditing (WPA2/WPA3 verification), power consumption measurement (deep sleep duty cycling for 1+ year battery life), and network capacity planning (200-device VLAN-segmented deployments).

  • Spectrum Analysis Lab: Using software spectrum analyzers (Wi-Fi analyzer apps, Wireshark) to visualize channel utilization and interference
  • Packet Capture: Capturing 802.11 management and data frames to analyze association, authentication, and data exchange sequences
  • RSSI Distance Mapping: Measuring signal strength at different distances to build empirical path loss models for a specific environment
  • Throughput Testing: Using iperf3 or similar tools to measure actual UDP/TCP throughput under different channel conditions
  • AP Configuration Lab: Hands-on configuration of SSID, channel, power, security settings, and QoS on a real or virtual AP
  • Roaming Lab: Measuring handover latency and packet loss during client transition between APs using 802.11r and without
  • Power Consumption Measurement: Using current probes or energy meters to measure real Wi-Fi module power in different states
  • IoT Wi-Fi Integration: Connecting an ESP32 or similar module to Wi-Fi and measuring association time, throughput, and power

48.1 Sensor Squad: Wi-Fi Weather Station Lab

Sammy the Sensor was SO excited – today he would build his very own weather station!

“First, we need to connect to Wi-Fi,” said Max the Microcontroller. “I use my Wi-Fi radio to find the network name (called the SSID), then I send the password. Once connected, the router gives me an IP address – like a home address for the internet!”

“Then I read the temperature and humidity,” said Sammy. “The DHT22 sensor speaks a special language – it sends exactly 40 bits of data: 16 bits for humidity, 16 bits for temperature, and 8 bits to check nothing got garbled.”

Lila the LED blinked with excitement: “And then Max runs a tiny WEB SERVER! When someone visits our IP address in their browser, Max builds a whole HTML page showing Sammy’s readings. It even auto-refreshes every 5 seconds!”

“The coolest part,” added Bella the Battery, “is that we also have a JSON API at /api/data. Other computers and apps can read our data without needing a web browser – they just ask for the numbers directly!”

48.2 Learning Objectives

By completing these hands-on labs, you will be able to:

  • Configure ESP32 Wi-Fi in Station (STA) mode and verify connectivity via serial output
  • Implement DHT22 digital sensor reads using the one-wire protocol on a specified GPIO pin
  • Construct an HTTP web server on embedded hardware that routes requests to handler functions
  • Generate dynamic HTML pages with auto-refreshing live sensor readings and a JSON API endpoint
  • Evaluate Wi-Fi channel congestion across 2.4 GHz and 5 GHz bands to recommend optimal channel assignments
  • Calculate daily energy budgets and predict battery life for duty-cycled Wi-Fi IoT devices
  • Architect VLAN-segmented Wi-Fi infrastructure supporting 200+ heterogeneous IoT devices

These hands-on labs give you practical experience with Wi-Fi configuration, scanning, analysis, and troubleshooting. You will use real tools to examine wireless networks, measure signal strength, and configure IoT devices. Each lab builds practical skills that transfer directly to real-world Wi-Fi deployments.

48.3 Hands-On Lab: Wi-Fi Weather Station

This interactive lab uses the Wokwi ESP32 simulator to build a complete Wi-Fi weather station. You will connect an ESP32 to Wi-Fi, read temperature and humidity data from a DHT22 sensor, and serve the readings through a simple web server.

48.3.1 Prerequisites

  • Basic understanding of Arduino/C++ syntax
  • Familiarity with Wi-Fi concepts from previous chapters
  • No physical hardware required (browser-based simulation)

48.3.2 About Wokwi

Wokwi is a free online simulator for Arduino, ESP32, and other microcontrollers. It allows you to build and test IoT projects entirely in your browser without purchasing hardware.

48.3.3 Launch the Simulator

Launch the simulator below to get started. The default project includes an ESP32 - you will add components and code as you progress.

Simulator Tips
  • Click on the ESP32 to see available pins
  • Use the + button to add components (search for “DHT22”)
  • Connect wires by clicking on pins
  • The Serial Monitor shows debug output
  • Press the green Play button to run your code

48.3.4 Step 1: Set Up the Circuit

  1. Add a DHT22 sensor: Click the + button and search for “DHT22”
  2. Wire the DHT22 to ESP32:
    • DHT22 VCC (pin 1) -> ESP32 3.3V
    • DHT22 Data (pin 2) -> ESP32 GPIO 4
    • DHT22 GND (pin 4) -> ESP32 GND
  3. Optional: Add an LED on GPIO 2 for status indication

Diagram showing ESP32

48.3.5 Step 2: Copy the Weather Station Code

Copy this code into the Wokwi code editor. The sketch reads DHT22 temperature/humidity, serves a live HTML dashboard, and exposes a JSON API.

// Wi-Fi Weather Station Lab
// Reads DHT22 temperature/humidity and serves data via web server

#include <WiFi.h>
#include <WebServer.h>
#include "DHT.h"

// ========== CONFIGURATION ==========
// Note: In Wokwi simulator, use these credentials:
const char* ssid = "Wokwi-GUEST";
const char* password = "";  // Leave empty for Wokwi

// DHT Sensor Configuration
#define DHTPIN 4        // GPIO pin connected to DHT22 data pin
#define DHTTYPE DHT22   // DHT22 (AM2302)
#define LED_PIN 2       // Built-in LED for status

// ========== GLOBAL OBJECTS ==========
DHT dht(DHTPIN, DHTTYPE);
WebServer server(80);

// Sensor readings (global for access in handlers)
float temperature = 0.0;
float humidity = 0.0;
unsigned long lastReadTime = 0;
const unsigned long READ_INTERVAL = 2000;  // Read every 2 seconds

// ========== WEB PAGE HTML ==========
String getHTML() {
  String html = "<!DOCTYPE html><html><head>";
  html += "<meta charset='UTF-8'>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<meta http-equiv='refresh' content='5'>";  // Auto-refresh every 5s
  html += "<title>ESP32 Weather Station</title>";
  html += "<style>";
  html += "body { font-family: Arial, sans-serif; background: #1a1a2e; color: #eee; ";
  html += "display: flex; justify-content: center; align-items: center; ";
  html += "min-height: 100vh; margin: 0; }";
  html += ".container { text-align: center; background: #16213e; ";
  html += "padding: 40px; border-radius: 20px; box-shadow: 0 10px 30px rgba(0,0,0,0.3); }";
  html += "h1 { color: #e94560; margin-bottom: 30px; }";
  html += ".reading { background: #0f3460; padding: 20px; margin: 15px; ";
  html += "border-radius: 15px; display: inline-block; min-width: 150px; }";
  html += ".value { font-size: 48px; font-weight: bold; color: #00fff5; }";
  html += ".label { font-size: 14px; color: #aaa; margin-top: 10px; }";
  html += ".unit { font-size: 24px; color: #e94560; }";
  html += ".status { margin-top: 20px; font-size: 12px; color: #666; }";
  html += "</style></head><body>";
  html += "<div class='container'>";
  html += "<h1>ESP32 Weather Station</h1>";
  html += "<div class='reading'>";
  html += "<div class='value'>" + String(temperature, 1) + "<span class='unit'>C</span></div>";
  html += "<div class='label'>Temperature</div>";
  html += "</div>";
  html += "<div class='reading'>";
  html += "<div class='value'>" + String(humidity, 1) + "<span class='unit'>%</span></div>";
  html += "<div class='label'>Humidity</div>";
  html += "</div>";
  html += "<div class='status'>Auto-refreshes every 5 seconds</div>";
  html += "</div></body></html>";
  return html;
}

// ========== REQUEST HANDLERS ==========
void handleRoot() {
  digitalWrite(LED_PIN, HIGH);  // Blink LED on request
  server.send(200, "text/html", getHTML());
  delay(50);
  digitalWrite(LED_PIN, LOW);
}

void handleJSON() {
  // API endpoint for JSON data
  String json = "{";
  json += "\"temperature\":" + String(temperature, 2) + ",";
  json += "\"humidity\":" + String(humidity, 2) + ",";
  json += "\"unit\":\"celsius\",";
  json += "\"timestamp\":" + String(millis());
  json += "}";
  server.send(200, "application/json", json);
}

void handleNotFound() {
  server.send(404, "text/plain", "404: Not Found");
}

// ========== SENSOR READING ==========
void readSensor() {
  if (millis() - lastReadTime >= READ_INTERVAL) {
    float h = dht.readHumidity();
    float t = dht.readTemperature();

    // Check if readings are valid
    if (!isnan(h) && !isnan(t)) {
      humidity = h;
      temperature = t;
      Serial.printf("Temperature: %.1f C, Humidity: %.1f%%\n", t, h);
    } else {
      Serial.println("Failed to read from DHT sensor!");
    }
    lastReadTime = millis();
  }
}

// ========== SETUP ==========
void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);

  Serial.println("\n========================================");
  Serial.println("   ESP32 Wi-Fi Weather Station Lab");
  Serial.println("========================================\n");

  // Initialize DHT sensor
  dht.begin();
  Serial.println("[OK] DHT22 sensor initialized");

  // Connect to Wi-Fi
  Serial.printf("[..] Connecting to %s", ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 30) {
    delay(500);
    Serial.print(".");
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));  // Blink while connecting
    attempts++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\n[OK] Wi-Fi connected!");
    Serial.print("[OK] IP Address: ");
    Serial.println(WiFi.localIP());
    digitalWrite(LED_PIN, HIGH);  // Solid LED when connected
  } else {
    Serial.println("\n[!!] Wi-Fi connection failed!");
    return;
  }

  // Set up web server routes
  server.on("/", handleRoot);
  server.on("/api/data", handleJSON);
  server.onNotFound(handleNotFound);

  // Start server
  server.begin();
  Serial.println("[OK] HTTP server started on port 80");
  Serial.println("\n========================================");
  Serial.printf("   Open: http://%s/\n", WiFi.localIP().toString().c_str());
  Serial.println("   API:  /api/data (JSON format)");
  Serial.println("========================================\n");
}

// ========== MAIN LOOP ==========
void loop() {
  server.handleClient();  // Handle incoming HTTP requests
  readSensor();           // Read sensor at intervals
}

48.3.6 Step 3: Configure the Wokwi Project

In Wokwi, configure the diagram.json file:

{
  "version": 1,
  "author": "IoT Class Lab",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-dht22", "id": "dht1", "top": -50, "left": 150, "attrs": {} }
  ],
  "connections": [
    [ "esp:TX0", "$serialMonitor:RX", "", [] ],
    [ "esp:RX0", "$serialMonitor:TX", "", [] ],
    [ "dht1:VCC", "esp:3V3", "red", [] ],
    [ "dht1:SDA", "esp:D4", "green", [] ],
    [ "dht1:GND", "esp:GND.1", "black", [] ]
  ],
  "dependencies": {}
}

48.3.7 Step 4: Run and Test

  1. Click the Play button to start the simulation
  2. Watch the Serial Monitor for connection status
  3. Once connected, Wokwi provides a simulated IP address
  4. Click the IP address link to open the web page
  5. Observe temperature and humidity readings updating

48.3.8 Understanding the Code

Diagram showing SENSOR

Key Components:

Component Purpose
WiFi.h ESP32 Wi-Fi library for network connectivity
WebServer.h Creates HTTP server to handle requests
DHT.h Library to read DHT22 temperature/humidity sensor
WiFi.begin() Initiates connection to Wi-Fi network
server.on() Registers URL routes and handler functions
server.handleClient() Processes incoming HTTP requests
Quick Check: ESP32 Web Server Basics

48.3.9 Challenge Exercises

The DHT library can calculate the “feels like” temperature. Modify the code to:

  1. Calculate heat index using dht.computeHeatIndex(temperature, humidity, false)
  2. Display heat index on the web page alongside temperature
  3. Add a warning message if heat index exceeds 32C

Hint: Add a new variable heatIndex and update both readSensor() and getHTML().

Create a simple history feature:

  1. Store the last 10 temperature readings in an array
  2. Create a new endpoint /api/history that returns all readings as JSON
  3. Add a simple bar chart to the HTML using CSS

Hint: Use a circular buffer array and update it each read cycle.

Implement an alert system:

  1. Define HIGH_TEMP and LOW_TEMP thresholds
  2. Change the web page background color based on temperature:
    • Blue if below LOW_TEMP
    • Green if normal
    • Red if above HIGH_TEMP
  3. Blink the LED when temperature is outside normal range

Hint: Pass a “status” parameter to the HTML generation function.

Expand the weather station:

  1. Add a second DHT22 sensor on a different GPIO pin
  2. Read both sensors and display indoor/outdoor readings
  3. Calculate the difference between the two
  4. Add a /api/compare endpoint with both readings

Hint: Create a second DHT object with a different pin number.

Add professional IoT features:

  1. Enable mDNS so the device is accessible at weather.local
  2. Add Over-The-Air (OTA) update capability
  3. Create a /settings page to configure the device name

Hint: Include ESPmDNS.h and ArduinoOTA.h libraries.

48.3.10 Troubleshooting Guide

Problem Possible Cause Solution
“Failed to read from DHT sensor!” Incorrect wiring Check GPIO 4 connection to DHT22 data pin
Wi-Fi won’t connect Wrong credentials Use “Wokwi-GUEST” with empty password
Web page not loading Server not started Check Serial Monitor for IP address
Readings show NaN Sensor initialization failed Ensure dht.begin() is called in setup
Page loads but shows 0.0 First reading not complete Wait 2+ seconds after boot

48.4 Exercise 1: Wi-Fi Channel Analysis

Objective: Analyze 2.4 GHz and 5 GHz channel congestion to optimize IoT device placement

Tasks:

  1. Scan 2.4 GHz Spectrum (simulated or real):
    • Use Wi-Fi analyzer app (Wi-Fi Analyzer for Android, NetSpot for PC/Mac)
    • Identify all access points on channels 1, 6, 11
    • Measure signal strength (RSSI) for each AP
    • Calculate channel utilization percentage
  2. Identify Overlapping Channels:
    • Determine which APs cause interference
    • Find the least congested channel
    • Calculate expected throughput reduction from overlapping APs
  3. Compare 2.4 GHz vs 5 GHz:
    • Scan 5 GHz channels
    • Count total APs on each band
    • Recommend band selection for IoT devices
  4. Design Channel Plan:
    • For a building with 3 Wi-Fi APs, assign non-overlapping channels
    • Account for neighboring networks
    • Minimize interference for IoT sensors

48.5 Exercise 2: Wi-Fi Security Audit

Objective: Audit a Wi-Fi network for security vulnerabilities

Tasks:

  1. Identify Security Weaknesses:
    • Scan for WEP/WPA networks (outdated encryption)
    • Find APs with default SSIDs
    • Detect open networks
    • Identify WPS-enabled networks
  2. Test WPA2/WPA3 Configuration:
    • Verify encryption method (CCMP vs TKIP)
    • Check if WPA2-Enterprise is available
    • Test guest network isolation
  3. IoT Device Security Assessment:
    • Inventory all IoT devices on network
    • Identify devices using weak passwords
    • Check for HTTP admin panels
    • Scan for outdated firmware
  4. Create Security Improvement Plan:
    • Upgrade to WPA3 if supported
    • Disable WPS
    • Create separate IoT VLAN

48.6 Exercise 3: Wi-Fi Power Consumption Measurement

Objective: Measure and optimize Wi-Fi power consumption for battery-powered IoT sensors

Scenario: ESP32-based temperature sensor sending data every 10 minutes

Tasks:

  1. Measure Current Draw (use multimeter or power analyzer):
    • Wi-Fi connection establishment
    • Active transmission
    • Wi-Fi modem sleep
    • Deep sleep
  2. Calculate Battery Life:
    • Battery: 2x AA (3000 mAh)
    • Operating voltage: 3.3V
    • Daily cycles: 144 (every 10 minutes)
  3. Optimize for 1-Year Battery Life:
    • Option A: Wi-Fi 4 with standard sleep
    • Option B: Wi-Fi 6 with TWT
    • Option C: Reduce transmission frequency

Expected Outcome:

Power Consumption Measurement:
====================================
ESP32 Wi-Fi Power States:

1. Deep Sleep: 10 uA
2. Wi-Fi Connecting: 160 mA for 2s
3. Transmitting Data: 120 mA for 1s
4. Wi-Fi Modem Sleep: 15 mA

Daily Power Budget (144 cycles):
====================================
Deep Sleep (23h 48m):
- 23.8h x 0.010 mA = 0.238 mAh

Wi-Fi Connection (144 x 2s):
- 288s x 160 mA / 3600 = 12.8 mAh

Data Transmission (144 x 1s):
- 144s x 120 mA / 3600 = 4.8 mAh

Total Daily: 17.84 mAh/day
Battery Life: 3000 mAh / 17.84 = 168 days

Battery life calculation with LaTeX precision:

Total daily energy consumption:

\[E_{daily} = E_{sleep} + E_{connect} + E_{transmit}\]

Where: \[E_{sleep} = \frac{(86400 - 144 \times 3)\ \text{s}}{3600} \times 10\ \mu\text{A} = 23.88\ \text{h} \times 0.01\ \text{mA} = 0.239\ \text{mAh}\]

\[E_{connect} = 144 \times 2\ \text{s} \times 160\ \text{mA} \times \frac{1}{3600} = 12.8\ \text{mAh}\]

\[E_{transmit} = 144 \times 1\ \text{s} \times 120\ \text{mA} \times \frac{1}{3600} = 4.8\ \text{mAh}\]

\[E_{daily} = 0.239 + 12.8 + 4.8 = 17.84\ \text{mAh/day}\]

Battery life (80% DoD): \[\text{Life} = \frac{3000\ \text{mAh} \times 0.8}{17.84\ \text{mAh/day}} \approx 135\ \text{days}\]

Key insight: Connection overhead (12.8 mAh) dominates power budget – use Wi-Fi 6 TWT or connection reuse to extend to 300+ days.

Optimization Strategy:
====================================
Option A: Wi-Fi 4 (current) -> 168 days
Option B: Wi-Fi 6 + TWT -> ~300 days
Option C: 30-minute intervals -> 456 days

Recommended: Option C + Option B
- 30-minute transmission + Wi-Fi 6 TWT
- New daily budget: 6.5 mAh/day
- Battery life: 461 days (1.26 years)

48.6.1 Interactive: Battery Life Calculator

48.7 Exercise 4: Wi-Fi Network Capacity Planning

Objective: Design Wi-Fi infrastructure for a 200-device IoT deployment

Scenario: Office building (2,000 sqm) deploying: - 100x Occupancy sensors (low bandwidth, 2.4 GHz) - 50x Smart lights (medium bandwidth, 2.4 GHz) - 30x Environmental sensors (low bandwidth, 2.4 GHz) - 20x Security cameras (high bandwidth, 5 GHz)

Tasks:

  1. Calculate Bandwidth Requirements:
    • Occupancy sensors: 100 bytes every 5 minutes
    • Smart lights: 200 bytes on state change
    • Environmental sensors: 500 bytes every 10 minutes
    • Cameras: 2 Mbps continuous
  2. Determine AP Count and Placement:
    • Consumer AP capacity: 30-50 devices
    • Enterprise AP capacity: 200-500 devices
    • Coverage area per AP
  3. Design VLAN Segmentation:
    • VLAN 10: Corporate laptops/phones
    • VLAN 20: IoT sensors
    • VLAN 30: Security cameras
    • VLAN 40: Guest network
  4. Calculate Total Cost:
    • Access points (enterprise-grade)
    • PoE switches for AP power
    • Controller software

48.8 Knowledge Check

Common Mistake: Forgetting to Handle Wi-Fi Reconnection in Production Code

The Mistake: A student deploys an ESP32 weather station that works perfectly in testing but fails in production after 2-3 days. The device stops sending data and requires a manual power cycle to recover.

What Went Wrong:

The code connects to Wi-Fi in setup() but never checks connection status in loop():

void setup() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
  Serial.println("Connected!");
}

void loop() {
  // Read sensor and send data - NO CONNECTION CHECK
  float temp = readSensor();
  client.publish("sensors/temp", String(temp).c_str());  // FAILS SILENTLY if disconnected
  delay(60000);  // Wait 1 minute
}

Why It Fails:

  1. Router reboots (firmware updates, power outages) - device doesn’t reconnect
  2. DHCP lease expires (typically 24-48 hours) - device loses IP address
  3. AP roaming (mesh networks) - device fails handoff
  4. Channel interference - temporary signal loss becomes permanent disconnect
  5. Wi-Fi power save glitches - modem sleep fails to wake properly

Real-World Impact:

  • Smart greenhouse loses 3 days of temperature data during router firmware update
  • Industrial sensor network goes offline after DHCP server maintenance
  • Smart home devices require manual reset after brief internet outage
  • Customer support calls: “Why does my IoT device stop working randomly?”

The Fix: Robust Reconnection with Exponential Backoff

unsigned long lastReconnectAttempt = 0;
unsigned long reconnectInterval = 1000;  // Start with 1 second
const unsigned long maxReconnectInterval = 300000;  // Max 5 minutes

void setup() {
  WiFi.mode(WIFI_STA);
  WiFi.setAutoReconnect(true);  // Enable auto-reconnect
  WiFi.persistent(false);  // Don't save to flash (battery optimization)

  connectToWiFi();
}

void connectToWiFi() {
  Serial.println("Connecting to Wi-Fi...");
  WiFi.begin(ssid, password);

  int timeout = 0;
  while (WiFi.status() != WL_CONNECTED && timeout < 20) {
    delay(500);
    Serial.print(".");
    timeout++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nConnected!");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
    reconnectInterval = 1000;  // Reset backoff on success
  } else {
    Serial.println("\nConnection failed");
  }
}

void loop() {
  // Check Wi-Fi status BEFORE every critical operation
  if (WiFi.status() != WL_CONNECTED) {
    unsigned long currentMillis = millis();

    // Exponential backoff: don't hammer the network
    if (currentMillis - lastReconnectAttempt >= reconnectInterval) {
      lastReconnectAttempt = currentMillis;
      Serial.println("Wi-Fi disconnected! Attempting reconnection...");

      WiFi.disconnect();
      delay(100);
      connectToWiFi();

      // Double the backoff interval (exponential backoff)
      reconnectInterval = min(reconnectInterval * 2, maxReconnectInterval);
      Serial.printf("Next reconnect attempt in %lu ms\n", reconnectInterval);
    }

    return;  // Skip this loop iteration if not connected
  }

  // Now safe to use Wi-Fi
  float temp = readSensor();
  if (client.publish("sensors/temp", String(temp).c_str())) {
    Serial.println("Data sent successfully");
  } else {
    Serial.println("Publish failed - will retry next cycle");
  }

  delay(60000);
}

Key Improvements:

  1. Connection Check Every Loop: if (WiFi.status() != WL_CONNECTED) before any Wi-Fi operation
  2. Exponential Backoff: 1s → 2s → 4s → 8s → … → 5 minutes (prevents flooding router during extended outages)
  3. AutoReconnect Flag: WiFi.setAutoReconnect(true) lets ESP32 handle some reconnections automatically
  4. Publish Error Handling: Check return value of client.publish() and log failures
  5. Clean Disconnect: WiFi.disconnect() before reconnecting (clears stale state)

Advanced Production Pattern: Connection State Machine

enum WiFiState {
  DISCONNECTED,
  CONNECTING,
  CONNECTED,
  RECONNECTING,
  FAILED
};

WiFiState wifiState = DISCONNECTED;
int failedAttempts = 0;
const int maxFailedAttempts = 10;

void handleWiFiStateMachine() {
  switch (wifiState) {
    case DISCONNECTED:
      if (millis() - lastReconnectAttempt >= reconnectInterval) {
        wifiState = CONNECTING;
        connectToWiFi();
      }
      break;

    case CONNECTING:
      if (WiFi.status() == WL_CONNECTED) {
        wifiState = CONNECTED;
        failedAttempts = 0;
        reconnectInterval = 1000;
        Serial.println("State: CONNECTED");
      } else if (millis() - lastReconnectAttempt > 10000) {  // 10s timeout
        wifiState = RECONNECTING;
        failedAttempts++;
        reconnectInterval = min(reconnectInterval * 2, maxReconnectInterval);
        Serial.printf("State: RECONNECTING (attempt %d)\n", failedAttempts);
      }
      break;

    case CONNECTED:
      if (WiFi.status() != WL_CONNECTED) {
        wifiState = DISCONNECTED;
        Serial.println("State: DISCONNECTED (connection lost)");
      }
      break;

    case RECONNECTING:
      if (failedAttempts >= maxFailedAttempts) {
        wifiState = FAILED;
        Serial.println("State: FAILED (too many retries, restarting ESP32)");
        delay(1000);
        ESP.restart();  // Last resort: full device reset
      } else {
        wifiState = DISCONNECTED;
      }
      break;

    case FAILED:
      // Dead state - device will restart
      break;
  }
}

void loop() {
  handleWiFiStateMachine();

  if (wifiState == CONNECTED) {
    // Safe to use Wi-Fi here
    float temp = readSensor();
    client.publish("sensors/temp", String(temp).c_str());
  }

  delay(1000);
}

Testing Reconnection Logic:

  1. Unplug router for 30 seconds, verify device reconnects
  2. Change Wi-Fi password (wrong credentials), verify exponential backoff doesn’t flood logs
  3. Long outage (2+ hours), verify device still reconnects on first try
  4. DHCP renewal (wait 48 hours), verify no connection loss
  5. Power brownout (brief voltage drop), verify device recovers without full reset

Key Insight: The difference between a prototype and a production IoT device is connection resilience. Always assume Wi-Fi will disconnect at the worst possible time. The pattern is: check connection → attempt reconnect with backoff → skip operation if offline → retry next cycle. Never assume WiFi.begin() in setup() guarantees connectivity for the device’s lifetime.

Concept Relates To Why It Matters
DHT22 Sensor I2C/SPI communication, Temperature/humidity sensing Demonstrates physical sensor integration with web reporting
HTTP Web Server ESP32 as server, Client-server architecture Enables browser-based device control and monitoring
JSON API Data serialization, Integration with dashboards Standard format for IoT data exchange
Connection Resilience Exponential backoff, Automatic reconnection Production devices must handle network failures gracefully
RSSI Monitoring Signal strength, AP placement validation Identifies weak coverage areas needing additional APs

48.9 See Also

48.10 Lab Summary

In these labs, you practiced:

  1. Connecting an ESP32 to a Wi-Fi network
  2. Reading temperature and humidity from a DHT22 sensor
  3. Creating an HTTP web server on embedded hardware
  4. Building a responsive web interface with auto-refresh
  5. Implementing a JSON API endpoint for data integration
  6. Analyzing Wi-Fi channel congestion
  7. Planning power budgets for battery-powered devices
  8. Designing network infrastructure for IoT

Common Pitfalls

Testing Wi-Fi performance in a shielded lab or RF-quiet room produces optimistic results not representative of real deployments. Always test in an environment with realistic interference levels — an office, warehouse, or outdoor space with existing Wi-Fi networks nearby.

Lab tests often validate a single configuration (one AP, one client, one channel). Real deployments have multiple APs, many clients, interference, and roaming. Expand lab tests to include multi-AP scenarios, high client density, interference injection, and mobility to validate real-world behavior.

iperf3 burst tests measure peak throughput which may exceed sustainable levels. IoT applications require sustained throughput over minutes or hours. Run iperf3 tests for 60+ seconds and measure average throughput, not just peak values.

Power consumption tests that measure only data transmission ignore connection setup overhead: Wi-Fi scanning (10-100 ms), association (50-200 ms), and DHCP (100-500 ms). For IoT devices that disconnect between transmissions, connection overhead dominates energy consumption. Always measure full connection-to-disconnect cycle energy.

48.11 What’s Next

If you want to… Read this
Deep dive ESP32 Wi-Fi implementation Wi-Fi Implementation: ESP32 Basics
Work through comprehensive lab exercises Wi-Fi Comprehensive Lab
Implement HTTP and WebSocket Wi-Fi HTTP & WebSocket
Optimize Wi-Fi power consumption Wi-Fi Power Optimization
Analyze Wi-Fi channels Wi-Fi Channel Analysis