Skip to main content

Python-to-MCU compiler driver

Project description


PyMCU

PyMCU

Python to bare-metal firmware — no runtime, no interpreter, no VM.
Explore the project »

Report a bug · Request a feature · Sponsor

PyPI version Python versions License Last commit Open issues Sponsor


[!IMPORTANT] Alpha — v0.1 — under active development. Core AVR compilation is stable and test-covered, but rough edges remain in error messages and tooling. Stability is expected for the supported feature set — 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 AVR firmware — 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
from time import sleep_ms

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    sleep_ms(500)
    led.value = False
    sleep_ms(500)

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 "pymcu-compiler[avr]"

Requires Python 3.11+ and pipx. The [avr] extra includes the AVR toolchain.

Package name: PyMCU is published as pymcu-compiler on PyPI while a PEP 541 request to reclaim the pymcu name is under review. Once approved, a pymcu metapackage will alias pymcu-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", "pymcu-circuitpython"]

[tool.pymcu]
board     = "arduino_uno"
frequency = 16000000
# src/main.py
import board
import digitalio
from time import sleep_ms

led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT

while True:
    led.value = True
    sleep_ms(500)
    led.value = False
    sleep_ms(500)

MicroPython style — add pymcu-micropython to dependencies:

# pyproject.toml
[project]
dependencies = ["pymcu-compiler", "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

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 EEPROMwrite(addr, val) / read(addr)
pymcu.hal.watchdog Watchdogenable / disable / feed
pymcu.hal.power sleep_idle / power_down / standby

Drivers: DHT11, DS18B20, LM35, HD44780 LCD, SSD1306 OLED, MAX7219, 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
  • Fixed arrays buf: uint8[16] and heap-bounded lists x: list[uint8] = list()
  • for, while, if, match / case, with, class, @inline, lambda
  • @interrupt ISR handlers, asm("...") inline assembly
  • try / except / raise / finally (AVR only; raise and except in same function)
  • CircuitPython and MicroPython compat packages

Not supported:

  • dict / set (hash tables require heap)
  • Runtime f"value={x}" with non-constant expressions (compile-time constants only)
  • async / await — use @interrupt + polling loop instead
  • Closures capturing mutable variables — use explicit parameters
  • *args / **kwargs

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.

Sponsor on GitHub


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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pymcu_compiler-0.1.0a1.post1-py3-none-win_arm64.whl (2.4 MB view details)

Uploaded Python 3Windows ARM64

pymcu_compiler-0.1.0a1.post1-py3-none-win_amd64.whl (2.5 MB view details)

Uploaded Python 3Windows x86-64

pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_x86_64.whl (2.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_aarch64.whl (2.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

pymcu_compiler-0.1.0a1.post1-py3-none-macosx_15_0_arm64.whl (2.5 MB view details)

Uploaded Python 3macOS 15.0+ ARM64

pymcu_compiler-0.1.0a1.post1-py3-none-macosx_13_0_x86_64.whl (2.7 MB view details)

Uploaded Python 3macOS 13.0+ x86-64

File details

Details for the file pymcu_compiler-0.1.0a1.post1-py3-none-win_arm64.whl.

File metadata

  • Download URL: pymcu_compiler-0.1.0a1.post1-py3-none-win_arm64.whl
  • Upload date:
  • Size: 2.4 MB
  • Tags: Python 3, Windows ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pymcu_compiler-0.1.0a1.post1-py3-none-win_arm64.whl
Algorithm Hash digest
SHA256 7e40b48a31a53d8fb696429be5b54b8082dc86fa5b6789e94f673483ca237034
MD5 4a9f68f4267f6eb35d1839b164a9692b
BLAKE2b-256 557972538ba5dea4cb7c82b4d2141fc8490121faca2eed1f1634e6c1b2479a7e

See more details on using hashes here.

File details

Details for the file pymcu_compiler-0.1.0a1.post1-py3-none-win_amd64.whl.

File metadata

  • Download URL: pymcu_compiler-0.1.0a1.post1-py3-none-win_amd64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pymcu_compiler-0.1.0a1.post1-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 154ec265d10922d150f9067cd3918a6fc7f8bae14c22ad0a5ab57e01d81ed41a
MD5 c76dfbfab003c4af71bb939ccfe32e9f
BLAKE2b-256 42137527763794c07ea7f7057cda25bb1188c982174cdf3e54cfdab06fa5ee4d

See more details on using hashes here.

File details

Details for the file pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_x86_64.whl.

File metadata

  • Download URL: pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_x86_64.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: Python 3, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 96d022b5f347d872eeeb5adbfaaf327a2c6c5c9c2f01e17028dccd5e65867ba9
MD5 4a1adca0ff29da59df365f76d989589b
BLAKE2b-256 7ce28e3709fc38b20e039540d7552c073672a24ac394fb43e80afa409c1d7e99

See more details on using hashes here.

File details

Details for the file pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_aarch64.whl.

File metadata

  • Download URL: pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_aarch64.whl
  • Upload date:
  • Size: 2.6 MB
  • Tags: Python 3, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pymcu_compiler-0.1.0a1.post1-py3-none-manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 9cf4efdb6fd1dc887c3072ad9ab68435f8da95cc1b2fd9de7b957f3dd4d73799
MD5 5a75d8a7a6af44b186747479e2475b97
BLAKE2b-256 d2405a37643a2eca7d0573e2af0ebba7d34b66f9ec97e59ad2bba5be48888a06

See more details on using hashes here.

File details

Details for the file pymcu_compiler-0.1.0a1.post1-py3-none-macosx_15_0_arm64.whl.

File metadata

  • Download URL: pymcu_compiler-0.1.0a1.post1-py3-none-macosx_15_0_arm64.whl
  • Upload date:
  • Size: 2.5 MB
  • Tags: Python 3, macOS 15.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pymcu_compiler-0.1.0a1.post1-py3-none-macosx_15_0_arm64.whl
Algorithm Hash digest
SHA256 1a8b36f4d0ddda505891c976c8cc3a0ea350dade11085c4ba567a8a3384bfebb
MD5 db71e8b72204caa9005176ee5d4f093f
BLAKE2b-256 89ee15dd1adbc4e871dcbef5380d736b5e35061cc3243ad044b5d00161a9fd4c

See more details on using hashes here.

File details

Details for the file pymcu_compiler-0.1.0a1.post1-py3-none-macosx_13_0_x86_64.whl.

File metadata

  • Download URL: pymcu_compiler-0.1.0a1.post1-py3-none-macosx_13_0_x86_64.whl
  • Upload date:
  • Size: 2.7 MB
  • Tags: Python 3, macOS 13.0+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for pymcu_compiler-0.1.0a1.post1-py3-none-macosx_13_0_x86_64.whl
Algorithm Hash digest
SHA256 59e166cd2de24d30ed9a8969f506d746caf5d7a576aca1f387926d645d29f690
MD5 2b8c3ac2d62f0770f2a959b28bb6a5bb
BLAKE2b-256 f73d7584f680ac5a249c5ee3482864db1f5432e70b0799af2bbd96f136d5bfab

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page