MicroPython
August 12, 2026 · View on GitHub
Platform notes for embedded MCUs and MicroPython on Unix. Quick start: ESP32 board guide.
Embedded (MCU)
Requirements
- A
board_config.pyfor your hardware. You can provide your own, or optionally install a prebuilt board package from pydevices — see board configs and install workflows. - Core packages (
displaydev,eventsys, …) via install workflows or GitHub MIP. - If you kept the optional
utils/package on the device, see Utils path setup for fallback when environment variables are unavailable or not set as recommended. Skip it if everything is installed flat into/lib.
Quick start with mpremote
See ESP32 board guide for the full install and hello workflow.
Brief version from the repo lib/ directory:
mpremote mip install "github:PyDevices/pydevices/board_configs/busdisplay/i80/wt32sc01-plus"
mpremote mount .
At the device REPL:
import utils.path # see ../utils.md#path-setup
import hello
WSL on Windows
Use WSL USB Manager to pass USB serial devices into WSL for mpremote.
Bus drivers
SPI displays use spibus.py; parallel I80 displays use i80bus.py. These install from GitHub only (viper). Board config packages pull them in automatically when needed.
For fastest buses, community C drivers (e.g. lvgl_micropython) can be wired through BusDisplay.
Background work (_thread)
On ESP32, MicroPython worker threads (_thread / mp_thread) have a very small
stack. Do not run network I/O, discovery, or other deep call stacks on a
new thread from a soft timer or input callback — that overflows the stack
(Stack protection fault in task mp_thread).
Queue the work and run it on the main tick instead: eventsys.Runtime.on_tick,
an LVGL lv.timer, or a soft multimer.Timer pump. Keep UI mutations on that
same main path. Desktop CPython can still use threads; this constraint is for
MCU MicroPython.
Unix (desktop MicroPython)
Use the same sibling source layout as CPython desktop, set
MICROPYPATH to pydevices-examples's src/utils plus the canonical hardware source
trees, and run micropython examples/<name>.py.
Install the desktop MIP board package or select a
pydevices/board_configs/sdldisplay config for SDL2 output.
Desktop SDL (usdl2)
SDLDisplay and the multimer sdl2 timer backend import usdl2 (an SDL2 subset). On desktop hosts, install it with the rest of the desktop board stack:
- CPython:
pydevices-desktopfrom TestPyPI (use the two-index install), which bundlesusdl2with the desktopboard_config - MicroPython / CircuitPython Unix (and
micropython.exe): the MIP desktop board package from pydevices (board_configs/desktop, which pulls indrivers/usdl2.py)
When a native usdl2 module is already present in the firmware or environment, that build is used; otherwise the pure-Python binding from pydevices-desktop / the MIP desktop board provides import usdl2. Desktop MicroPython / CircuitPython unix firmware that includes displayif freezes native usdl2 (it wins over MIP lib/usdl2.py). Timer auto-selection is unchanged (multimer still prefers _librt or threading backends first on each platform).
Frozen firmware
The product repo's pydevices/manifest.py lists the canonical core
packages for frozen MicroPython builds. Upstream port manifests remain
responsible for asyncio where multimer.AsyncTimer is needed.
Clone this repo as a sibling of micropython/ (and any native usermods such as
pygraphics),
then point FROZEN_MANIFEST at this file. On Make ports,
USER_C_MODULES is the workspace parent; build Windows with a variant that
enables MICROPY_PY_ASYNCIO and select (e.g. dev):
# workspace/
# micropython/
# pydevices/ ← product packages
# pydevices-examples/ ← examples
# pygraphics/ ← optional native module
cd micropython/ports/unix
make USER_C_MODULES=../../.. FROZEN_MANIFEST=../../../pydevices/manifest.py
cd micropython/ports/windows
make USER_C_MODULES=../../.. FROZEN_MANIFEST=../../../pydevices/manifest.py
# use a board/variant that enables asyncio/select as needed
(cmods ./build_mp.sh is an optional convenience wrapper for the same sibling layout — not required.)
See multimer and the example test tools.