Control DC motors and servos on an Arduino over Firmata — with optional VL53L0X time-of-flight distance sensors.
from megawrapper import Board
board = Board("/dev/ttyUSB0")
motor = board.attach_motor(2, 4, 11)
motor.forward(100)
board.close()From PyPI (recommended):
pip install megawrapper # core (pyfirmata2)
pip install 'megawrapper[pyserial]' # + built-in Firmata backend
pip install 'megawrapper[sensor]' # + VL53L0X ToF sensor
pip install 'megawrapper[pyserial,sensor]' # all extrasFrom source (editable):
git clone https://github.com/vihaanvp/MegaWrapper.git
cd MegaWrapper/repo
pip install -e .Requirements: Python 3.8+, Arduino running StandardFirmata.
Dependencies:
| Dep | Type | For |
|---|---|---|
pyfirmata2 |
hard | Default Firmata backend |
pyserial |
optional ([pyserial]) |
Alternative built-in Firmata client |
adafruit-circuitpython-vl53l0x |
optional ([sensor]) |
VL53L0X ToF sensor (Raspberry Pi / Blinka) |
Arduino IDE → File → Examples → Firmata → StandardFirmata → Upload.
| OS | Typical port |
|---|---|
| Linux | /dev/ttyUSB0, /dev/ttyACM0 |
| macOS | /dev/tty.usbserial-* |
| Windows | COM3, COM4 |
import time
from megawrapper import Board
board = Board("/dev/ttyUSB0")
motor = board.attach_motor(2, 4, 11) # d1, d2, pwm pins
motor.forward(75)
time.sleep(2)
motor.stop()
board.close()from megawrapper import Board
# Connect
board = Board(port="/dev/ttyUSB0", stby=None)
# Attach motors
motor = board.attach_motor(d1=2, d2=4, pwm=11, name=None) # → Motor
# Control
board.stop_all() # coast all motors
board.wake() # STBY → HIGH (requires stby pin)
board.sleep() # STBY → LOW (requires stby pin)
# Lifecycle
board.close() # clean teardown
with Board(...) as b: # context manager — auto-close
# Singleton access
Board.get_active_board() # → Board | raises RuntimeError
Board.has_active_board() # → bool__repr__: Board(port='COM3', motors=2) (pyserial mode) or Board(motors=2) (pyfirmata2).
Backend mode (optional kwarg on Board):
board = Board("/dev/ttyUSB0", backend="pyserial") # built-in Firmata via pyserial
board = Board("/dev/ttyUSB0") # pyfirmata2 (default)Drives a motor through 2 direction pins + 1 PWM pin (L298N, TB6612FNG, L293D, MX1508).
motor.forward(speed=100) # 0–100, default full
motor.backward(speed=100) # 0–100, default full
motor.stop() # coast (both direction pins LOW)
motor.brake() # short terminals (both HIGH)
motor.set_speed(50) # change speed, keep direction
motor.speed # current speed (0–100), settable
motor.direction # "forward" | "backward" | "stopped" | "braked"
motor.name # optional label from attach_motor()Raises InvalidSpeedError if speed is not a number or outside 0–100.
from megawrapper import Servo
servo = Servo()
servo.attach(pin=9) # requires a Board to exist first
servo.write(90) # 0–180 (clamped)
servo.read() # last commanded angle
servo.move_smooth(180, 15) # step 1° at a time
servo.sweep(0, 180, 1, 15) # continuous sweep
servo.detach() # release pin
servo.pin # attached pin or None
servo.current_angle # last angle or NoneRaises:
RuntimeErrorif no Board exists or servo not attachedValueErrorif angle is not a number orstep ≤ 0
I²C-based, independent of Board/Firmata. Runs on the host (Raspberry Pi / Blinka).
from megawrapper.sensor import VL53L0X # or: from megawrapper import VL53L0X
# Single sensor
sensor = VL53L0X() # auto I²C, address 0x29
print(sensor.distance) # centimetres
# Multiple sensors on one bus
sensors = VL53L0X.auto_address([17, 27, 22]) # GPIO pins for XSHUT
for s in sensors:
print(s.distance)| Method / Property | Description |
|---|---|
VL53L0X(i2c=None, address=0x29) |
Constructor. Auto-creates I²C bus if omitted. |
.distance |
Measured distance in cm (raw mm ÷ 10). |
VL53L0X.auto_address(xshut_pins, i2c=None) |
Wake up to 16 sensors, assign addresses 0x30–0x3F. |
Requires: pip install 'megawrapper[sensor]' and Blinka-compatible hardware.
from megawrapper import delay, millis
delay(1000) # pause 1 second
now = millis() # epoch millisecondsMegaWrapperError # base (catch-all)
├── InvalidSpeedError # speed not a number or outside 0–100
├── BoardConnectionError # cannot reach Arduino
└── StandbyNotConfiguredError # wake()/sleep() without STBY pin
8 ready-to-run scripts in examples/:
| File | Shows |
|---|---|
single_motor.py |
Basic forward / stop |
dual_motor.py |
Two motors with names |
keyboard_control.py |
Interactive CLI motor control |
endless_sweep.py |
Servo sweep loop |
move_smooth.py |
Smooth servo movement |
pyserial_control.py |
Built-in pyserial Firmata backend |
vl53l0x_sensor.py |
Single VL53L0X distance readout |
vl53l0x_multi_sensor.py |
Multi-sensor auto-addressing |
python examples/single_motor.pyMotor/servo examples need an Arduino; sensor examples need a Raspberry Pi (or Blinka board) with VL53L0X.
python -m pytest tests/ -v91 tests (90 passing, 1 pre-existing env-related skip), all mock the Arduino — no hardware required.
Full documentation lives on the GitHub Wiki:
MIT © Vihaan Parlikar