ESP32

The ESP32 family from Espressif did something unusual: it put a competent 32-bit microcontroller, Wi-Fi, and Bluetooth on a single chip for a few dollars, with a genuinely good open-source SDK. Before it, adding Wi-Fi to an embedded product meant a separate module, a separate host processor, and a licensing conversation. After it, connectivity became the default assumption for hobby and commercial IoT alike.

The practical consequence for a developer is that an ESP32 is not really a bare-metal target. ESP-IDF ships FreeRTOS as the runtime, a full TCP/IP stack, TLS, filesystems, OTA infrastructure, and a driver layer. You're writing tasks, not a superloop — which means the RTOS concepts apply from your first project rather than being something you graduate into.

TL;DR

Quick Example

Connect to Wi-Fi, read a sensor, publish over MQTT, and sleep — the archetypal battery IoT node:

The energy shape of that program is worth internalizing: nearly all of the power goes to the two seconds of Wi-Fi association, not to the sensor read or the publish. Optimizing battery life on an ESP32 is almost entirely about connecting less often and associating faster.

Choosing a Variant

Practical guidance: C3 for cost-sensitive Wi-Fi nodes, S3 when you need performance, USB, or camera and audio work, C6 for Matter, Thread, or Zigbee, and the original ESP32 when you want the deepest pool of existing examples. RAM and flash vary by module, and PSRAM is available on some parts — check the specific module rather than the chip family.

Core Concepts

ESP-IDF and FreeRTOS

app_main is already a FreeRTOS task, so concurrency is available from the start:

Note the stack size unit: ESP-IDF's xTaskCreate takes bytes, unlike vanilla FreeRTOS which takes words. 4096 is 4 KB. Wi-Fi and TLS operations need generous stacks — 4–8 KB is typical, and stack overflow shows up as a confusing crash rather than a clear error.

The framework is built on components with CMakeLists.txt and idf.py:

menuconfig is where Wi-Fi buffers, log levels, partition layout, power management, and TLS options live. Much of ESP32 tuning is configuration rather than code.

Arduino vs. ESP-IDF

The Arduino core is genuinely built on top of ESP-IDF, so you can call IDF APIs from an Arduino sketch. Start in Arduino if you want a fast result; move to IDF when you need deep sleep tuning, OTA, secure boot, or proper debugging — which is to say, when you're building a product.

Power management

This is where ESP32 projects succeed or fail on battery.

Deep sleep resets the CPU: execution resumes at app_main, and everything except RTC_DATA_ATTR variables and NVS is gone. Design the firmware as a short, stateless routine that runs and sleeps rather than a long-lived loop.

The ULP coprocessor is the advanced tool — a tiny always-on core that can poll a sensor at microamp cost and wake the main CPU only when a threshold is crossed. For a device sampling frequently and reporting rarely, it's the difference between weeks and years of battery life.

NVS and partitions

NVS is a wear-levelled key-value store in a flash partition — the right place for Wi-Fi credentials, device configuration, and counters. It handles erase-block management, which raw flash writes do not.

The partition table defines flash layout, and the OTA layout is what makes safe updates possible:

Two app slots plus an otadata partition recording which is active means an update writes to the inactive slot and the bootloader can roll back if the new image fails to confirm itself. See OTA Firmware Updates.

Wi-Fi and Bluetooth coexistence

Both radios share one antenna and one RF front end. Running Wi-Fi and BLE simultaneously works — the coexistence arbiter time-slices them — but both lose throughput and latency becomes less predictable. Where the product allows, use BLE for provisioning and then switch to Wi-Fi, rather than running both continuously.

Best Practices

Design the firmware around the sleep cycle

For a battery device, the structure is: wake → connect → do the minimum → sleep. Anything that extends the awake window costs battery directly. Measure the actual awake duration and current draw with a power profiler; the result is usually dominated by something you didn't expect, like DHCP or DNS.

Cache Wi-Fi connection parameters

A full association with DHCP and DNS can take several seconds. Storing the channel, BSSID, and a static IP in RTC memory or NVS cuts reconnect time dramatically — often from 3 seconds to under 500 ms, which is a several-fold improvement in battery life for a periodic reporter.

