# Lappy v0.2.1 — paced BLE recovery and first-stream diagnostics

V2 Rev1.1 only. This patch fixes the unbounded BLE recovery loop confirmed in the 22 October road logs. It also aligns the initial status read with the working Mac capture sequence and allows more time for the first telemetry packet. The UI design, Street filter, Drag/Track algorithms and timing quality thresholds are unchanged. Packaged for testing; not flashed and not yet validated with a real Dragy.

## Changes

- Replace blocking scan/result polling with explicit asynchronous start/completion/cancellation handling. Every worker iteration waits 100 ms. Failed/empty/early/error scan results use 1/2/5/10-second backoff, capped at ten seconds. Scan results are accessed or cleared only after scanning stops.
- Reset telemetry at actual connection/release/reinitialization/overflow boundaries, with a recorded reason; never on every scan poll. Publish status only when it changes. The previous reset storm and misleading continuous searching are removed from the tested failure paths.
- After a disconnect completes, try the saved address and address type directly once; fall back to scanning if that attempt fails. Do not rescan while the previous link is still active. Delayed disconnects remain paced and report their state.
- Retain ordered FD00 discovery, the FD03 response, acknowledged FD02 subscription and explicit CCCD readback. Read optional FD04 status immediately after verified setup using the nonblocking host API. A three-second warning does not block the worker or cancel an ATT request still owned by NimBLE. Late status callbacks are ignored after the connection context changes.
- Separate the ten-second first-stream warning from recovery. Allow 180 seconds after verified setup for the first decoded GPS sample, with one in-place setup repair after 30 seconds if optional ATT work has finished. Repair does not extend that deadline. Once decoded no-fix samples arrive, there is no satellite-acquisition timeout. A previously established stream that goes silent/undecodable for ten seconds recovers separately.
- Log scan outcomes/duration, retry reason, direct versus scan connection, MTU, characteristic handles/properties, challenge/response, CCCD, FD04 outcome and first packet/decoded-sample events. Raw GPS sample values and captures are not filtered or rewritten.

## Verification

- ESP-IDF 5.4.2 V2 build passes.
- 34 scenarios run the actual production BLE worker against a deterministic simulated SDK/radio/clock, under ASan/UBSan. Five 30-minute simulated soaks cover immediate scan failures, empty results, early completion, fresh no-fix messages, and an optional read that never completes while GPS remains live. Other cases cover delayed first packets, startup expiry, undecodable data, 20-second disconnect delays, scan deadlines, stuck cancellation, direct-connect fallback, failed repair, transient/established stream loss, late status callbacks and control requests during scan/setup. These are simulated durations, not a physical radio soak.
- Negative control: the historical v0.2.0 production worker reproduces 1,001 resets with zero simulated elapsed time under an immediate empty scan return. v0.2.1 makes 183 paced scan attempts in 30 simulated minutes with zero telemetry resets under scan-start failure.
- Full parser, Street, Drag/Track, China pack, telemetry-ingress, bootloader/backlight and ten storage/shutdown fault scenarios pass. Raw fragmented no-fix data still reaches captures and both timing adapters unchanged; a stream boundary invalidates in-flight attempts without creating a PB.
- Native simulator interactions pass. Of 148 rendered screens, 147 are pixel-identical to v0.2.0; only the Settings version caption changes. All 98 protected source/assets files compare byte-identically against the immutable v0.2.0 package, including UI/fonts/icons, timing cores/adapters, Street filter, startup/backlight and track pack.

See [implementation and test scope](IMPLEMENTATION_v0.2.1.md), [Mac versus Lappy comparison](BLE_CAPTURE_COMPARISON_v0.2.1.md), [hardware acceptance checklist](HARDWARE_BENCH_v0.2.1.md) and [UI review](../design/DESIGN_REVIEW_v0.2.1.md).

## Install and remaining uncertainty

Use the complete V2 package with tools/flash.py. Partition/NVS/PB/course formats are unchanged from v0.2.0; erase-all is unnecessary. Keep the packaged ELF for crash decoding. Existing releases and their archives are preserved.

The road-log reset storm is established. The reason the receiver initially delivered no notifications to Lappy is not established. An immediate FD04 read is a confirmed difference from the successful Mac captures and a compatibility step to test, not proof of a required wake command. A live receiver test is still necessary before calling the original zero-packet condition solved. Mandatory connect/setup ATT operations still run synchronously on the dedicated BLE worker using the stack's own timeout; this patch does not invent a safe cancellation API. A host that never completes scan cancellation/disconnection is not forcibly deleted/reset; the worker waits without a busy loop and leaves controls on their own tasks. Initial PWR flash, UI design, timing quality policy and approximate China timing gates remain unchanged.
