Skip to content

BLE mesh packet logging#2039

Open
sybrenstuvel wants to merge 15 commits intomeshcore-dev:devfrom
sybrenstuvel:sybren/ble-packet-logging
Open

BLE mesh packet logging#2039
sybrenstuvel wants to merge 15 commits intomeshcore-dev:devfrom
sybrenstuvel:sybren/ble-packet-logging

Conversation

@sybrenstuvel
Copy link

@sybrenstuvel sybrenstuvel commented Mar 15, 2026

Disclaimer: this PR is indended as a starting point for the feature. The code itself is more a proof of concept, and I'm happy to go over it & clean things up. Let's first look at whether it's a feature that has the project's interest.

This PR adds support for mesh packet logging via a Bluetooth connection.

Adds a wrapper around the Serial.print() calls for MESH_PACKET_LOGGING. The wrapper can either send to serial, as MeshCore does now, or to a BLE client.

The BLE connection doesn't require any PIN, making it easy to connect with meshcore-to-mqtt. I have a matching PR for that too: Cisien/meshcoretomqtt#35

BLE MTU is set to max 256 bytes; the size actually used is also determined by the remote side. The payload will be split into multiple BLE messages when they are longer than the MTU.

For debugging independently of meshcore-to-mqtt, use this script. It uses the bleak package to do BLE.

I've tested this with a Heltec Wireless Tracker, and my meshcoretomqtt branch on a macbook.

#!/usr/bin/env python

import asyncio
from bleak import BleakScanner, BleakClient

NUS_TX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
NAME = "repeater-roomserver-name"  # Name of your repeater / room server.


def on_notify(_, data: bytearray):
    print(data.decode("utf-8", errors="replace"), end="")


async def main():
    print(f"Scanning for '{NAME}'...")
    device = await BleakScanner.find_device_by_name(NAME, timeout=10)
    if not device:
        print("Not found")
        return

    async with BleakClient(device) as client:
        print(f"Connected: {client.address}")
        await client.start_notify(NUS_TX, on_notify)
        await asyncio.sleep(3600)  # run for an hour, Ctrl-C to stop


try:
    asyncio.run(main())
except KeyboardInterrupt:
    pass

Disclaimer: this PR was made with the help of Claude AI. I'm a human, though, and happy to personally work on any feedback.

Made with Claude AI.
Three fixes applied:

BLELogInterface.h:108 — setMinPreferred(0x12) → setMaxPreferred(0x12) (bug: was clobbering the min value instead of setting max)
BLELogInterface.h:42 — removed the redundant _line_len = 0 in flushLine(), collapsed the guard to a one-liner
heltec_tracker/platformio.ini and sensecap_solar/platformio.ini — re-commented MESH_PACKET_LOGGING and BLE_PACKET_LOGGING
@sybrenstuvel sybrenstuvel marked this pull request as draft March 15, 2026 19:57
Use `lib_ldf_mode=chain+` so the LDF evaluates preprocessor conditions
when scanning for library dependencies. Without this, the LDF (in
default 'deep' mode) ignores it to find #include <BLEDevice.h> inside
the #ifdef ESP32 block in esp32/BLELogInterface.h, pulling in the
Arduino-Pico BLE library. That library only compiles correctly when
PIO_FRAMEWORK_ARDUINO_ENABLE_BLUETOOTH is set (which enables the
required BTstack defines), so without that flag the build fails. chain+
prevents this by honouring the #ifdef ESP32 guard.
@sybrenstuvel sybrenstuvel marked this pull request as ready for review March 15, 2026 20:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant