TL;DR:

  • The ESP32 from Espressif Systems is the dominant low-cost microcontroller for WiFi/BLE IoT projects — widely available, well-documented, and supported by Arduino, ESP-IDF, MicroPython, and Rust.
  • The family has expanded significantly: the ESP32-S3 adds USB OTG and AI acceleration, the ESP32-C3/C6 bring RISC-V cores and Thread/Zigbee support, and the ESP32-P4 targets high-performance embedded vision.
  • For production deployments, understanding power management, OTA update patterns, and security provisions (Secure Boot, Flash Encryption) separates hobby projects from reliable fielded devices.

Espressif’s ESP32 has become the microcontroller equivalent of the Raspberry Pi — the default starting point for anyone building a connected device. First released in 2016, the chip’s combination of dual-core processing, integrated WiFi and Bluetooth, generous I/O, and sub-$3 pricing created a category. In 2026, the ESP32 family spans over a dozen variants optimised for different use cases, and the development ecosystem has matured to the point where serious commercial products are built on it.

The ESP32 Family in 2026

Understanding which variant to use matters. The original ESP32 is still widely available and appropriate for many projects, but newer variants often deliver better power efficiency, security, or connectivity:

ESP32 (original) — Dual Xtensa LX6 cores at 240MHz, WiFi b/g/n + BT/BLE 4.2, 520KB SRAM. The most documented, most used. Appropriate for most WiFi-connected sensor or control projects.

ESP32-S3 — Dual Xtensa LX7 cores at 240MHz, WiFi + BLE 5, USB OTG, and a vector instruction extension for AI/ML inference. Choose this for anything involving local ML (keyword spotting, anomaly detection) or USB device functionality.

ESP32-C3 — Single RISC-V core at 160MHz, WiFi + BLE 5. Smaller, cheaper than the original, lower power. Good for simple WiFi sensors where you don’t need dual-core.

ESP32-C6 — RISC-V, WiFi 6, BLE 5, plus Thread and Zigbee (802.15.4). The obvious choice for Matter-compatible smart home devices and mesh networking applications.

ESP32-H2 — RISC-V, BLE 5 + Thread/Zigbee only (no WiFi). Designed for Thread border router accessories and Zigbee end devices where WiFi isn’t needed.

ESP32-P4 — Dual RISC-V cores, no wireless (intended to pair with an ESP32-C6 for connectivity), USB 2.0 HS, hardware image processing pipeline. For embedded camera, display, and vision applications.

For a new project in 2026: default to the ESP32-S3 if you want the best combination of capability and future-proofing; use the C6 if Thread/Matter compatibility matters; use the original ESP32 if you need maximum library compatibility and are working from existing code.

Development Environments

ESP-IDF (Espressif IoT Development Framework) — The official SDK, written in C/C++ with FreeRTOS. Verbose but complete. Best for production deployments where you need fine-grained control over power, memory, and peripherals.

# Install ESP-IDF
mkdir -p ~/esp && cd ~/esp
git clone --recursive https://github.com/espressif/esp-idf.git
cd esp-idf && ./install.sh esp32s3
source export.sh

# Create and build a project
idf.py create-project my-sensor
cd my-sensor
idf.py set-target esp32s3
idf.py build && idf.py flash

Arduino + ESP32 Arduino Core — The lowest barrier to entry. Thousands of libraries, familiar syntax, large community. Fine for prototyping; for production, the Arduino framework adds overhead and lacks some ESP-IDF features (notably finer-grained power management).

#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "your-network";
const char* password = "your-password";
const char* mqtt_server = "mqtt.example.com";

WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);

void setup() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  mqttClient.setServer(mqtt_server, 1883);
}

void loop() {
  if (!mqttClient.connected()) reconnect();
  mqttClient.loop();
  
  float temperature = readSensor();
  char payload[50];
  snprintf(payload, 50, "{\"temp\": %.2f}", temperature);
  mqttClient.publish("sensors/temperature", payload);
  delay(30000);
}

