Files
firmware/src
James RichandClaude Fable 5.1 668645fdee Make the mesh-peer egress actually deliver: resolve the handle and register peers server-side
The transport connected and subscribed but never delivered a frame to the phone.
Four things were wrong, found on the bench and fixed here:

- Peers were only registered from onSubscribe/onWrite, which never fired for a
  central that connected through the phone-API advertising instance. Register
  every inbound link from the server's own onConnect callback (ESP32BLEGattMesh::
  onConnect, hooked in NimbleBluetoothServerCallback::onConnect), which fires
  regardless of advertising instance, and also handle the raw
  BLE_GAP_EVENT_SUBSCRIBE in our GAP callback.
- The characteristic's value handle never resolved (getHandle() == 0xFFFF): the
  wrapper only resolves handles inside BLEServer::start(), which it triggers from
  BLEAdvertising::start(). This firmware advertises through the raw
  ble_gap_ext_adv API and never calls the wrapper's advertising, so force
  server->start() after registering the service.
- Notifications went through the raw NimBLE ble_gatts_notify_custom() with an
  esp_gatts handle, which returns success but delivers nothing. Send through the
  wrapper's own BLECharacteristic::notify() - the exact path the phone-API's
  fromNum uses.

Proven on hardware: an Android client (node-kmp monitor) subscribed to the
mesh-peer service receives frames the v3 originates and relays - rx counter
advanced with a frame from the v3's own node number over BLE GATT.

Still spike-quality: verbose diagnostic logs remain (revert), notify() broadcasts
to all subscribers rather than excluding the arrival peer (dedup covers it for
now), and the C3/nRF52 bring-up is unbuilt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 07:38:13 -05:00
..
…
…
…
…
…
…
…
…
…
…
…
…