- 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
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:
- Slot 1: Left turn arrow
- Slot 2: Right turn arrow
- 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:
wled1at IP4.3.2.1(hardcoded)wled2at mDNS nametangerine-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):
-
~/ble-capture-only.py— BLE GATT server that impersonates the panel, logs all writes from the app. Run withsudo python3 ~/ble-capture-only.py. Requires real panel to be OFF/disconnected. -
~/led-panel-ble.py— BLE client for testing commands against the real panel: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 -
Alternative: Android HCI snoop log (Developer Options → Enable Bluetooth HCI snoop log)
Capture process:
- Turn off real panel
sudo python3 ~/ble-capture-only.pyon Orin- Open Amusing LED app, connect to fake
YS623B101069L - Perform actions: switch between 3 saved animations, upload a new image
- 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:
- Update
Led.cppwith correct command format - Set MAC to
E0:6E:41:94:39:70 - Wire buttons: 1=left arrow, 2=right arrow, 3=brake/pace
- 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:
bleak3.0.1,bless0.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