Give tasks generous stacks and check the high-water mark

TLS handshakes and Wi-Fi callbacks are stack-hungry. Start at 4–8 KB for anything touching the network, then measure with uxTaskGetStackHighWaterMark and trim. A stack overflow here manifests as a Guru Meditation Error with a corrupted backtrace, which is unpleasant to diagnose.

Use NVS, not raw flash writes

NVS handles wear levelling and power-loss safety. Writing configuration directly to a flash partition risks corruption on an interrupted write and wears out one erase block.

Enable a watchdog and handle brownouts

The task watchdog catches a hung task; the interrupt watchdog catches a blocked ISR. The brownout detector matters specifically on battery and on boards with marginal regulators — a Wi-Fi transmit burst can pull 240 mA and drop the rail, corrupting a flash write in progress.

Pin timing-sensitive work to the app core

On dual-core parts, the Wi-Fi and Bluetooth stacks run on core 0. Pinning your real-time work to core 1 with xTaskCreatePinnedToCore keeps it away from radio interrupt load.

Enable secure boot and flash encryption for products

Without them, anyone with physical access can read your firmware out of flash — including any credentials in it — and write their own image. Both are one-way fuses, so test the process thoroughly on sacrificial hardware before enabling in production. See IoT Security.

Use the log level configuration rather than removing logs

ESP_LOGD and friends compile out at the configured level, so you can keep diagnostics in the source and ship with them disabled. Verbose logging over UART also costs real time and power when the radio is off.

Common Mistakes

Expecting RAM to survive deep sleep

Blocking in an event handler

Sleeping with the radio still on

Undersized task stacks

Using strapping pins as ordinary GPIO

The specific pins differ by variant, and this is a board-design mistake that's expensive to discover after fabrication. ADC2 pins are similarly unusable while Wi-Fi is active.

Ignoring power supply requirements

FAQ

ESP32 or Raspberry Pi Pico W?

ESP32 for connectivity-heavy work — its Wi-Fi and BLE stacks are more mature, power management is far more capable, and OTA and security features are first-class. Pico (RP2040/RP2350) for exceptional documentation, PIO for custom hardware protocols, and dual-core work where you want simple, predictable behavior. For a battery-powered Wi-Fi sensor, ESP32.

Can I run MicroPython on it?

Yes, and it's excellent for prototyping, education, and low-volume projects — the REPL over serial makes iteration very fast. The costs are RAM (the interpreter takes a substantial share), power (less control over sleep), and performance. Most commercial firmware is C with ESP-IDF, and MicroPython is a genuinely good way to validate an idea first.

How long will it run on a battery?

Almost entirely determined by duty cycle. A device that wakes every 5 minutes, stays up 3 seconds, and deep-sleeps otherwise averages a few hundred microamps and will run for months to years on a couple of AA cells. The same device kept continuously connected draws 50–150 mA and lasts a day. Model the duty cycle before choosing a battery — and measure, because the first version is always awake longer than intended.

Does it support Matter and Thread?

The C6 and H2 have 802.15.4 radios and support Thread and Zigbee; Espressif's Matter SDK builds on ESP-IDF and supports Matter over Wi-Fi on Wi-Fi parts and over Thread on 802.15.4 parts. If Matter compatibility is a product requirement, the variant choice is largely made for you.

How do I debug beyond serial printing?

JTAG works — some variants (S3, C3, C6) have built-in USB JTAG requiring no external probe, and OpenOCD plus GDB gives breakpoints and memory inspection. ESP-IDF also provides core dumps written to flash on panic, heap corruption detection, and a task watchdog that reports which task hung. The built-in USB JTAG on newer parts is a significant quality-of-life improvement over the ESP32's external-probe requirement.

Is it secure enough for a commercial product?

The hardware provides secure boot (signature verification), flash encryption, an eFuse-backed key store, and a hardware RNG and crypto accelerators — a sound foundation. Whether a specific product is secure depends on using them: per-device keys, enabled secure boot, signed OTA, and disabled debug interfaces in production. Shipping with defaults means shipping readable firmware. See IoT Security.

Related Topics

References