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).
Key Concepts
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!”
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
Add a DHT22 sensor: Click the + button and search for “DHT22”
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
Optional: Add an LED on GPIO 2 for status indication
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.
Expand Full Weather Station Code (~159 lines)
// 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:constchar* ssid ="Wokwi-GUEST";constchar* 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;unsignedlong lastReadTime =0;constunsignedlong 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 validif(!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
Click the Play button to start the simulation
Watch the Serial Monitor for connection status
Once connected, Wokwi provides a simulated IP address
Click the IP address link to open the web page
Observe temperature and humidity readings updating
48.3.8 Understanding the Code
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
Challenge 1: Add Heat Index Calculation (Beginner)
The DHT library can calculate the “feels like” temperature. Modify the code to:
Calculate heat index using dht.computeHeatIndex(temperature, humidity, false)
Display heat index on the web page alongside temperature
Add a warning message if heat index exceeds 32C
Hint: Add a new variable heatIndex and update both readSensor() and getHTML().
Challenge 2: Add Historical Data Graph (Intermediate)
Create a simple history feature:
Store the last 10 temperature readings in an array
Create a new endpoint /api/history that returns all readings as JSON
Add a simple bar chart to the HTML using CSS
Hint: Use a circular buffer array and update it each read cycle.
Challenge 3: Add Temperature Alerts (Intermediate)
Implement an alert system:
Define HIGH_TEMP and LOW_TEMP thresholds
Change the web page background color based on temperature:
Blue if below LOW_TEMP
Green if normal
Red if above HIGH_TEMP
Blink the LED when temperature is outside normal range
Hint: Pass a “status” parameter to the HTML generation function.
Challenge 4: Multiple Sensors (Advanced)
Expand the weather station:
Add a second DHT22 sensor on a different GPIO pin
Read both sensors and display indoor/outdoor readings
Calculate the difference between the two
Add a /api/compare endpoint with both readings
Hint: Create a second DHT object with a different pin number.
Challenge 5: mDNS and OTA Updates (Advanced)
Add professional IoT features:
Enable mDNS so the device is accessible at weather.local
Add Over-The-Air (OTA) update capability
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:
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
Identify Overlapping Channels:
Determine which APs cause interference
Find the least congested channel
Calculate expected throughput reduction from overlapping APs
Compare 2.4 GHz vs 5 GHz:
Scan 5 GHz channels
Count total APs on each band
Recommend band selection for IoT devices
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:
Identify Security Weaknesses:
Scan for WEP/WPA networks (outdated encryption)
Find APs with default SSIDs
Detect open networks
Identify WPS-enabled networks
Test WPA2/WPA3 Configuration:
Verify encryption method (CCMP vs TKIP)
Check if WPA2-Enterprise is available
Test guest network isolation
IoT Device Security Assessment:
Inventory all IoT devices on network
Identify devices using weak passwords
Check for HTTP admin panels
Scan for outdated firmware
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:
Measure Current Draw (use multimeter or power analyzer):
Wi-Fi connection establishment
Active transmission
Wi-Fi modem sleep
Deep sleep
Calculate Battery Life:
Battery: 2x AA (3000 mAh)
Operating voltage: 3.3V
Daily cycles: 144 (every 10 minutes)
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
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 CHECKfloat temp = readSensor(); client.publish("sensors/temp", String(temp).c_str());// FAILS SILENTLY if disconnected delay(60000);// Wait 1 minute}
Why It Fails:
Router reboots (firmware updates, power outages) - device doesn’t reconnect
Long outage (2+ hours), verify device still reconnects on first try
DHCP renewal (wait 48 hours), verify no connection loss
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.
Reading temperature and humidity from a DHT22 sensor
Creating an HTTP web server on embedded hardware
Building a responsive web interface with auto-refresh
Implementing a JSON API endpoint for data integration
Analyzing Wi-Fi channel congestion
Planning power budgets for battery-powered devices
Designing network infrastructure for IoT
Interactive Quiz: Match Concepts
Interactive Quiz: Sequence the Steps
Common Pitfalls
1. Performing Lab Tests in an RF-Clean Environment
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.
2. Testing Only One Configuration Point
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.
3. Measuring Peak Throughput Instead of Sustained Throughput
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.
4. Ignoring the Connection Overhead in IoT Power Tests
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.