Docs / Contact / sdks/micropython/boards

Supported boards

Two things decide whether a board fits your project, and neither is the RAM number printed on the chip:

  1. How much memory your code actually gets. A classic ESP32 has 520 KB of SRAM and hands MicroPython 157.8 KB of it, or 30%. A Raspberry Pi Pico W has half the SRAM and hands over 178.6 KB, or 68%. The spec sheet is not a ranking.
  2. What the firmware includes. Some capabilities are compiled into MicroPython and cannot be added afterwards. The local database is the big one: it needs the btree module, which upstream MicroPython enables for the ESP32 port (every ESP32 board, plus larger-flash ESP8266 builds) and leaves off in the rp2, stm32, alif, mimxrt, samd, nrf and renesas-ra ports. It is a per-port build decision, not a question of RAM: a $2 ESP32-C3 has it and boards with far more memory on the other ports don't.

Everything below is measured on hardware. Tested means we ran the SDK on that board and recorded the numbers. Expected means it's the right architecture and should work, but nobody has put one on a bench. If you're choosing hardware for a deployment, prefer a tested board.

At a glance

Board Chip RAM Free at boot Free after install SDK cost Local database Camera Status
ESP32-C3 400 KB 169.7 KB 146.3 KB 23.4 KB Yes No ✅ Measured
ESP32-WROOM (no PSRAM) 520 KB 157.8 KB 133.7 KB 24.2 KB Yes No ✅ Measured
ESP32-WROVER (PSRAM) 520 KB + PSRAM 1.99 MB 1.96 MB 23.9 KB Yes No ✅ Measured
Raspberry Pi Pico W 264 KB 178.6 KB 110.9 KB 67.8 KB No No ⚠️ Measured, caveats
ESP32-S2 / S3 320 KB+ Yes No Not yet measured
OpenMV AE3 (Alif) ~4 MB 3.90 MB 3.84 MB 64.8 KB No Yes ✅ Measured
OpenMV N6 (STM32) ~25 MB 24.42 MB 24.35 MB 65.8 KB No Yes ✅ Measured
OpenMV H7 / RT1062 Varies Unverified Yes Not yet measured

Every ESP32 row above runs the ROMFS install; the Pico W runs a filesystem install because ROMFS isn't available on that port. That single difference is most of the gap in the "SDK cost" column; see What the install path costs.

"Free after install" is what's left for your application with the full client loaded. "SDK cost" is what the client holds resident once loaded. All figures come from tests/measure_board.py on MicroPython 1.28 (1.22 on the Pico W), so they're directly comparable; see How these numbers were measured.

Feature support

Every supported board does MQTT publish/subscribe over TLS and file transfer, including device-to-device; those aren't listed below because they don't vary. What does vary:

Board Local database Buffer survives reboot Low-RAM install Camera & vision Flash from browser
ESP32-C3 Yes Yes Yes (ROMFS) No Yes
ESP32-WROOM / WROVER Yes Yes Yes (ROMFS) No Yes
ESP32-S2 / S3 Yes Yes Yes (ROMFS) No Yes
Raspberry Pi Pico W No No No No No (UF2)
OpenMV AE3 / N6 No No Not used Yes No (OpenMV IDE)
OpenMV H7 / RT1062 Unverified Unverified Not used Yes No (OpenMV IDE)

Reading the columns:

How much database do you need?

Most of the difference between these boards comes down to how much on-device storage your application actually wants. Work out which level you need, and the board choice mostly answers itself.

Level 0: Publish and forget

The device reads something, sends it, and the platform does the rest. No local storage at all.

python

from tendrl import Client

client = Client(client_db=False)
client.start()
client.publish({"temp_c": 21.4})

Runs on: every supported board, including the Pico W.

Level 1: Survive an outage

Undelivered messages are written to flash and flushed when the connection returns, so a network drop or a reboot normally doesn't lose data. On-device storage is best-effort, not guaranteed: a power cut mid-write can cost the buffered messages (see durability).

python

client = Client(offline_storage=True)
client.publish({"temp_c": 21.4}, write_offline=True)

Runs on: ESP32 boards only; they're the only ones with btree. On a Pico W or an OpenMV the same call still buffers in RAM and still flushes on reconnect, but the buffer does not survive a power cycle. See MicroTetherDB for the details.

Level 2: Decide locally

Keep a rolling window on the device and query it: thresholds, debounce, rate-of-change, "only alert if it's been high for ten minutes." The device stops being a dumb sensor and starts filtering what's worth sending.

This is what examples/tendrl_dht/simple_dht.py does: a DHT sensor that stores readings with a TTL, queries its own recent history, and only publishes when something is actually worth reporting.

Runs on: ESP32 boards. A C3 handles it comfortably.

Level 3: Learn over time

Weeks of history, structured queries across it, thresholds that adapt to the device's own baseline. examples/tendrl_dht/statistical_examples.py has three worked versions of this: trend detection with cloud sync, long-term pattern learning, and adaptive thresholds.

Runs on: ESP32 boards with room to spare. A C3 does it; PSRAM makes it comfortable. Size your retention with max_records rather than assuming; see MicroTetherDB for the memory characteristics.

Level 4: See

Camera capture, video streaming, and on-device machine vision need buffers measured in megabytes.