MicroPython — Python on the ESP32. Great for rapid prototyping and scripting-style projects. Significantly more memory overhead than C; not suitable for tight memory budgets or complex real-time requirements. But for a sensor that reads data and publishes to MQTT every 30 seconds, MicroPython is perfectly adequate and much faster to write.

import network
import time
from umqtt.simple import MQTTClient
import ujson

# Connect to WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('your-network', 'your-password')
while not wlan.isconnected():
    time.sleep(0.1)

# Connect to MQTT
client = MQTTClient('esp32-sensor', 'mqtt.example.com')
client.connect()

while True:
    temp = read_temperature()
    payload = ujson.dumps({'temp': temp})
    client.publish('sensors/temperature', payload)
    time.sleep(30)

Rust (esp-hal) — Memory safety without a garbage collector. The esp-hal ecosystem has matured considerably; esp-idf-svc provides bindings to ESP-IDF services. For production deployments where correctness matters and you want Rust’s compile-time safety, this is increasingly viable.

Power Management: The Production Challenge

Battery-powered IoT devices live or die on power management. The ESP32’s deep sleep mode draws as little as 10–20µA, but reaching that floor requires attention:

// ESP-IDF: configure deep sleep wake from timer
#include "esp_sleep.h"
#include "esp_log.h"

void app_main(void) {
    // Read sensor and publish data
    take_measurement_and_send();
    
    // Configure wake timer (60 seconds)
    esp_sleep_enable_timer_wakeup(60 * 1000000ULL);
    
    // Enter deep sleep — execution resumes from app_main after wake
    esp_deep_sleep_start();
}

Key power management considerations:

  • WiFi connection time dominates power budget. Each connection cycle uses ~150–300mA for 1–3 seconds. Minimise connection frequency; use static IP instead of DHCP to reduce connection time; consider MQTT persistent sessions to avoid repeated authentication.
  • GPIO hold in sleep — configure GPIO to hold their state during sleep or you’ll float unintended pins.
  • ADC accuracy — the onboard ADC is noisy; for precise analogue readings, use an external ADC or calibrate carefully.

OTA Updates

A production device needs to update its firmware remotely. ESP-IDF provides native OTA support:

#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"

void perform_ota_update(const char* update_url) {
    esp_http_client_config_t config = {
        .url = update_url,
        .cert_pem = server_cert_pem,  // Verify server identity
    };
    
    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };
    
    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK) {
        esp_restart();  // Reboot into new firmware
    }
}

The ESP32’s partition table supports A/B firmware slots. If the new firmware fails to boot and mark itself valid, the bootloader rolls back to the previous version. This rollback capability is essential for production deployments.

Security: Secure Boot and Flash Encryption

Default ESP32 builds are not secure — firmware is readable from flash, and there’s no verification that the firmware is genuine. For deployments where security matters:

Secure Boot — the bootloader verifies the firmware’s cryptographic signature before running it. Only firmware signed with your key will boot. Enabling it requires provisioning a key during manufacture and cannot be undone (it’s fused into eFuses).

Flash Encryption — encrypts the contents of flash memory, preventing readout even with physical access. Keys are generated on-device during provisioning.

Both features are configured via idf.py menuconfig under Security Features. Enable them before deploying to production; retrofitting to deployed devices requires a reflash procedure.

Getting Started in Practice

The ESP32’s documentation quality is high. Espressif’s ESP-IDF Programming Guide covers every peripheral in detail. For learning:

  1. Start with an ESP32-S3 devkit (widely available for £5–15)
  2. Install ESP-IDF and build the hello_world example
  3. Work through the wifi/getting_started/station example to establish connectivity
  4. Add MQTT publishing with the protocols/mqtt/tcp example
  5. Implement deep sleep once your basic application works

The community resources — Espressif’s forums, the r/esp32 subreddit, and the Arduino ESP32 GitHub issues — are active and generally helpful. Most problems have been encountered and solved by someone else.

For serious production deployments, the ESP32 reward design attention to security provisioning, OTA update infrastructure, and power budgeting. But for the majority of connected device projects — from home automation sensors to industrial monitoring nodes — it remains the most capable and practical choice at its price point.