Questions people ask¶
Short answers, each one true of the code as it ships today. Every answer links to the library that does the work.
Why does my board freeze while it talks to the network?¶
Because most Python libraries for boards wait with the whole program stopped. A request to a slow server, or a WiFi retry against a router that is unplugged, holds the processor until it finishes, and every other job on the board waits with it: the status LED, the button, the sensor read.
ChuMicro libraries work in small steps instead. Each pass through your loop, every library does a little and hands control back, so a dead network costs you the network and nothing else. The LED keeps blinking, the button keeps answering, and you decide how long to wait and what happens when the waiting is over.
How do I use MQTT on CircuitPython without freezing everything else?¶
Call handle() once per pass through your loop. MQTT is the small
publish-and-subscribe protocol most home-automation setups speak, and
chumicro-mqtt runs
each step of it (connect, subscribe, publish, the acknowledgements
that follow) as a piece of work that finishes in one tick.
from chumicro_timing import ticks_ms
from chumicro_mqtt import MQTTClient
client = MQTTClient(socket_factory, config)
client.connect() # returns right away; no I/O here yet
while True:
now = ticks_ms()
client.handle(now) # one step of protocol work
led.value = not led.value # your program keeps running
QoS 0 and 1, last will, retained messages, and TLS all work this way.
Does this use async and await?¶
No, and your own program still can. The libraries make progress
through check and handle calls that return immediately, so many of
them share one loop without a scheduler.
That is a rule about what is inside the libraries. Your application
can be an asyncio program, a thread, or a plain while True: loop.
You tick the client from wherever your loop lives.
The reason is measured rather than stylistic. CircuitPython compiles
every await into a method dispatch that allocates a fresh generator
each time it resumes, and its asyncio port has carried a broken socket
layer since 2021. MicroPython compiles the same await to a single
bytecode. Building on async means paying heap churn on one runtime
or quietly supporting only the other, so the libraries use generators,
which are one bytecode on both.
How do I keep WiFi connected on a Pico W or an ESP32?¶
Hand the radio to
chumicro-wifi and
read its state. It connects, retries with backoff, reconnects after a
drop, and reports every transition (disconnected, connecting,
connected, reconnecting, failed) so your app can react to a
change rather than poll for one.
It owns the radio outright, which matters: firmware-level auto-connect settings compete with a supervisor and produce reconnect storms. One component does the reconnecting.
Does anything ever block?¶
One thing does, and it is worth knowing before you pick a runtime.
CircuitPython's firmware exposes only a blocking wifi.radio.connect(),
with no non-blocking variant, so while the WiFi service is connecting
or reconnecting on a CircuitPython board, that call holds the loop for
up to your connect timeout (15 seconds by default). Once the link is
up, the loop runs at full speed again.
MicroPython's wlan.connect() associates in the background on both
ESP32 and Pi Pico W, so the same code never stalls there. If a
never-stalling connect matters to your project, choose MicroPython on
those boards.
Everything after association, on both runtimes, runs a step at a time.
Can I run the same code on CircuitPython and MicroPython?¶
Yes, and on the Python on your laptop. Every library runs unmodified on all three, so you can write and test a program at your desk and deploy those same files to a Pico W running CircuitPython or an ESP32 running MicroPython.
Each library takes its I/O and its clock as arguments, which is what
makes this work. A socket needs four methods (recv_into, send,
close, setblocking) and a clock needs three (ticks_ms,
ticks_add, ticks_diff). Anything with those methods is accepted,
including a fake you write for a test.
How do I store a value that survives a reboot?¶
Use chumicro-kvstore, which reads and writes like a dictionary and picks the right storage for the board it finds itself on: NVM on CircuitPython, NVS on ESP32 MicroPython, LittleFS elsewhere, and memory on your laptop.
commit_if_changed() writes only when a value actually changed, which
keeps a counter you update every loop from wearing out the flash.
How much space do these take on a board?¶
A few kilobytes each. CI holds every library under a flash-size
ceiling, measured as MicroPython bytecode: 2,595 bytes for
chumicro-timing, 7,339 for chumicro-wifi, and 21,516 for
chumicro-mqtt, the largest of them. Raising a ceiling takes a
measured justification in the commit that does it.
Install only what you use. A project that needs a timer installs a timer.
How do I install one library on a board?¶
One command per runtime, with the library name in it:
circup bundle-add ChuMicro/ChuMicro-Bundle && circup install chumicro_mqtt
mpremote mip install github:ChuMicro/ChuMicro-Bundle/chumicro_mqtt
pip install chumicro-mqtt
The first line is CircuitPython through circup, the second MicroPython through mip, the third the Python on your computer, which is how you run tests without a board plugged in.
Can I test my project without hardware?¶
Yes. The libraries run on desktop Python, and each one ships fakes for the parts that would otherwise need a board: a fake socket, a fake clock, a fake WiFi radio, a recording transport. Tests drive time forward instead of sleeping, so a suite that covers a 30-second timeout finishes in milliseconds.
When you do have a board, the same pytest runs on the silicon.