Runs on: OpenMV boards only. See Vision.

What the install path costs

The SDK's memory cost depends far more on how it's installed than on which board it runs on. Measured across six boards on four different MicroPython ports:

Install Board Port Peak during import Resident after
ROMFS ESP32-C3 esp32 29.9 KB 23.4 KB
ROMFS ESP32-WROOM esp32 30.8 KB 24.2 KB
ROMFS ESP32-WROVER esp32 30.5 KB 24.0 KB
Filesystem OpenMV AE3 alif 74.7 KB 64.8 KB
Filesystem OpenMV N6 stm32 75.9 KB 65.4 KB
Filesystem Raspberry Pi Pico W rp2 96.2 KB 67.8 KB

Both groups cluster tightly, and they don't overlap. ROMFS lands between 23.4 and 24.2 KB resident on boards with anywhere from 158 KB to 1.99 MB of free heap. A filesystem install lands between 64.8 and 67.9 KB across three different architectures: Alif, STM32 and RP2040. The cost is a property of the install path, not of the board.

The reason is mechanical: a ROMFS install executes in place from a read-only flash partition and never copies bytecode into the heap, while a filesystem install has to load it. That's a 2.8× difference in resident memory for identical code.

ROMFS is ESP32-only, which is why every non-ESP32 board sits in the second group.

Peak is the number that decides whether a board can install at all. A transient spike during import can exhaust a small heap even when the steady-state cost would have fit comfortably. Resident is what you pay afterwards.

Board by board

ESP32-C3: the default recommendation

Around $2, RISC-V, 400 KB of SRAM with 169.7 KB free at boot. With the ROMFS install the SDK imports in 163 ms and leaves 146.3 KB free, the most usable memory of any board we've measured without PSRAM, and comfortably more than a classic ESP32 has at boot.

Everything except camera work runs here, up to and including Level 3. If you have no specific reason to pick something else, pick this.

ESP32-WROOM (no PSRAM)

520 KB on the chip, of which MicroPython gets 157.8 KB (30%), the smallest share of any board here. The ROMFS install leaves 133.7 KB free.

Comfortable for Levels 0–3, though a C3 costs less and still gives you more room. Leave margin for TLS either way; handshakes fragment the heap.

ESP32-WROVER (PSRAM)

1.99 MB free at boot and 1.96 MB after the SDK loads. With PSRAM the memory question simply stops applying; the client's ~26 KB is a rounding error. Everything up to Level 3 runs without thought.

ESP32-S2 / S3

Not yet measured. ROMFS firmware ships for 4 MB and 16 MB S2/S3 boards, with or without PSRAM, so the low-RAM install is available.

Raspberry Pi Pico W

264 KB of SRAM with 178.6 KB free at boot (68%), the highest share of any board here. The SDK imports in 640 ms and leaves 110.9 KB for your application. Note that it ends up with the least free memory despite starting with the most: no ROMFS on this port means the bytecode is loaded into the heap rather than run from flash.

Three limitations, all of them firmware rather than memory:

Good for Level 0 telemetry where connectivity is reliable or short outages are acceptable. Not the right board where losing a buffer on power loss means losing data.

Verified end to end: provisioned from the device console, then heartbeats and a tagged publish both delivered over MQTT and stored.

OpenMV (AE3, N6, H7, RT1062)

Camera boards with megabytes of RAM, purpose-built for Level 4. Measured: the AE3 starts with 3.90 MB free and keeps 3.84 MB once the SDK loads; the N6 starts with 24.42 MB and keeps 24.35 MB. Memory is not a constraint on either.

What they don't have is btree, so the local database is unavailable, confirmed on both the AE3 (Alif port) and the N6 (STM32 port). Their range is Levels 0 and 4: publish, subscribe, transfer files, and do camera and vision work. Level 1's durable offline buffer isn't available; writes fall back to an in-RAM queue that flushes on reconnect but doesn't survive a reboot. H7 and RT1062 are unverified, but with two different ports both lacking it, assume they do too until you've checked.

Two other OpenMV-specific notes: their firmware ships without mip, so use the serial install paths, and flashing is done with the OpenMV IDE rather than from the browser.

Quick answers

If you… Choose
Are starting fresh and want the best value ESP32-C3
Need on-device history, trends, or adaptive thresholds An ESP32; no other board runs the database
Need a camera, video, or on-device vision OpenMV
Already own Pico Ws and only need telemetry Pico W, with client_db=False
Are deploying where power is intermittent An ESP32, so undelivered data survives a reboot

How these numbers were measured

Every figure on this page comes from one script, tests/measure_board.py, so the rows are directly comparable. Run it against your own board to reproduce them:

bash

mpremote connect /dev/ttyUSB0 soft-reset run tests/measure_board.py

Two things that will skew a measurement if you're reproducing these. Hard-reset the board first if anything has touched the network: a soft reset doesn't tear down the ESP32 WiFi driver, and a single network.WLAN(STA_IF).active(True) earlier in the session cost 12 KB that looked exactly like a real regression. And capability probes have to run after the measurement, not before: importing umqtt to check where it came from moved free-at-boot by ~3 KB on its own.

Board capabilities are detected on the device at connect time rather than read from this table, so custom MicroPython builds are reported accurately. If you compile a Pico W image with btree enabled, the device console will offer you the full install.