Skip to content

vihaanvp/MegaWrapper

Repository files navigation

MegaWrapper

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()

Installation

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 extras

From 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)

Quick Start

1. Upload StandardFirmata to your Arduino

Arduino IDE → File → Examples → Firmata → StandardFirmata → Upload.

2. Find the serial port

OS Typical port
Linux /dev/ttyUSB0, /dev/ttyACM0
macOS /dev/tty.usbserial-*
Windows COM3, COM4

3. Run a motor

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()

API Reference

Board — Arduino connection

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)

Motor — DC motor via H-bridge

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.


Servo — standard servo on PWM pin

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 None

Raises:

  • RuntimeError if no Board exists or servo not attached
  • ValueError if angle is not a number or step ≤ 0

VL53L0X — Time-of-Flight Distance Sensor

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 0x300x3F.

Requires: pip install 'megawrapper[sensor]' and Blinka-compatible hardware.


Utilities

from megawrapper import delay, millis

delay(1000)       # pause 1 second
now = millis()    # epoch milliseconds

Exceptions

MegaWrapperError           # base (catch-all)
├── InvalidSpeedError      # speed not a number or outside 0–100
├── BoardConnectionError   # cannot reach Arduino
└── StandbyNotConfiguredError  # wake()/sleep() without STBY pin

Examples

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.py

Motor/servo examples need an Arduino; sensor examples need a Raspberry Pi (or Blinka board) with VL53L0X.


Testing

python -m pytest tests/ -v

91 tests (90 passing, 1 pre-existing env-related skip), all mock the Arduino — no hardware required.


Project Wiki

Full documentation lives on the GitHub Wiki:


License

MIT © Vihaan Parlikar

About

Arduino-style DC motor and servo control via Firmata — a unified Python library

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages