mirror of
https://github.com/meshtastic/firmware.git
synced 2026-09-21 05:45:27 -04:00
On the bench the Pixel's GATT rx sat at zero from launch and across every node rebuild while ble-adv, lora and udp all flowed, and its GATT tx was "accepted by a peer". The peer was the iPad: logcat shows the Pixel's central connecting only to random-address Apple peers and never to a public-address NimBLE device. The V3's connectionless broadcast (instance 1) was on the air; its connectable mesh-peer advertisement (instance 2) was not, so the Pixel's filtered scan had nothing to find. The phone-API set (instance 0) had gone dark with it. Root cause is a conflation in ESP32BLEGattMesh: onSubscribe() called addLink(conn, true), flagging any link that wrote the mesh CCCD as viaMeshAdv. That flag does two jobs - it is the slot count startAdvertising() gates on (BLE_GATT_MESH_MAX_LINKS is 1) and it is the early return in NimbleBluetoothServerCallback::onDisconnect that skips the phone-API session reset and re-arm. Both sets advertise the same public address, so a central that finds instance 2 in a scan can land its CONNECT_IND on instance 0's ADV_IND (the earlier "onSubscribe never fired for a central that connected via the phone-API instance" commit saw exactly that). Such a link, once subscribed, counted against the mesh slot; when a second central (the iPad) took instance 2 the slot was full, and when the first link dropped on a monitor rebuild the early return left instance 0 unarmed too - "peer slots full, not advertising" at LOG_DEBUG was the only trace. Nothing recovered until reboot. - onSubscribe() now calls addLink(conn, false): a CCCD write sets `subscribed` (still the only thing that makes a link a notify target) and leaves `viaMeshAdv` exactly as the instance-2 CONNECT event set it, which is the one meaning it can safely carry. - onDisconnect() re-arms the mesh-peer advertisement for every dropped link and lets startAdvertising() decide, since a phone-API link dropping is what frees the connection slot a deferred start was waiting for. startAdvertising() skips a set that ble_gap_ext_adv_active() says is already running, so the re-arm never interrupts a live advertisement. - The slot-full line is LOG_INFO and names the conn handle holding the slot; an ENOMEM start (every connection slot taken) is LOG_WARN "deferred", and every ext-adv rc carries a hint: 6 = no free connection, 519 = controller activity budget (CONFIG_BT_CTRL_BLE_MAX_ACT). - Subscribe and disconnect lines say which advertisement the link arrived on. BLE_GATT_MESH_MAX_LINKS stays 1: two dual-role phones still race for the single mesh slot by design, and the header now says so and what to do about it (run the second phone PERIPHERAL_ONLY, or raise the slot with MAX_CONNECTIONS/MAX_ACT). The esp32-common.ini note records that the duplicated CONFIG_BT_NIMBLE_MAX_ CONNECTIONS / MAX_ACT keys resolve to [ble_mesh_esp32]'s values - verified in the generated sdkconfig.defaults - and that comment lines are stripped before the custom_sdkconfig hash, so the note itself does not trigger a libs rebuild. Built for heltec-v3 (pio run, 90 s, no IDF-lib recompile). Not yet flashed or verified on the bench. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>