Public alpha — out now on PyPI
PyMCU, the Python compiler for microcontrollers
Python, compiled to bare-metal machine code. No interpreter, no heap, no garbage collection — deterministic by design, and small enough for a $2 Arduino. AVR, RP2040 / RP2350, PIC and RISC-V.
Native firmware
As lean as C. A fraction of Arduino.
The same blink — toggle the LED, wait 500 ms — built three ways, total flash with the vector table and startup included. PyMCU emits the same native instructions a C compiler does, so it lands right next to hand-written C.
Blink firmware for an Arduino Uno — smaller is better.
- PyMCU native HAL / MicroPython 142 B
- C avr-gcc -Os 162 B
- Arduino IDE defaults ~1,024 B
Why 142 bytes? 104 of them are the interrupt-vector table that every AVR program carries — C included — so
on that front PyMCU and hand-written C are essentially the same. What's actually yours is the remaining 38 bytes: the startup stub and the blink loop. Arduino's extra bytes aren't waste either; they're the core's conveniences
(millis(), preconfigured timers, one sketch for every board). PyMCU just includes only what your
program uses, which is why the same blink ends up roughly 7× lighter.
Playground
Don't take our word for it. Compile it.
The whole toolchain runs inside your browser — nothing to install, no account, and your code never leaves the tab. Write Python, read the assembly it turns into, and watch the firmware blink on an emulated board.
from machine import Pin
import time
led = Pin(13, Pin.OUT)
while True:
led.toggle()
time.sleep_ms(500)
The 142-byte program from the chart above, start to finish.
-
The real compiler, in your tab
pymcuc itself, compiled to WebAssembly, with the AVR assembler and linker behind it. Same pipeline as your laptop, same bytes out.
-
See the assembly it emits
Every build shows the generated instructions and exactly how much flash the firmware takes on the chip.
-
Watch the firmware run
The result is executed on an emulated Arduino Uno or Raspberry Pi Pico — LED, pins and UART included.
The same blink, two worlds
One blink program. 142 bytes — or a whole interpreter.
The identical program — blink an LED every 500 ms — compiled by PyMCU versus run on MicroPython's fastest port. PyMCU's figures are total flash (vector table included), measured on the firmware the compiler emits.
MicroPython
~640 KB
interpreter firmware on the chip
before a single line of your code runs
PyMCU
142 B
the entire blink firmware
— interpreter included, because there isn't one
31% of 2 MB — before your code
73% of 264 KB — reserved for the GC
dynamic dispatch through the live interpreter
Even on its fastest port and with the @native/@viper emitters, the chip must still carry
the interpreter and a garbage-collected heap.
0.4% of 32 KB — your whole program
no heap, no GC — nothing reserved
a single sbi — 2 cycles, ~125 ns, every time
led.toggle() compiles to one AVR instruction. The whole program fits on a chip MicroPython cannot even
boot on.
142 B
total blink firmware
0 B
SRAM — no runtime overhead
1
instruction per pin toggle
No GC
deterministic timing, always
Go deeper than blink
See how led.toggle() becomes a single instruction, then watch a full DHT11 sensor driver compile to
~1,480 bytes with zero SRAM.
Features
What you will get with PyMCU
A free, deterministic compiler designed to bring the simplicity of Python to microcontrollers. A new way to write embedded software that is predictable, efficient and accessible.
Ahead-Of-Time Compilation
PyMCU is an AOT compiler that turns a statically-typed subset of Python into efficient bare-metal machine code.
Zero Runtime Overhead
Run firmware without a heap, interpreter, or garbage collection. Enjoy the full performance of your MCU.
Four Architectures
AVR, the RP2040 and RP2350, PIC and RISC-V — from a $2 ATtiny to a dual-core Pico 2, from the same source.
Deterministic Execution
Predictable timing for your embedded projects, ensuring your code runs exactly when it should.
Ready-to-use HALs
Hardware Abstraction Layers that make GPIO, timers, and peripherals easy to use from day one.
Open Source
PyMCU is community-driven and licensed under MIT, open for feedback and contributions.
Inside PyMCU
Python Meets Bare Metal!
AOT Compilation
PyMCU transforms a statically-typed subset of Python directly into bare-metal machine code, ensuring peak performance and reliability.
No Heap, No Interpreter
Unlike other Python implementations for MCUs, PyMCU has zero runtime overhead. No garbage collection and no dynamic memory.
Deterministic Timing
Built for applications where timing is critical. Your firmware runs predictably without OS interruptions.
One Language, Many Chips
The same Python compiles for an 8-bit ATmega and a 32-bit Cortex-M33. Pick the board; the compiler picks the backend.
Get your first firmware running with PyMCU.
Step 1: Install the compiler
Install it with a single command: pipx install --pip-args="--pre" "pymcu-compiler[avr]". The [avr] extra brings the whole AVR toolchain with it.
Step 2: Write your code
Write firmware in a statically-typed subset of Python. Familiar, clean, and compiled directly to bare-metal machine code. Check the documentation to get started.
Step 3: Flash & test
Flash the generated binary to your Arduino Uno and enjoy zero runtime overhead and deterministic timing.
Ready!
FAQs
Frequently Asked Questions
Dive into the following questions to gain insights into what PyMCU is, where it’s going, and how it can reshape embedded development with Python.
What is PyMCU?
PyMCU is an Ahead-Of-Time (AOT) compiler that translates a statically-typed subset of Python into bare-metal machine code. It is designed to be deterministic, lightweight, and efficient, bringing the simplicity of Python into the world of low-level development.
Is PyMCU production-ready?
Not yet — PyMCU is in public alpha, available on PyPI. It compiles for AVR, the RP2040 and RP2350, PIC and RISC-V, and the language and error messages are still moving between releases. Makers and developers are welcome to try it out and share feedback.
How is PyMCU being built?
PyMCU is built as an AOT compiler. It parses standard Python using AST, lowers it to a compact Intermediate Representation (IR), and then generates optimized machine code. This approach ensures zero runtime overhead—no interpreter and no heap required.
What license does PyMCU use?
PyMCU is released under the MIT License — simple, permissive, and open. This ensures anyone can use, adapt, or contribute to the project with full transparency.
Will it support official toolchains?
Yes, the long-term plan is to align with official toolchains provided by each vendor (such as those for PIC, AVR, ARM Cortex-M, and ESP32). This will let developers combine PyMCU with trusted ecosystems they already know.
Which architectures are supported?
The primary focus of current development is AVR (ATmega328P/Arduino Uno). Support for other architectures like PIC, ARM Cortex-M, and ESP32 is planned for future releases.
How does it differ from MicroPython or CircuitPython?
PyMCU is not an interpreter. It doesn’t run a VM or rely on dynamic memory. Instead, it compiles directly to machine code, with deterministic timing and no garbage collection. It is designed for developers who need Python’s clarity but require bare-metal reliability.
How can I get involved?
You can help shape PyMCU by following the project on GitHub, sharing feedback, and contributing code, documentation, or testing. Every contribution helps strengthen and grow the ecosystem.