fix: prevent beacon disconnects during provisioning #13

Merged
schwifty merged 2 commits from schwifty/fix-beacon-disconnect-root-cause into main 2026-03-22 02:57:36 +00:00
Collaborator

Root Cause

The DX-Smart CP28 beacons were disconnecting 4+ times during provisioning because the 0.3s inter-command write delay was too fast for the beacon's MCU.

What was happening

  1. iOS sends GATT write command to beacon
  2. 0.3s later, sends the next command
  3. Beacon MCU is still processing the previous command (especially frame selection and type changes which trigger internal state restructuring)
  4. Beacon misses a BLE connection event because its radio is busy
  5. After 6 missed events → BLE supervision timeout → disconnect
  6. iOS reconnects, resumes writing, beacon disconnects again → repeat 4x

The Fix: Adaptive Inter-Command Delays

Instead of a flat 0.3s delay between all commands, we now use adaptive delays based on command type:

Command Type Old Delay New Delay Why
Base (simple params) 0.3s 0.5s More breathing room
Frame select (0x11-0x16) 0.3s 1.0s MCU switches internal frame context
Frame type (0x61, 0x62) 0.3s 1.0s Triggers config restructuring
UUID write (21 bytes) 0.3s 0.8s Largest payload
Resume after reconnect 0.5s 1.5s Let BLE stack stabilize
Non-fatal skip 0.15s 0.5s Was way too fast

Total provisioning time goes from ~8s to ~16s, but it should actually complete without disconnecting.

Test Plan

  • Provision a beacon — should complete without any disconnects
  • Verify all 24 commands write successfully
  • Check debug log for no "Unexpected disconnect" messages

🤖 Generated with Claude Code

## Root Cause The DX-Smart CP28 beacons were disconnecting 4+ times during provisioning because the **0.3s inter-command write delay was too fast** for the beacon's MCU. ### What was happening 1. iOS sends GATT write command to beacon 2. 0.3s later, sends the next command 3. Beacon MCU is still processing the previous command (especially frame selection and type changes which trigger internal state restructuring) 4. Beacon misses a BLE connection event because its radio is busy 5. After 6 missed events → BLE supervision timeout → **disconnect** 6. iOS reconnects, resumes writing, beacon disconnects again → repeat 4x ### The Fix: Adaptive Inter-Command Delays Instead of a flat 0.3s delay between all commands, we now use adaptive delays based on command type: | Command Type | Old Delay | New Delay | Why | |---|---|---|---| | Base (simple params) | 0.3s | **0.5s** | More breathing room | | Frame select (0x11-0x16) | 0.3s | **1.0s** | MCU switches internal frame context | | Frame type (0x61, 0x62) | 0.3s | **1.0s** | Triggers config restructuring | | UUID write (21 bytes) | 0.3s | **0.8s** | Largest payload | | Resume after reconnect | 0.5s | **1.5s** | Let BLE stack stabilize | | Non-fatal skip | 0.15s | **0.5s** | Was way too fast | Total provisioning time goes from ~8s to ~16s, but it should actually complete without disconnecting. ## Test Plan - [ ] Provision a beacon — should complete without any disconnects - [ ] Verify all 24 commands write successfully - [ ] Check debug log for no "Unexpected disconnect" messages 🤖 Generated with [Claude Code](https://claude.com/claude-code)
schwifty added 2 commits 2026-03-22 02:53:58 +00:00
Previously, when the beacon disconnected mid-write, the reconnect handler
cleared the entire command queue and reset writeIndex to 0, causing all 24
commands to be re-sent from scratch on every reconnect. This could confuse
the beacon firmware with duplicate config writes and wasted reconnect cycles.

Changes:
- On disconnect during writing, PRESERVE command queue and write index
- After reconnect + re-auth, resume from the last command instead of rebuilding
- Increase MAX_DISCONNECT_RETRIES from 3 to 5 (resume is lightweight)
- Increase inter-command delay from 150ms to 300ms for firmware breathing room
- Increase global timeout from 45s to 90s to accommodate more retries
- Add resumeWriteAfterDisconnect flag to control post-auth flow

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: DX-Smart CP28 beacons were disconnecting during provisioning
because the 0.3s inter-command delay was too fast for the beacon's MCU.
Frame selection (0x11-0x16) and type commands (0x61, 0x62) trigger internal
state changes that need processing time. Rapid writes caused the beacon to
miss BLE connection events, triggering link-layer supervision timeouts.

Changes:
- Base delay: 0.3s → 0.5s for all commands
- Heavy delay: 1.0s after frame select/type commands (MCU state change)
- Large payload delay: 0.8s after UUID writes (21 bytes)
- Resume delay: 0.5s → 1.5s after reconnect (let BLE stack stabilize)
- Non-fatal skip delay: 0.15s → 0.5s

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
schwifty merged commit d44ca47f36 into main 2026-03-22 02:57:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: payfrit/payfrit-beacon-ios#13
No description provided.