CircuitPython for Makers: Getting Started on ESP32 and Raspberry Pi Pico
CircuitPython is Adafruit's fork of MicroPython, built around one core idea: a microcontroller running CircuitPython shows up on your computer as a USB flash drive, and editing code is as simple as saving a .py file to that drive — no separate flashing step, no IDE upload dialog, no waiting for a compile. For anyone who's used MicroPython through Thonny or the ESP32 Arduino core, the appeal is the drag-and-drop workflow and Adafruit's enormous, consistently-documented library ecosystem (the "CircuitPython libraries" bundle) that gives nearly plug-and-play support for hundreds of sensors and displays. This guide covers getting CircuitPython running on both an ESP32 and a Raspberry Pi Pico, and where it fits relative to MicroPython and the Arduino IDE, which this site already covers in depth elsewhere.
CircuitPython vs. MicroPython vs. Arduino
CircuitPythonMicroPythonArduino (C++) DeploymentDrag-and-drop file to USB driveUpload via Thonny/ampy/mpremote over serialCompile and flash via IDE Library ecosystemHuge, Adafruit-maintained, very consistent APIsGood but more fragmented, community-maintainedLargest overall, but more variable quality PerformanceSlower — interpreted, extra abstraction layersSlower — interpretedFastest — compiled Board supportStrong on Adafruit boards and many ESP32/RP2040 boards; narrower than MicroPython overallVery broad — most ESP32/ESP8266/RP2040/STM32 boardsBroadest — virtually everything Best forFast prototyping with sensors/displays, teaching, projects that change oftenPrototyping where CircuitPython's board/library isn't available, tighter memory budgetsProduction firmware, performance-critical or memory-constrained projects, widest hardware supportNone of these is strictly "better" — they trade off differently. If you're bouncing between five different sensor breakouts trying to get a prototype working over a weekend, CircuitPython's library consistency saves real time. If you're shipping a finished, performance-sensitive firmware image, Arduino/C++ (or ESP-IDF directly) is still the right call. This site's existing ESP32 MicroPython guide covers the closely related MicroPython workflow if that's a better fit for a specific board.
Setting Up CircuitPython on a Raspberry Pi Pico
- Download the CircuitPython UF2 file for the Pico (or Pico W, which needs its own build with WiFi support) from circuitpython.org/downloads.
- Hold the BOOTSEL button on the Pico while plugging it into USB — it mounts as a mass-storage drive named RPI-RP2.
- Drag the downloaded .uf2 file onto that drive. The Pico reboots automatically and remounts as a drive named CIRCUITPY.
- Open the CIRCUITPY drive in any text editor and edit code.py directly — saving the file restarts the program immediately, no separate upload step.
- Download the CircuitPython library bundle matching your CircuitPython version from circuitpython.org/libraries, and copy only the specific library folders your project needs into the lib folder on the CIRCUITPY drive — copying the entire bundle will fill the Pico's limited flash.
Setting Up CircuitPython on an ESP32
ESP32 support works the same way but is more board-dependent than the Pico's — check circuitpython.org/downloads for a build matching your exact ESP32 board (plain ESP32, S2, S3, or C3), since flash size and pin mapping vary and a mismatched build can fail to boot or leave pins mapped incorrectly.
- Install esptool (pip install esptool) since most ESP32 boards need an initial serial flash rather than a drag-and-drop UF2 bootloader.
- Put the board in bootloader mode (usually holding BOOT while tapping RESET, board-dependent) and erase flash first: esptool.py --chip esp32s3 erase_flash.
- Flash the downloaded CircuitPython .bin for your exact board: esptool.py --chip esp32s3 write_flash -z 0x0 adafruit-circuitpython-*.bin.
- Reset the board — it should enumerate as a CIRCUITPY USB drive, the same as the Pico.
- Copy matching libraries from the bundle into lib as above.
Basic Project Structure
A CircuitPython project on the CIRCUITPY drive typically looks like:
- code.py — the main program, runs automatically on boot and re-runs on save.
- lib/ — folder for CircuitPython libraries (sensor drivers, display drivers, etc.), copied in as compiled .mpy files where available to save flash space.
- boot.py — optional, runs once before code.py and before USB is fully initialized; used for things like disabling the CIRCUITPY drive's writability for a "finished" deployed project.
- settings.toml — for WiFi credentials and other configuration values you don't want hardcoded into code.py, readable via os.getenv().
A minimal example reading an I2C temperature/humidity sensor and printing to the serial console:
import time import board import adafruit_ahtx0 i2c = board.I2C() sensor = adafruit_ahtx0.AHTx0(i2c) while True: print(f"Temp: {sensor.temperature:.1f} C Humidity: {sensor.relative_humidity:.1f} %") time.sleep(2)The pattern is consistent across almost every Adafruit-supported sensor and display library: instantiate the driver with an I2C or SPI bus object from board, then read properties or call simple methods — no manual register-level code required for the vast majority of common sensors, which is the main productivity win over writing the same drivers in raw MicroPython or C.
Where CircuitPython Falls Short
- Memory and performance. Tight loops, image processing, or anything latency-sensitive (audio, fast PWM control) will hit CircuitPython's interpreter overhead sooner than MicroPython and much sooner than compiled C — this site's ESP32 I2S audio guide, for example, is squarely Arduino/C++ territory rather than a good CircuitPython fit.
- Board support gaps. Not every ESP32 variant or third-party board has an official CircuitPython build; MicroPython's board support is broader, and Arduino's is broader still.
- WiFi/BLE stack maturity. CircuitPython's networking libraries are usable but less battle-tested than the ESP-IDF-based Arduino WiFi/BLE stacks for anything doing heavy or latency-sensitive networking (ESP-NOW mesh, BLE central-mode scanning) — check this site's ESP-NOW and BLE guides if that's the goal.
For quick sensor and display prototyping — especially projects that will get poked at, modified, and re-saved dozens of times during development — CircuitPython's drag-and-drop workflow and library consistency make it worth having in the toolbox alongside Arduino and MicroPython, even if it's not the right choice for every project on this site's more performance-sensitive ESP32 builds.
Related Guides
- ESP32 + MicroPython: Getting Started (vs Arduino IDE)
- Raspberry Pi Pico 2 and the RP2350: What's New and Getting Started
- Adafruit Feather RP2040
- Raspberry Pi Pico — First Steps with MicroPython
- Arduino vs ESP32: Which Should You Use? A Practical Comparison
- Getting Started with ESP32: GPIO, WiFi, and Your First Project
- Choosing the Right ESP32 Variant: ESP32, S2, S3, C3, and C6 Compared
- ESP32-P4 Explained: Espressif's High-Performance Vision MCU and When to Pair It With a C6 or S3