This release is a pre-release and may not be stable for production use.
PyMCU
Python to bare-metal firmware — no runtime, no interpreter, no VM.
Explore the project »
Report a bug
·
Request a feature
·
Sponsor
[!IMPORTANT] Alpha 3 is out — v0.1.0a3 release notes. The language grows generators, async/await, dict/set literals, f-strings as values and type inference; the ARM targets (RP2040 / RP2350) reach feature parity with AVR — exceptions, floats, WiFi; and a brand-new PIC backend joins the family. Core compilation is stable and test-covered, but rough edges remain in error messages and tooling — if you hit a bug, please open an issue, it helps a lot.
Avoid
pymcu.hal.*during the alpha — the native HAL API may change between releases. Use the MicroPython or CircuitPython compat API instead; those are stable and community-specified.
PyMCU compiles a statically-typed subset of Python into bare-metal firmware for AVR, ARM (RP2040 / RP2350) and PIC — no runtime, no interpreter, no virtual machine. The same binary you would write in C.
The pitch in one table
LED blink for ATmega328P @ 16 MHz — all variants do the same thing:
configure PB5 as output, then loop LED on → wait 500 ms → LED off → wait 500 ms forever.
| Source | Total flash | SRAM |
|---|---|---|
C (avr-gcc -Os) |
176 B | 0 B |
| PyMCU (native HAL) | 162 B | 0 B |
| PyMCU (MicroPython API) | 162 B | 0 B |
| PyMCU (CircuitPython API) | 164 B | 0 B |
| Arduino (IDE defaults) | ~1 024 B | 9 B |
PyMCU produces a smaller binary than C here. Why?
Pin("PB5", Pin.OUT) and delay_ms(500) are resolved entirely at compile time — the
compiler sees through the Python objects and emits the same raw SBI/CBI port-toggle
instructions a C programmer would write by hand. The delay loop PyMCU generates happens to
use one fewer padding instruction than the loop avr-gcc -Os emits for this particular pattern.
The interrupt vector table and startup stub are identical fixed overhead in both toolchains.
Native HAL and MicroPython API produce byte-for-byte identical firmware — both compile down to the same SBI/CBI toggle and the same delay loop. The API is a zero-cost abstraction. CircuitPython is 2 bytes larger because the Direction.OUTPUT setter clears the PORT register before setting DDR, as the CircuitPython spec requires.
These numbers are for a minimal blink. Real programs that use SRAM (global variables, buffers) will emit a small zeroing loop at startup, just like C does.
For complex drivers (custom protocols, timing-critical bit-bang): expect 2-3x flash vs hand-written C. PyMCU is not competing with C — the goal is to make microcontroller development approachable in Python you already know, without the overhead of Arduino. The output is still 100-1000x smaller than any embedded Python interpreter.
Write code you already know
Pick the API that fits your background. Both compile to the same bare-metal firmware.
CircuitPython
# The exact same code that runs on a Pico under CircuitPython
import board
import digitalio
import time
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = True
time.sleep(0.5)
led.value = False
time.sleep(0.5)
MicroPython
# The exact same code that runs on a Pico under MicroPython
from machine import Pin
from utime import sleep_ms
led = Pin(13, Pin.OUT)
while True:
led.value(1)
sleep_ms(500)
led.value(0)
sleep_ms(500)
pymcu build # → dist/firmware.hex (56 bytes flash, 0 bytes SRAM)
pymcu flash # → avrdude upload to Arduino Uno
First binary in under 5 minutes
1. Install
pipx install --pip-args=--pre "pymcu-compiler[avr]" # AVR (ATmega / ATtiny)
pipx install --pip-args=--pre "pymcu-compiler[arm]" # RP2040 / RP2350 (Pico / Pico 2)
pipx install --pip-args=--pre "pymcu-compiler[pic]" # PIC16
pipx install --pip-args=--pre "pymcu-compiler[all]" # everything
Requires Python 3.11+ and pipx. Each extra bundles its full toolchain
(compiler backend + assembler/linker binaries) — no system packages needed.
Package name: PyMCU is published as
pymcu-compileron PyPI while a PEP 541 request to reclaim thepymcuname is under review. Once approved, apymcumetapackage will aliaspymcu-compiler— installs and project configs will stay compatible.
2. Create a project
pymcu new blink
cd blink
3. Choose your API and write the program
CircuitPython style — add pymcu-circuitpython to dependencies:
# pyproject.toml
[project]
dependencies = ["pymcu-compiler[avr]", "pymcu-circuitpython"]
[tool.pymcu]
board = "arduino_uno"
frequency = 16000000
# src/main.py
import board
import digitalio
import time
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
while True:
led.value = True
time.sleep(0.5)
led.value = False
time.sleep(0.5)
MicroPython style — add pymcu-micropython to dependencies:
# pyproject.toml
[project]
dependencies = ["pymcu-compiler[avr]", "pymcu-micropython"]
[tool.pymcu]
board = "arduino_uno"
frequency = 16000000
# src/main.py
from machine import Pin
from utime import sleep_ms
led = Pin(13, Pin.OUT)
while True:
led.value(1)
sleep_ms(500)
led.value(0)
sleep_ms(500)
4. Build and flash
pymcu build
# Compiling src/main.py...
# → dist/firmware.hex
pymcu flash --port /dev/cu.usbmodem*
# avrdude: flash verified
Choosing an API
| Package | API surface | Install |
|---|---|---|
pymcu-circuitpython |
digitalio, analogio, busio, pwmio, time, board, neopixel |
pip install pymcu-circuitpython |
pymcu-micropython |
machine (Pin/UART/ADC/PWM/SPI/I2C/Timer/WDT), utime |
pip install pymcu-micropython |
pymcu.hal.* |
Direct register-level HAL — lowest overhead | pymcu-stdlib (installed automatically with pymcu-compiler) |
Start with MicroPython or CircuitPython — they are stable, community-specified,
and backed by real hardware compatibility guarantees. The pymcu.hal.* native HAL is
functional but its API may change between alpha releases — avoid it unless you need
direct register access not yet covered by the compat layers.
Supported targets
| Architecture | Chips |
|---|---|
| AVR (ATmega) | ATmega48/88/168/328P, ATmega2560, ATmega32U4 |
| AVR (ATtiny) | ATtiny25/45/85, ATtiny24/44/84, ATtiny13/13A, ATtiny2313/4313 |
| ARM (Cortex-M0+ / M33) | RP2040 (Pico / Pico W), RP2350 (Pico 2 / Pico 2 W) — incl. PIO and CYW43 WiFi |
| PIC (mid-range) | PIC16F84A, PIC16F877A — new in alpha 3 |
HAL coverage (ATmega328P / Arduino Uno)
| Module | Features |
|---|---|
pymcu.hal.gpio |
Pin — high / low / toggle / irq / pulse_in |
pymcu.hal.uart |
UART — write / read / println / RX interrupt |
pymcu.hal.adc |
AnalogPin — poll + interrupt; internal temperature |
pymcu.hal.timer |
Timer(n, prescaler) — CTC mode; millis() / micros() |
pymcu.hal.pwm |
PWM — multi-channel; set_duty / set_freq |
pymcu.hal.spi |
SPI + SoftSPI |
pymcu.hal.i2c |
I2C + SoftI2C |
pymcu.hal.eeprom |
EEPROM — write(addr, val) / read(addr) |
pymcu.hal.watchdog |
Watchdog — enable / disable / feed |
pymcu.hal.power |
sleep_idle / sleep_adc_noise / sleep_power_down / sleep_power_save / sleep_standby / sleep_extended_standby |
Drivers: DHT11, DS18B20, HD44780 LCD, SSD1306 OLED, MAX7219 8x8 matrix, BMP280, WS2812 NeoPixel.
What Python features are supported
PyMCU accepts Python syntax but enforces a strict compile-time type system.
Supported:
- Integer types:
uint8,int8,uint16,int16,uint32,int32,float— with type inference for unannotateddefparameters and returns - Fixed arrays
buf: uint8[16], heap-bounded listsx: list[uint8] = list(), equal-length slice assignment for,while,if,match / case,with,class,@inline,lambda- Generators (
yield),async/awaitwithasyncio.run/gather dict/setliterals as closed compile-time lookup tables, pluspymcu.collections.FixedDictfor mutable fixed-capacity maps — still no heap- f-strings with runtime interpolations and format specs, as stream writes or values
try / except / raise / finallywith cross-function propagation (AVR and ARM)@interruptISR handlers,asm("...")inline assembly (with operands on ARM)- CircuitPython and MicroPython compat packages, plus
pymcu lintto vet a port
Not supported:
- Open-ended
dict/setmutation beyondFixedDict's fixed capacity (no heap hash tables) - Closures capturing mutable variables — use explicit parameters
*args/**kwargs, reflection (getattr/setattr/eval)
The compiler rejects unsupported features with a clear error at compile time. See the Language Limitations page for the full list.
CLI reference
| Command | Description |
|---|---|
pymcu new <name> |
Scaffold a new project |
pymcu build |
Compile src/ → dist/firmware.hex |
pymcu flash |
Upload via avrdude |
pymcu clean |
Remove build artifacts |
Sustainability
Post-alpha development will be slower and community-driven. If PyMCU saves you time, consider sponsoring the project — the goal is $200-300/month to cover the AI tooling costs that made this first release possible and keep active development going.
License
All components are licensed under the MIT License. Your compiled firmware output is entirely yours — no runtime license, no attribution required.
Contributing
See CONTRIBUTING.md and LANGUAGE_ROADMAP.md.
Credits
Special thanks to Richard Wardlow, creator of the original pyMCU project (2012). See CREDITS.md for the full acknowledgement.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymcu_compiler-0.1.0a7-py3-none-win_arm64.whl.
File metadata
- Download URL: pymcu_compiler-0.1.0a7-py3-none-win_arm64.whl
- Upload date:
- Size: 2.7 MB
- Tags: Python 3, Windows ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd1c84ab4bbed76479cf93e44cb8104a6b1f38662ffa6770fa34127267b8aac5
|
|
| MD5 |
5706b034d6f243104f9ee2f9ba1c7b6f
|
|
| BLAKE2b-256 |
aae3bfb7f7340d408ed9861907e2ca386f4670e01d3f1823c84d2eb7421fef37
|
Provenance
The following attestation bundles were made for pymcu_compiler-0.1.0a7-py3-none-win_arm64.whl:
Publisher:
publish.yml on PyMCU/PyMCU
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymcu_compiler-0.1.0a7-py3-none-win_arm64.whl -
Subject digest:
cd1c84ab4bbed76479cf93e44cb8104a6b1f38662ffa6770fa34127267b8aac5 - Sigstore transparency entry: 2496796108
- Sigstore integration time:
-
Permalink:
PyMCU/PyMCU@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Branch / Tag:
refs/tags/v0.1.0a7 - Owner: https://github.com/PyMCU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pymcu_compiler-0.1.0a7-py3-none-win_amd64.whl.
File metadata
- Download URL: pymcu_compiler-0.1.0a7-py3-none-win_amd64.whl
- Upload date:
- Size: 2.8 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
64dd79d7257bcb755d561bfad1a1e6ce2ccbaf50741a0d19cc8a5c4df5b658a4
|
|
| MD5 |
6dd953e932ca93ae559245ac250f8fba
|
|
| BLAKE2b-256 |
0a50c14d7a226fdbea524f52901f771839559cc58e498c54adfeee24c438d13d
|
Provenance
The following attestation bundles were made for pymcu_compiler-0.1.0a7-py3-none-win_amd64.whl:
Publisher:
publish.yml on PyMCU/PyMCU
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymcu_compiler-0.1.0a7-py3-none-win_amd64.whl -
Subject digest:
64dd79d7257bcb755d561bfad1a1e6ce2ccbaf50741a0d19cc8a5c4df5b658a4 - Sigstore transparency entry: 2496796010
- Sigstore integration time:
-
Permalink:
PyMCU/PyMCU@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Branch / Tag:
refs/tags/v0.1.0a7 - Owner: https://github.com/PyMCU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_x86_64.whl.
File metadata
- Download URL: pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_x86_64.whl
- Upload date:
- Size: 3.0 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b45915093e0153794dcd3f83ea9ac94bc86409acd302bf649102fe6d30a019d2
|
|
| MD5 |
b611146413c95f8d2a7a34cc4798fcbc
|
|
| BLAKE2b-256 |
ed34204c444278608d946622c5d21deb5f416ed12ebb4b00038d6854c1c06b26
|
Provenance
The following attestation bundles were made for pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_x86_64.whl:
Publisher:
publish.yml on PyMCU/PyMCU
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_x86_64.whl -
Subject digest:
b45915093e0153794dcd3f83ea9ac94bc86409acd302bf649102fe6d30a019d2 - Sigstore transparency entry: 2496796379
- Sigstore integration time:
-
Permalink:
PyMCU/PyMCU@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Branch / Tag:
refs/tags/v0.1.0a7 - Owner: https://github.com/PyMCU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_aarch64.whl.
File metadata
- Download URL: pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_aarch64.whl
- Upload date:
- Size: 2.8 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a16d7a0bc07574ab1179a4f5060359972567cbc9be86dd0b99db379219f25c41
|
|
| MD5 |
0a6d3816bbf753eccfc03c362be986ae
|
|
| BLAKE2b-256 |
a314d14aa70fcced01d5d3b6aea047943d4a11f01e2f1ac4a6f4eb126210bb89
|
Provenance
The following attestation bundles were made for pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_aarch64.whl:
Publisher:
publish.yml on PyMCU/PyMCU
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymcu_compiler-0.1.0a7-py3-none-manylinux_2_17_aarch64.whl -
Subject digest:
a16d7a0bc07574ab1179a4f5060359972567cbc9be86dd0b99db379219f25c41 - Sigstore transparency entry: 2496796276
- Sigstore integration time:
-
Permalink:
PyMCU/PyMCU@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Branch / Tag:
refs/tags/v0.1.0a7 - Owner: https://github.com/PyMCU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pymcu_compiler-0.1.0a7-py3-none-macosx_14_0_arm64.whl.
File metadata
- Download URL: pymcu_compiler-0.1.0a7-py3-none-macosx_14_0_arm64.whl
- Upload date:
- Size: 2.8 MB
- Tags: Python 3, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eebc2f6105140c822c241f48c0032f26a7894787be653944bacfe7160f96d76a
|
|
| MD5 |
831ea40cb1e55bed3f989a353752887d
|
|
| BLAKE2b-256 |
44720c5dcb92996113255c4e10085e537e6944c169f372a12d1452841a883cec
|
Provenance
The following attestation bundles were made for pymcu_compiler-0.1.0a7-py3-none-macosx_14_0_arm64.whl:
Publisher:
publish.yml on PyMCU/PyMCU
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymcu_compiler-0.1.0a7-py3-none-macosx_14_0_arm64.whl -
Subject digest:
eebc2f6105140c822c241f48c0032f26a7894787be653944bacfe7160f96d76a - Sigstore transparency entry: 2496796584
- Sigstore integration time:
-
Permalink:
PyMCU/PyMCU@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Branch / Tag:
refs/tags/v0.1.0a7 - Owner: https://github.com/PyMCU
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6b295267c1bcafce62329c13ac8a50087cb5b948 -
Trigger Event:
release
-
Statement type: