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.

PyMCU Logo

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.

Python on microcontrollers is going mainstream — Arduino itself now offers official MicroPython support. We think that direction is exactly right. PyMCU shares the goal of Python-friendly embedded and takes it all the way to the bare metal: the same familiar syntax, compiled to native machine code with no interpreter on the chip.

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.

blink.py Arduino Uno · MicroPython API
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.

Run this blink Open the playground

Opens with this program already loaded.

Philosophy: Determinism, No OS and High Performance

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

MicroPython fastest port · RP2040 @ 133 MHz
Interpreter firmware ~640 KB flash

31% of 2 MB — before your code

GC heap (default) ~192 KB SRAM

73% of 264 KB — reserved for the GC

Pin toggle interpreted call

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.

PyMCU ATmega328P · Arduino Uno
Blink firmware (total) 142 B flash

0.4% of 32 KB — your whole program

Runtime / GC heap 0 B SRAM

no heap, no GC — nothing reserved

Pin toggle 1 instruction

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!

Steps image

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.