led-panel/README.md
Salty 71515ef517 Initial: panel docs, BLE tools, glove firmware notes
- LED panel BLE protocol (partial RE from glove firmware)
- Panel MAC: E0:6E:41:94:39:70, BLE name: YS623B101069L
- led-panel-ble.py: scan, slot switch, cycle tool
- ble-capture-only.py: GATT server capture proxy
- Glove firmware docs (ESP32 WROOM, 4-button controller)
- Slot switch command doesn't work — capture needed
2026-04-01 17:46:23 -04:00

169 lines
6.2 KiB
Markdown

# LED Panel — BLE Protocol Reverse Engineering & Turn Signal Controller
## Goal
Build a BLE-controlled turn signal system using a cheap "Amusing LED" backpack panel. The panel displays saved animations (arrows, brake lights, etc.) and is switched via BLE commands from an ESP32 glove controller worn while riding an EUC (electric unicycle).
## The Panel
- **Brand:** "Amusing LED" (generic Chinese LED backpack panel)
- **BLE Name:** `YS623B101069L`
- **BLE MAC:** `E0:6E:41:94:39:70` (address type: LE Random)
- **BLE Service UUID:** `49535343-fe7d-4ae5-8fa9-9fafd205e455` (Microchip Transparent UART)
- **Companion App:** "Amusing LED" (Android/iOS)
### BLE Characteristics
| UUID | Properties | Purpose |
|------|-----------|---------|
| `49535343-8841-43f4-a8d4-ecbe34729bb3` | write-without-response, write | **Main command channel** |
| `49535343-6daa-4d02-abf6-19569aca69fe` | write | Secondary write |
| `49535343-1e4d-4bd9-ba61-23c647249616` | notify | Panel → phone notifications |
| `49535343-aca3-481c-91ec-d85e28a60318` | write, notify | Bidirectional channel |
### Known Protocol (Partial — from glove firmware)
**Slot switch command** (20 bytes):
```
AA 55 FF FF 0E 00 01 00 C1 02 18 06 02 XX 00 01 FF 7F YY 05
```
Where:
- Byte 13 (`XX`): `0x3C + (slot_id - 61)`
- Byte 18 (`YY`): `0xAA + (slot_id - 61)`
- Base slot_id = 61
**⚠️ STATUS: This command connects successfully but does NOT switch animations on the current panel. The slot numbering or command format may have changed between firmware versions. Protocol capture needed.**
### Current Saved Animations
Uploaded via the Amusing LED app:
1. **Slot 1:** Left turn arrow
2. **Slot 2:** Right turn arrow
3. **Slot 3:** Pace/running animation
(Internal slot IDs unknown — need protocol capture to determine)
## The Glove Controller
An ESP32 WROOM-based controller with 4 buttons, originally built for Inmotion V13 EUC integration. Source code in the `glove/` directory on Orin at `/home/seb/glove/`.
### Architecture
```
┌─────────────────┐ BLE ┌──────────────┐
│ ESP32 Glove │─────────────▶│ LED Panel │
│ (4 buttons) │ │ (backpack) │
│ │ WiFi ┌──────────────┐
│ │─────────────▶│ WLED Strip 1│
│ │─────────────▶│ WLED Strip 2│
│ │ BLE ┌──────────────┐
│ │─────────────▶│ Inmotion V13│
└─────────────────┘ │ (EUC) │
└──────────────┘
```
### Button Mapping
| Button | Single Press | Double Tap |
|--------|-------------|------------|
| 1 (GPIO 32) | Toggle LEFT signal | V13 Horn |
| 2 (GPIO 33) | Toggle RIGHT signal | V13 Headlight |
| 3 (GPIO 25) | Toggle BRAKE | — |
| 4 (GPIO 26) | (unused) | — |
### States
- **NEUTRAL** — default, no signals
- **LEFT** — left turn signal active
- **RIGHT** — right turn signal active
- **BRAKE** — brake light active
Each button toggles its state; pressing again returns to NEUTRAL.
### WLED Integration
The glove sends commands to two WLED strips via WiFi HTTP API:
- `wled1` at IP `4.3.2.1` (hardcoded)
- `wled2` at mDNS name `tangerine-disk`
- Uses WLED presets named: `LEFT`, `RIGHT`, `BRAKE`, `NEUTRAL`
### V13 EUC Integration
Connects via BLE to Inmotion V13 (`V13-3002563C`):
- Horn command
- Headlight toggle
## What Needs To Be Done
### Phase 1: Protocol Capture (PRIORITY)
The slot-switch command from the glove firmware doesn't work on this panel. We need to capture what the Amusing LED app actually sends.
**Tools ready on Orin (`saltylab-orin` / `192.168.86.158`):**
1. **`~/ble-capture-only.py`** — BLE GATT server that impersonates the panel, logs all writes from the app. Run with `sudo python3 ~/ble-capture-only.py`. Requires real panel to be OFF/disconnected.
2. **`~/led-panel-ble.py`** — BLE client for testing commands against the real panel:
```bash
python3 ~/led-panel-ble.py scan # find devices
python3 ~/led-panel-ble.py slot E0:6E:41:94:39:70 61 # switch slot
python3 ~/led-panel-ble.py cycle E0:6E:41:94:39:70 1 10 3 # cycle slots
```
3. **Alternative:** Android HCI snoop log (Developer Options → Enable Bluetooth HCI snoop log)
**Capture process:**
1. Turn off real panel
2. `sudo python3 ~/ble-capture-only.py` on Orin
3. Open Amusing LED app, connect to fake `YS623B101069L`
4. Perform actions: switch between 3 saved animations, upload a new image
5. Ctrl+C, examine `~/ble-capture-log.jsonl`
### Phase 2: Protocol Decode
From captures, determine:
- **Slot switch command format** (which bytes, which slot IDs)
- **Image upload protocol** (pixel format, chunking, headers)
- **Any handshake/init** the app sends on connect
- **Brightness, speed, mode commands**
### Phase 3: Update Glove Firmware
Once protocol is known:
1. Update `Led.cpp` with correct command format
2. Set MAC to `E0:6E:41:94:39:70`
3. Wire buttons: 1=left arrow, 2=right arrow, 3=brake/pace
4. Build & flash ESP32
### Phase 4: Direct Pixel Control (Stretch)
If we decode the image upload protocol, we can push custom animations directly from the ESP32 or Orin without using the app at all.
## Environment
- **Orin:** `seb@192.168.86.158` (saltylab-orin, Jetson Orin Nano)
- **Glove source:** `/home/seb/glove/` on Orin (PlatformIO, ESP32 WROOM)
- **Python deps:** `bleak` 3.0.1, `bless` 0.3.0 (both installed for user + root)
- **BLE adapter:** hci0, `F8:3D:C6:56:E6:FC`
- **Panel is nearby** the Orin in the lab
## Files
```
scripts/
led-panel-ble.py — BLE client: scan, slot switch, cycle
ble-capture-only.py — GATT server capture proxy
captures/
(place .jsonl capture files here)
docs/
glove-firmware.md — notes on the existing glove codebase
```
## References
- Microchip Transparent UART BLE service: `49535343-*`
- WLED HTTP API: https://kno.wled.ge/interfaces/http-api/
- bleak (BLE client): https://github.com/hbldh/bleak
- bless (BLE server): https://github.com/kevincar/bless