Live Wiring commands from Python to your microcontroller
Project description
liveduino
A live Python REPL for your board: type a command, watch the hardware react instantly.
The Arduino API you already know, now live, from Python. Call pinMode,
digitalWrite, analogRead and watch the board react right away: no compile, no
upload, no flashing. Just Python talking to real hardware in real time.
It feels like a REPL for your circuit: type a line, the LED blinks; read a pin, the live value comes back. The Arduino edit-compile-upload loop is gone.
Connect any board, any way. USB, serial, Bluetooth, Wi-Fi or Ethernet: same code, same API, just point it at the wire you have. Prototype over USB on your desk, then drive the exact same board over Wi-Fi from across the room without changing a single line.
Not MicroPython. Not a sketch compiler. Not yet another pin-object API. If you know Arduino, you already know liveduino. There is nothing new to learn.
The spiritual successor to Frameduino, rebuilt from scratch for Python 3.13.
Table of Contents
- About
- Features
- Liveduino vs. the alternatives
- Quick start
- How it works
- Tech stack
- Arduino API
- Supported boards
- Connections
- Command-line interface
- Development
- Legacy
- Documentation
- License
About
Arduino's superpower is its API (pinMode, digitalWrite, analogRead): clean, famous,
and loved by millions. Its workflow, though, is stuck in batch: write a sketch, compile
it, upload it, wait, repeat. Every tiny change costs you a full round trip, and you never
get to talk to the hardware while it runs.
liveduino keeps the API and kills the loop. Your code runs on the host and drives the board live over a wire protocol (StandardFirmata), so every command hits the hardware the instant you call it. Same function names. Same semantics. Zero new concepts. Now interactive, scriptable, and powered by Python 3.13.
Prototype faster, debug interactively, automate test rigs, drive sensors and actuators from your data pipeline, all without leaving Python. This realtime, line-by-line control is the original Frameduino vision, rebuilt from scratch for Python 3.13 and a growing catalog of boards.
How it works in one line: liveduino runs your Python on the computer and speaks to firmware already flashed on your board. It does not run Python on the chip or compile sketches, and that is exactly why it is instant.
Features
| Zero learning curve | If you know Arduino, you are already done. Same names, same semantics, in Python |
| Instant feedback | Every digitalWrite / analogRead fires on the board now: no compile, no upload, no wait |
| Real device coverage | Digital I/O, analog input, PWM, servo, and I2C (sensors, displays, ...) — all over the bundled StandardFirmata, no extra firmware |
| No dependency bloat | Native StandardFirmata 2.x, written in-house. No third-party Firmata library to drag along |
| No Arduino IDE | Flashes StandardFirmata itself in pure Python over the bootloader; no IDE, no avrdude, no toolchain |
| Connect any way | One API over USB serial, Wi-Fi/Ethernet (TCP), or Bluetooth RFCOMM; just swap the driver |
| Batteries-included catalog | Auto-discovered profiles for UNO, Nano, Mini, Pro Mini, Fio, and more; add a board by dropping a file |
| Typed and safe | Literal types (PinMode, DigitalValue, BitOrder) with pins, modes, and values validated before they hit the wire |
| Rock-solid | 100% unit-test coverage with mocks, plus real-hardware integration tests |
Liveduino vs. the alternatives
Others make you learn a new API or a new language. liveduino bets on the one you already know.
| liveduino | pyFirmata / Telemetrix | MicroPython | |
|---|---|---|---|
| API style | Arduino/Wiring (pinMode, digitalWrite) |
Library-specific | Python on device |
| I2C / serial API | Arduino Wire / Serial1 |
Library-specific | Native Python |
| Code runs on | Host Python | Host Python | Microcontroller |
| Firmware | Stock StandardFirmata | Firmata / a custom sketch | MicroPython |
| Flashing | Built in, pure Python (no Arduino IDE) | Arduino IDE / avrdude | esptool / external tool |
| Transports | USB, Wi-Fi/Ethernet, Bluetooth (swap the driver) | Serial (mostly) | On-device |
| Learning curve for Arduino users | Zero | New API | New language |
What only liveduino does:
- The Arduino API all the way down. Not just
pinMode/digitalWrite:board.wireis Arduino'sWire,board.serial(1)isSerial1(samebegin/write/available/read/end), plusservoWrite,analogRead, timing. A sketch ports almost line for line. - Runs on the StandardFirmata that's already everywhere. No custom firmware to build or maintain (Telemetrix needs its own); if the board already has StandardFirmata, liveduino just talks to it — and flashes it for you in pure Python if it doesn't.
- The board self-describes.
capabilities()reads each pin's real modes from the firmware and uses them over the hardcoded catalog. - One API over any wire. Swap USB, Wi-Fi/Ethernet, or Bluetooth by changing a single line, never your code.
Quick start
Prerequisites
Python 3.13+ is all you need to use liveduino (uv is only
for developing it). Connect an Arduino UNO (or compatible) via USB and note its serial port
(liveduino-cli ports lists them). No Arduino IDE, no avrdude, no toolchain — liveduino
flashes the firmware for you next.
Install
pip install liveduino
# or
uv add liveduino
Requires Python 3.13+.
Flash the firmware
liveduino ships a prebuilt StandardFirmata image for each board and flashes it over the
serial bootloader itself, in pure Python (it speaks STK500v1 and auto-resets the board via
DTR/RTS). No Arduino IDE, no avrdude, no .hex file, no toolchain. One command and the
board is ready:
liveduino-cli flash arduino:uno --port /dev/ttyACM0 # flash bundled StandardFirmata
That is the whole setup. The bundled image works offline, and flashing targets the
ATmega328 family (UNO, Nano, Mini, Pro Mini, ...) today. Variants, custom .hex, and every
option: docs/CLI.md.
Already flashed StandardFirmata yourself (e.g. from the Arduino IDE)? Skip this step, liveduino talks to whatever StandardFirmata build is already on the board.
Blink from Python
From zero to a blinking LED in a handful of lines, and it runs the moment you hit enter.
from liveduino import ArduinoUno, OUTPUT, HIGH, LOW
board = ArduinoUno().connect("/dev/ttyACM0") # or COM3 on Windows
board.pinMode(13, OUTPUT)
while True:
board.digitalWrite(13, HIGH)
board.delay(1000)
board.digitalWrite(13, LOW)
board.delay(1000)
Read a pin
from liveduino import A0
val = board.analogRead(A0) # same as analogRead(0); returns 0-1023
board.close()
More on analog pins and the full method table: docs/API.md.
How it works
You call a plain Python method (board.pinMode(13, OUTPUT)); the board validates the pin,
the protocol (what is spoken: Firmata) encodes it, and the driver (where it connects:
serial, TCP, Bluetooth) ships the bytes. The two layers are decoupled, so a board works over
any channel by swapping the driver, never your code.
Deep dive: docs/ARCHITECTURE.md.
Tech stack
Python 3.13+, stdlib-only (pyserial for serial;
stdlib sockets for TCP/Bluetooth). Board subclasses expose camelCase Arduino methods over a
native FirmataProtocol (StandardFirmata 2.x — no third-party Firmata library). Quality gate:
pytest at 100% coverage, ruff, flake8, pylint, mypy, pyright, bandit.
Arduino API
Public board methods use camelCase to match Arduino/Wiring exactly: pinMode,
digitalWrite, digitalRead, analogRead, analogWrite, servoWrite, plus host-side
timing (delay, millis, ...). Device functions that StandardFirmata cannot perform
(tone, pulseIn, shiftOut/shiftIn) exist for fidelity but raise
UnsupportedOperationError.
What works over StandardFirmata today:
| Capability | Methods | Firmware |
|---|---|---|
| Digital I/O | pinMode, digitalWrite, digitalRead |
✅ built in |
| Analog input | analogRead |
✅ built in |
| PWM output | analogWrite |
✅ built in |
| Servo | servoWrite, servoConfig |
✅ built in (Servo lib) |
| I2C | board.wire (Arduino Wire) |
✅ built in (Wire lib) |
| Discovery | info, capabilities, pinState, status |
✅ built in (Firmata queries) |
| Streaming & serial | samplingInterval, readString, serial, continuous I2C |
✅ built in |
| Host-side timing | delay, delayMicroseconds, millis, micros |
✅ host only |
| Tone / pulse / shift | tone, noTone, pulseIn, shiftOut, shiftIn |
⚠️ raise UnsupportedOperationError |
Servo and I2C are the real Arduino names too. I2C is Arduino's Wire — alias it
and a sketch ports verbatim:
board.servoWrite(9, 90) # servo on pin 9
Wire = board.wire # in place of: #include <Wire.h>
Wire.begin()
Wire.requestFrom(0x68, 6, 0x3B) # read 6 bytes from register 0x3B (MPU-6050)
while Wire.available():
value = Wire.read()
Discovery — ask the board about itself instead of trusting the catalog:
board.info() # firmware + board identity
board.status() # live state of every pin
board.capabilities() # per-pin modes read from the firmware (cached), else the catalog
Full method table, servo/I2C/discovery/serial details, and helpers: docs/API.md.
Supported boards
Arduino UNO is fully supported (StandardFirmata over USB serial). Nano, Mini, Pro Mini, Fio, Duemilanove/Diecimila, Ethernet, BT, LilyPad, and UNO Mini are supported; Mega, Leonardo, Micro, and Pinguino are planned. Board profiles are auto-discovered, so adding one is just dropping a file.
from liveduino import connect
board = connect("arduino:uno", "/dev/ttyACM0")
Full board table and how to add a board: docs/BOARDS.md.
Connections
One API, every wire. The board talks over a pluggable driver, so the same code runs over USB, Wi-Fi/Ethernet, or Bluetooth — you swap one line, never your code:
board = ArduinoUno().connect("/dev/ttyACM0") # USB serial (default)
board = ArduinoUno().connect(driver=TcpDriver("192.168.1.50", 3030)) # Wi-Fi / Ethernet
board = ArduinoUno().connect(driver=BluetoothDriver("AA:BB:CC:DD:EE:FF")) # Bluetooth RFCOMM
Every driver and the protocol override: docs/CONNECTIONS.md.
Command-line interface
Installing liveduino adds the liveduino-cli console command, pure Python with no extra
toolchain:
liveduino-cli flash arduino:uno --port /dev/ttyACM0 # flash bundled StandardFirmata (STK500v1)
liveduino-cli boards # list catalog boards
liveduino-cli ports # list serial ports
Full reference: docs/CLI.md.
Development
Requires Python 3.13 and uv.
uv python pin 3.13
uv sync --all-groups
make install-dev # installs dev deps + pre-commit hooks
make check # lint + type-check + 100% coverage gate
Every make target, the coverage gate, and how to run hardware integration tests:
docs/DEVELOPMENT.md.
Legacy
Frameduino 0.x (Python 2, Pinguino-only) lives in the original Frameduino repository.
Documentation
| Document | Audience | Contents |
|---|---|---|
| This README | Everyone | Motivation, quick start, overview |
docs/API.md |
Users | Full Arduino method table and analog pin model |
docs/BOARDS.md |
Users | Supported boards and how to add one |
docs/CONNECTIONS.md |
Users | Drivers (serial, TCP, Bluetooth) and protocol override |
docs/CLI.md |
Users | liveduino-cli command: flash firmware, list boards and ports |
docs/DEVELOPMENT.md |
Contributors | Setup, make targets, and tests |
docs/ARCHITECTURE.md |
Contributors | Layers, data flow, drivers, analog pins, testing |
firmware/arduino/README.md |
Users | StandardFirmata setup and serial settings |
AGENTS.md |
AI agents | Coding standards and guardrails |
License
Distributed under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 liveduino-0.2.1.tar.gz.
File metadata
- Download URL: liveduino-0.2.1.tar.gz
- Upload date:
- Size: 291.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d7c87ba6974a66aa171efe74d117f8712f7754c99a7919790948b06d5dba332
|
|
| MD5 |
d5d1d1f5a416782772547ea2d1b9c203
|
|
| BLAKE2b-256 |
cb64d9fb1a000b68e318d9cba78c8cf6306dbe3e371bd170afb2291bbac23c58
|
File details
Details for the file liveduino-0.2.1-py3-none-any.whl.
File metadata
- Download URL: liveduino-0.2.1-py3-none-any.whl
- Upload date:
- Size: 220.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4f58d854237212890879b182b8f8d955cc46070b295e0adb31e2d71239507212
|
|
| MD5 |
8185818b84fc43f38e5456baf5ae19ac
|
|
| BLAKE2b-256 |
d9de4b7aecf955f1236bcb107a8e51ef6f4037cb8dfaee6e5ef3cf8d6c25a870
|