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

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:

    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

Description
BLE LED Panel reverse engineering + turn signal controller
Readme 169 KiB
Languages
Python 100%