Day 3 of 5
⏱ ~60 minutes
IoT in 5 Days — Day 3

ESP32 and WiFi

The ESP32 is the Arduino-compatible powerhouse behind most modern IoT projects — WiFi, Bluetooth, dual-core CPU, and deep sleep all in one $5 chip.

ESP32 vs Arduino

The ESP32 (Espressif) runs at 240MHz (vs Arduino's 16MHz), has 520KB RAM (vs 2KB), and built-in WiFi+BLE. Programmed with the Arduino IDE using the ESP32 board package. Key pins: GPIO0 (boot mode), GPIO2 (onboard LED), 34–39 (input only). Operating voltage: 3.3V — don't connect 5V directly to GPIO pins. Use esp_sleep_enable_timer_wakeup() for deep sleep to extend battery life.

WiFi Connection and HTTP POST

WiFi.begin(ssid, password) connects to WiFi. Poll WiFi.status() == WL_CONNECTED to wait. Then use HTTPClient or WiFiClient to make requests. HTTP POST sensor data to a server or cloud endpoint. Always add a timeout and retry logic — WiFi connections drop. Use WiFi.disconnect() followed by reconnect on repeated failures.

Deep Sleep for Battery Life

ESP32 deep sleep shuts down most of the chip, waking on a timer (µs precision), GPIO change, or touchpad. Deep sleep current: ~10µA (vs 70mA active). A 1000mAh battery lasts ~4 months sleeping 60s, awake 1s vs 6 hours awake continuously. Pattern: wake → read sensor → connect WiFi → publish → deep sleep. Store state in RTC memory (persists through deep sleep): RTC_DATA_ATTR int bootCount = 0;

cpp
// ESP32: WiFi + MQTT sensor node with deep sleep
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include "esp_sleep.h"

const char* SSID     = "YOUR_SSID";
const char* PASSWORD = "YOUR_PASSWORD";
const char* MQTT_BROKER = "192.168.1.100"; // your local broker
const char* MQTT_TOPIC  = "home/esp32/sensors";

#define DHT_PIN  4
#define DHT_TYPE DHT22
#define SLEEP_US (60 * 1000000ULL)  // 60 seconds

RTC_DATA_ATTR int bootCount = 0;  // survives deep sleep!

DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);

bool connectWiFi() {
  WiFi.begin(SSID, PASSWORD);
  unsigned long start = millis();
  while (WiFi.status() != WL_CONNECTED && millis()-start < 10000) {
    delay(100);
  }
  return WiFi.status() == WL_CONNECTED;
}

bool connectMQTT() {
  mqtt.setServer(MQTT_BROKER, 1883);
  return mqtt.connect("esp32-sensor");
}

void setup() {
  Serial.begin(115200);
  bootCount++;
  Serial.printf("Boot #%d
", bootCount);

  dht.begin();
  delay(2000);  // DHT22 warmup

  float temp = dht.readTemperature();
  float hum  = dht.readHumidity();

  if (!isnan(temp) && connectWiFi() && connectMQTT()) {
    char payload[128];
    snprintf(payload, sizeof(payload),
      "{"temp":%.1f,"hum":%.1f,"boot":%d,"ip":"%s"}",
      temp, hum, bootCount, WiFi.localIP().toString().c_str());

    mqtt.publish(MQTT_TOPIC, payload, true);  // retain=true
    mqtt.loop();
    delay(100);  // ensure publish completes
    Serial.printf("Published: %s
", payload);
  }

  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);

  Serial.println("Going to deep sleep...");
  esp_deep_sleep(SLEEP_US);
}

void loop() {} // never reached with deep sleep
💡
Use esp_deep_sleep() instead of delay() between readings on battery-powered devices. The difference: delay() burns 70mA for 60 seconds = 70mAh. Deep sleep burns 10µA for 60 seconds = 0.0003mAh. That's a 200,000x power reduction.
📝 Day 3 Exercise
Deploy a Battery-Powered ESP32 Sensor
  1. Install ESP32 board package in Arduino IDE: File → Preferences → add ESP32 board URL. Tools → Board → ESP32 Dev Module.
  2. Upload the sketch (update SSID/password/broker). Verify sensor data appears in your MQTT subscriber.
  3. Add deep sleep. Monitor current with a USB power meter — measure active vs sleep current.
  4. Add OTA updates: include ArduinoOTA.begin() in setup. Skip it when running on battery (WiFi-only wake).
  5. Run for 24 hours on a USB power bank. Calculate daily energy consumption from your readings.

Day 3 Summary

  • ESP32: 240MHz, 520KB RAM, WiFi+BLE built-in, 3.3V GPIO — the workhorse of IoT
  • Deep sleep cuts current from 70mA to 10µA — extends battery from hours to months
  • RTC_DATA_ATTR variables persist through deep sleep — use for boot count, last readings, connection state
  • Always check isnan() on sensor reads and handle WiFi/MQTT connection failures gracefully
Challenge

Implement provisioning: instead of hardcoding SSID/password, have the ESP32 start in AP (access point) mode on first boot. Host a simple HTML form at 192.168.4.1 where the user enters WiFi credentials. Store them in NVMe flash with the Preferences library. On subsequent boots, use the stored credentials. This is how real commercial IoT devices work.

Finished this lesson?