# espnow-sender (ESP-NOW bridge)

ESP32 firmware that relays **binary** ESP-NOW packets to/from led-controller over WebSocket.

Layout matches **led-driver** so you deploy with **led-tool** from this directory:

```
espnow-sender/
  src/          # uploaded to device root via --src
    main.py
    wifi_ap.py
    util.py
    espnow_wire.py
  lib/          # uploaded to /lib via --lib
    aioespnow.py
    microdot/
```

## Deploy with led-tool

```bash
cd espnow-sender
python ../led-tool/cli.py -p /dev/ttyUSB0 --src --lib -r -f
```

| Flag | Effect |
|------|--------|
| `--src` | Upload `./src` → device `:/` (`main.py`, `util.py`, `espnow_wire.py`) |
| `--lib` | Upload `./lib` → device `/lib` (aioespnow, Microdot) |
| `-r` | Reset after upload |
| `-f` | Follow serial output |

From **led-controller** root:

```bash
python led-tool/cli.py -p /dev/ttyUSB0 --src --lib -r -f
```

(run with `cwd` = `espnow-sender`, or `cd espnow-sender` first)

Optional: `--force-upload` to ignore `file_hashes.json` on the device.

## Runtime

- **Wi-Fi access point** (default IP **192.168.4.1**): connect the Pi to the bridge SSID (`name` in `/settings.json`, e.g. `bridge-aabbccddeeff`)
- WebSocket server: `/ws` on port **80** — set Pi `bridge_ws_url` to `ws://192.168.4.1/ws` (or the printed IP)
- Optional `ap_password` in `/settings.json` (empty = open network)
- Default Wi-Fi channel: **6** (Pi sends `BRIDGE_CH` on connect; updates AP + ESP-NOW STA)
- Max **20** ESP-NOW peers (LRU eviction)

## Protocol

- [docs/espnow-architecture.md](../docs/espnow-architecture.md)
- [docs/espnow-binary-protocol.md](../docs/espnow-binary-protocol.md)
