Metadata-Version: 2.4
Name: megawrapper
Version: 0.2.2
Summary: Arduino-style motor and servo control using Firmata — a combined MegaWrapper library
Author: Vihaan Parlikar
License: MIT
Keywords: arduino,motor,servo,control,firmata,megawrapper
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pyfirmata2
Provides-Extra: pyserial
Requires-Dist: pyserial; extra == "pyserial"
Provides-Extra: sensor
Requires-Dist: adafruit-circuitpython-vl53l0x; extra == "sensor"
Dynamic: license-file

# MegaWrapper

Control DC motors and servos on an Arduino over Firmata — with optional VL53L0X time-of-flight distance sensors.

```python
from megawrapper import Board

board = Board("/dev/ttyUSB0")
motor = board.attach_motor(2, 4, 11)
motor.forward(100)
board.close()
```

---

## Installation

**From PyPI (recommended):**

```bash
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):**

```bash
git clone https://github.com/vihaanvp/MegaWrapper.git
cd MegaWrapper/repo
pip install -e .
```

**Requirements:** Python 3.8+, Arduino running [StandardFirmata](https://github.com/firmata/arduino).

**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

```python
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

```python
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`):

```python
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).

```python
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

```python
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).

```python
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.

---

### Utilities

```python
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/`](https://github.com/vihaanvp/MegaWrapper/tree/main/repo/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 |

```bash
python examples/single_motor.py
```

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

---

## Testing

```bash
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](https://github.com/vihaanvp/MegaWrapper/wiki):

- [Board API](https://github.com/vihaanvp/MegaWrapper/wiki/Board-API)
- [Motor API](https://github.com/vihaanvp/MegaWrapper/wiki/Motor-API)
- [Servo API](https://github.com/vihaanvp/MegaWrapper/wiki/Servo-API)
- [Sensor API](https://github.com/vihaanvp/MegaWrapper/wiki/Sensor-API)
- [Utilities](https://github.com/vihaanvp/MegaWrapper/wiki/Utilities)
- [Exceptions](https://github.com/vihaanvp/MegaWrapper/wiki/Exceptions)
- [Examples](https://github.com/vihaanvp/MegaWrapper/wiki/Examples)
- [Edge Cases & Notes](https://github.com/vihaanvp/MegaWrapper/wiki/Edge-Cases)

---

## License

MIT © [Vihaan Parlikar](https://github.com/vihaanvp)
