Skip to content

feat(mesh): add BLE advertisement bearer scaffold#10292

Draft
Komzpa wants to merge 2 commits into
meshtastic:masterfrom
Komzpa:codex/ble-adv-mesh
Draft

feat(mesh): add BLE advertisement bearer scaffold#10292
Komzpa wants to merge 2 commits into
meshtastic:masterfrom
Komzpa:codex/ble-adv-mesh

Conversation

@Komzpa
Copy link
Copy Markdown
Contributor

@Komzpa Komzpa commented Apr 25, 2026

Summary

Adds an experimental firmware-side BLE advertisement mesh bearer:

  • defines a BLE_ADVERTISEMENT_BROADCAST network protocol flag and TRANSPORT_BLE_ADVERTISEMENT packet transport marker in generated protobuf headers
  • adds a BleAdvertisementMeshCodec that fragments encrypted MeshPacket protobufs into legacy BLE advertisement manufacturer-data frames and reassembles them with CRC-16 validation
  • sizes frames for the real legacy ADV budget: 31 bytes total advertisement data, 27 bytes of Meshtastic frame payload after the 16-bit manufacturer id overhead
  • sizes the send queue from the maximum encoded MeshPacket size, so a full encrypted packet can be sent across all repeated fragment rounds without dropping frames
  • adds an OSThread-backed frame queue so router send paths enqueue ADV fragments instead of blocking on each fragment burst
  • sends fragmented packets in repeated rotating rounds, so receivers with narrow scan windows are not synchronized forever to the same fragment index
  • deduplicates recently reassembled BLE ADV packets so repeated advertising events from the same burst are not fed back into the router repeatedly
  • adds an ESP32/NimBLE2 platform implementation that uses a secondary legacy advertising instance for BLE mesh frames while leaving the existing connectable phone GATT advertising instance untouched
  • starts passive NimBLE scan callbacks when the BLE ADV network flag is enabled, and feeds matching manufacturer data frames back into the mesh router
  • makes the experimental manufacturer-data company id compile-time overridable via BLE_MESH_ADVERTISEMENT_COMPANY_ID
  • keeps nRF52/Bluefruit builds compile-safe and disabled by default until a safe concurrent advertise/scan backend is proven; the shared codec still builds there without pulling ESP/NimBLE headers into Bluefruit
  • enables the required NimBLE observer role and second extended-advertising instance on the current ESP32-C6 NimBLE2 target
  • adds native unit coverage for fragmented round-trip, maximum encrypted packet round-trip, frame count consistency, CRC rejection, and interleaved packet reassembly

Motivation

The goal is to make BLE advertisements behave like a LoRa-like broadcast bearer for encrypted Meshtastic packets, rather than like the existing phone GATT API. This keeps the current phone FromRadio/ToRadio BLE service untouched while giving supported BLE stacks a small transport contract to plug into.

Notes

This is still a draft and experimental. It does not enable BLE advertisement relaying through default saved config; the new bearer only starts when BLE_ADVERTISEMENT_BROADCAST is present in config.network.enabled_protocols.

The matching canonical schema enum/source PR is meshtastic/protobufs#917; keep this firmware PR draft until that schema lands or generated bindings are refreshed from the accepted schema.

The current real platform path is ESP32 devices using NimBLE2 extended-advertising support, currently represented by the ESP32-C6 target in this tree. Older ESP32/NimBLE1 builds and nRF52 builds do not enable the bearer by default, so they should keep their current phone BLE behavior.

The manufacturer id defaults to an experimental placeholder (0x05e1) and can be overridden by builds. Review should decide whether Meshtastic wants an assigned Bluetooth SIG company id, service-data encoding, or a project-specific compile-time id.

Validation

  • git diff --check - passed
  • docker run --rm -v "$PWD:/src:ro" -v /home/kom/proj/lora/.platformio-cache-pr10292:/home/meshtastic/.platformio meshtastic-native-test bash -lc 'rm -rf /tmp/fw-test && cp -a /src /tmp/fw-test && cd /tmp/fw-test && sed -i '''s/-DBUILD_EPOCH=$UNIX_TIME/#-DBUILD_EPOCH=$UNIX_TIME/''' platformio.ini && platformio test -e coverage -v -f test_ble_advertisement_mesh' - passed, 4/4 tests
  • docker run --rm -v "$PWD:/src:ro" -v /home/kom/proj/lora/.platformio-cache-pr10292:/home/meshtastic/.platformio meshtastic-native-test bash -lc 'rm -rf /tmp/fw-build && cp -a /src /tmp/fw-build && cd /tmp/fw-build && platformio run -e m5stack-unitc6l && platformio run -e rak4631' - passed; m5stack-unitc6l and rak4631 both reported SUCCESS
  • checked the ESP32/NimBLE2 calls against NimBLE-Arduino 2.3.7 headers for NimBLEExtAdvertising, NimBLEExtAdvertisement, NimBLEScan, and NimBLEAdvertisedDevice
  • Linux BlueZ smoke on two nearby adapters:
    • nucat adapter: 94:B8:6D:25:56:F1, smallcat adapter: 44:03:2C:ED:51:F6
    • BlueZ accepts 27-byte manufacturer-data payloads, matching the codec frame size
    • smallcat -> nucat and nucat -> smallcat both received single BLE ADV frames
    • a Python harness using the same MT fragment header, CRC, rotating fragment order, and repeated rounds successfully sent text in both directions over BLE advertisements: nucat -> smallcat (ping from nucat via ble-adv-mesh) and smallcat -> nucat (ack-smallcat)
    • Linux BlueZ D-Bus scanner required discovery-window hopping/cache clearing to observe changing manufacturer data from the same advertiser; firmware NimBLE scan callbacks should receive raw advertising reports without that BlueZ property-cache workaround
    • granting CAP_NET_RAW only to a small HCI monitor-channel helper allowed non-root raw advertisement receive without restarting bluetoothd, claiming the adapter, or touching normal BlueZ GATT/advertising state; the monitor path saw live repeated LE Advertising Reports directly and assembled text without the BlueZ D-Bus cache workaround: smallcat -> nucat (monitor path from smallcat, helper path from smallcat) and nucat -> smallcat (monitor slow nucat)

Not Yet Validated

  • Real ESP32-C6 or nRF52 hardware flashing/radio coexistence inside firmware. The Linux two-adapter harness validates the advertisement-frame transport and the firmware builds validate the target APIs, but actual embedded runtime airtime/coexistence still needs device testing.
  • nRF52 transmit/receive backend. The PR intentionally keeps nRF52 disabled by default until a Bluefruit implementation can scan and advertise without disturbing the existing phone BLE GATT path.

@github-actions github-actions Bot added the enhancement New feature or request label Apr 25, 2026
@Komzpa Komzpa force-pushed the codex/ble-adv-mesh branch 5 times, most recently from 9332707 to 40f3a56 Compare April 25, 2026 04:00
@Komzpa Komzpa force-pushed the codex/ble-adv-mesh branch from 40f3a56 to 94b8b2d Compare May 3, 2026 14:21
@jp-bennett jp-bennett force-pushed the master branch 2 times, most recently from 68e8b9c to 41f5317 Compare May 4, 2026 23:08
@Komzpa Komzpa force-pushed the codex/ble-adv-mesh branch from 94b8b2d to 8530eaf Compare May 24, 2026 03:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant