ble
Bluetooth context for the Megahub project. Use when working on BLE GATT server, Classic Bluetooth HID host, Web Bluetooth API integration, btremote library, connection handling, or any Bluetooth-relat
SKILL.md.claude/skills/ble/SKILL.md
GitHubBluetooth Architecture — Megahub ESP32
The project uses both BLE and Classic Bluetooth simultaneously (BTDM dual-mode).
| Type | Purpose | Library |
|---|---|---|
| BLE (GATT server) | Web Bluetooth API ↔ firmware | lib/btremote/ |
| Classic BT (HID host) | Gamepad/keyboard/mouse input | lib/btremote/ |
BLE GATT Server
Service UUID: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
| Characteristic | UUID | Direction | Type |
|---|---|---|---|
| REQUEST | beb5483e-36e1-4688-b7f5-ea07361b26a8 |
Browser → Firmware | Write |
| RESPONSE | 1c95d5e3-d8f7-413a-bf3d-7a2e5d7be87e |
Firmware → Browser | Notify |
| EVENT | d8de624e-140f-4a22-8594-e2216b84a5f2 |
Firmware → Browser | Notify |
| CONTROL | f78ebbff-c8b7-4107-93de-889a6a06d408 |
Firmware → Browser | Indicate |
- Indications vs Notifications: CONTROL uses indications (require client ACK). RESPONSE/EVENT use notifications.
- MTU: Default 23 bytes on connect. Firmware sends
MTU_INFOnotification immediately after connect, then again after negotiation (up to 517 bytes). - From real device logs: MTU negotiates to 514/517 bytes reliably.
BLE Rules (never violate)
- Never block in BLE callbacks — they run in the BLE stack task context
- Always check
isConnected()before sending notifications/indications - Reconnection: Browser handles reconnect via
device.gatt.connect()— firmware must keep advertising - Multiple connections: Each consumes ~30KB heap — the BT stack alone drops free heap from ~140KB to ~75KB
- BLE callbacks → post work to a queue, handle in
BTMsgProcessortask - Indication failures (status=132) during startup are normal — the client ACK pipeline needs time to stabilize after connect
Classic Bluetooth (HID Host)
- Only for HID devices: gamepads, keyboards, mice
- Auto-pairing enabled (IO capability: NONE, PIN: "0000")
- HID events processed in
HIDEventTask(priority 5, separate from BLE stack) - Never block in HID callback — post to queue
- Pairing state is persisted in NVS; handle removal gracefully
Startup Sequence
From real device logs, BT init order:
- BT controller starts in BTDM dual-mode (
esp_bt_controller_enable) - Classic GAP callback registered
- Classic scan mode: connectable + discoverable
- HID Host initialized → waits for
ESP_HIDH_INIT_EVT - Bluedroid BLE init → waits for GATTS registration
- GATT service + characteristics created (handles 40–51)
- Advertising started
- Total heap consumed by BT stack: ~65KB
Message Protocol (BLE)
- 5-byte fragment header:
[type, messageId, fragmentNum(2), flags] - Fragmentation: automatic for all requests/responses
- Streaming file upload: sliding window of 8 chunks, waits for ACK before advancing
- App request types: from logs — type 10=ready-for-events, type 7=get-projects, type 8=get-autostart, type 9=set-autostart, type 2=get-file, type 6=execute-lua
Debugging BLE
# Enable HCI logs in ESP-IDF menuconfig for raw BLE packet capture
# Watch for these in serial log:
# [INFO] handleGattsConnect() — client connected
# [INFO] handleGattsMTU() — MTU negotiated
# [WARN] handleGattsConfirm() — indication failed (status=132 at startup is normal)