This commit is contained in:
Sybren A. Stüvel 2026-04-20 10:05:07 +00:00 committed by GitHub
commit 7d55e13a05
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 295 additions and 37 deletions

View file

@ -459,14 +459,6 @@ const char *MyMesh::getLogDateTime() {
return tmp;
}
void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Serial.print(" RAW: ");
mesh::Utils::printHex(Serial, raw, len);
Serial.println();
#endif
}
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_BRIDGE

View file

@ -137,8 +137,6 @@ protected:
bool allowPacketForward(const mesh::Packet* packet) override;
const char* getLogDateTime() override;
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override;
void logRx(mesh::Packet* pkt, int len, float score) override;
void logTx(mesh::Packet* pkt, int len) override;
void logTxFail(mesh::Packet* pkt, int len) override;

View file

@ -2,6 +2,17 @@
#include <Mesh.h>
#include "MyMesh.h"
#if BLE_PACKET_LOGGING
#if defined(NRF52_PLATFORM) || defined(ESP32)
#include <helpers/BLELogInterface.h>
#else
#error "BLE_PACKET_LOGGING is not supported on this platform (only ESP32 and nRF52)"
#endif
#endif
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && (defined(NRF52_PLATFORM) || defined(ESP32))
static BLELogInterface ble_log;
#endif
#ifdef DISPLAY_CLASS
#include "UITask.h"
@ -95,6 +106,11 @@ void setup() {
the_mesh.begin(fs);
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && (defined(NRF52_PLATFORM) || defined(ESP32))
ble_log.begin(the_mesh.getNodeName());
the_mesh.setPacketLogStream(&ble_log);
#endif
#ifdef DISPLAY_CLASS
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
#endif
@ -153,6 +169,9 @@ void loop() {
ui_task.loop();
#endif
rtc_clock.tick();
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && defined(ESP32)
ble_log.loop();
#endif
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
#if defined(NRF52_PLATFORM)

View file

@ -199,14 +199,6 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
return 0; // unknown command
}
void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Serial.print(" RAW: ");
mesh::Utils::printHex(Serial, raw, len);
Serial.println();
#endif
}
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
if (_logging) {

View file

@ -131,7 +131,6 @@ protected:
return _prefs.airtime_factor;
}
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override;
void logRx(mesh::Packet* pkt, int len, float score) override;
void logTx(mesh::Packet* pkt, int len) override;
void logTxFail(mesh::Packet* pkt, int len) override;

View file

@ -2,6 +2,17 @@
#include <Mesh.h>
#include "MyMesh.h"
#if BLE_PACKET_LOGGING
#if defined(NRF52_PLATFORM) || defined(ESP32)
#include <helpers/BLELogInterface.h>
#else
#error "BLE_PACKET_LOGGING is not supported on this platform (only ESP32 and nRF52)"
#endif
#endif
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && (defined(NRF52_PLATFORM) || defined(ESP32))
static BLELogInterface ble_log;
#endif
#ifdef DISPLAY_CLASS
#include "UITask.h"
@ -72,6 +83,11 @@ void setup() {
the_mesh.begin(fs);
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && (defined(NRF52_PLATFORM) || defined(ESP32))
ble_log.begin(the_mesh.getNodeName());
the_mesh.setPacketLogStream(&ble_log);
#endif
#ifdef DISPLAY_CLASS
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
#endif
@ -113,4 +129,7 @@ void loop() {
ui_task.loop();
#endif
rtc_clock.tick();
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && defined(ESP32)
ble_log.loop();
#endif
}

View file

@ -217,21 +217,18 @@ void Dispatcher::checkRecv() {
}
if (pkt) {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Serial.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d time=%d",
pkt->getRawLength(), pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
_packet_log.printf("%s: RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d time=%d",
getLogDateTime(), pkt->getRawLength(), pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000), air_time);
static uint8_t packet_hash[MAX_HASH_SIZE];
pkt->calculatePacketHash(packet_hash);
Serial.print(" hash=");
mesh::Utils::printHex(Serial, packet_hash, MAX_HASH_SIZE);
_packet_log.print(" hash=");
mesh::Utils::printHex(_packet_log, packet_hash, MAX_HASH_SIZE);
if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ
|| pkt->getPayloadType() == PAYLOAD_TYPE_RESPONSE || pkt->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) {
Serial.printf(" [%02X -> %02X]\n", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
_packet_log.printf(" [%02X -> %02X]\n", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
} else {
Serial.printf("\n");
_packet_log.print("\n");
}
#endif
logRx(pkt, pkt->getRawLength(), score); // hook for custom logging
@ -338,14 +335,13 @@ void Dispatcher::checkSend() {
outbound_expiry = futureMillis(max_airtime);
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Serial.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)",
len, outbound->getPayloadType(), outbound->isRouteDirect() ? "D" : "F", outbound->payload_len);
_packet_log.printf("%s: TX, len=%d (type=%d, route=%s, payload_len=%d)",
getLogDateTime(), len, outbound->getPayloadType(), outbound->isRouteDirect() ? "D" : "F", outbound->payload_len);
if (outbound->getPayloadType() == PAYLOAD_TYPE_PATH || outbound->getPayloadType() == PAYLOAD_TYPE_REQ
|| outbound->getPayloadType() == PAYLOAD_TYPE_RESPONSE || outbound->getPayloadType() == PAYLOAD_TYPE_TXT_MSG) {
Serial.printf(" [%02X -> %02X]\n", (uint32_t)outbound->payload[1], (uint32_t)outbound->payload[0]);
_packet_log.printf(" [%02X -> %02X]\n", (uint32_t)outbound->payload[1], (uint32_t)outbound->payload[0]);
} else {
Serial.printf("\n");
_packet_log.print("\n");
}
#endif
}

View file

@ -1,6 +1,34 @@
#pragma once
#include <MeshCore.h>
#if MESH_PACKET_LOGGING && ARDUINO
#include <Arduino.h>
#include <stdarg.h>
class LogPrint : public Print {
Print* _impl;
public:
LogPrint() : _impl(&Serial) {}
void setStream(Print* s) { if (s) _impl = s; }
size_t write(uint8_t c) override { return _impl->write(c); }
size_t write(const uint8_t* buf, size_t n) override { return _impl->write(buf, n); }
void printf(const char* fmt, ...) {
char buf[192]; // sized for longest log line: ~31 char datetime + ~90 char fields
va_list args;
va_start(args, fmt);
int n = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
if (n >= (int)sizeof(buf)) {
// truncation occurred: mark it visibly rather than silently losing data
buf[sizeof(buf) - 4] = '.';
buf[sizeof(buf) - 3] = '.';
buf[sizeof(buf) - 2] = '\n';
buf[sizeof(buf) - 1] = '\0';
}
_impl->print(buf);
}
};
#endif
#include <Identity.h>
#include <Packet.h>
#include <Utils.h>
@ -129,6 +157,9 @@ class Dispatcher {
void processRecvPacket(Packet* pkt);
void updateTxBudget();
#if MESH_PACKET_LOGGING
LogPrint _packet_log;
#endif
protected:
PacketManager* _mgr;
@ -154,7 +185,13 @@ protected:
virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
virtual void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { } // custom hook
virtual void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { // custom hook
#if MESH_PACKET_LOGGING
_packet_log.printf("%s RAW: ", getLogDateTime());
mesh::Utils::printHex(_packet_log, raw, len);
_packet_log.print("\n");
#endif
}
virtual void logRx(Packet* packet, int len, float score) { } // hooks for custom logging
virtual void logTx(Packet* packet, int len) { }
@ -172,6 +209,9 @@ protected:
public:
void begin();
void loop();
#if MESH_PACKET_LOGGING
void setPacketLogStream(Print* s) { _packet_log.setStream(s); }
#endif
Packet* obtainNewPacket();
void releasePacket(Packet* packet);

View file

@ -99,7 +99,7 @@ void Utils::toHex(char* dest, const uint8_t* src, size_t len) {
*dest = 0;
}
void Utils::printHex(Stream& s, const uint8_t* src, size_t len) {
void Utils::printHex(Print& s, const uint8_t* src, size_t len) {
while (len > 0) {
uint8_t b = *src++;
s.print(hex_chars[b >> 4]);

View file

@ -1,7 +1,7 @@
#pragma once
#include <MeshCore.h>
#include <Stream.h>
#include <Print.h>
#include <string.h>
namespace mesh {
@ -69,7 +69,7 @@ public:
/**
* \brief Prints the hexadecimal representation of 'src' bytes of given length, to Stream 's'.
*/
static void printHex(Stream& s, const uint8_t* src, size_t len);
static void printHex(Print& s, const uint8_t* src, size_t len);
/**
* \brief parse 'text' into parts separated by 'separator' char.

View file

@ -0,0 +1,17 @@
#pragma once
/**
* Platform-selecting shim for BLELogInterface.
* Include this header and use BLELogInterface directly; the correct
* platform implementation is pulled in automatically.
*
* Supported platforms: ESP32, nRF52.
* On unsupported platforms this header intentionally defines nothing
* guard usage with #if defined(NRF52_PLATFORM) || defined(ESP32).
*/
#if defined(NRF52_PLATFORM)
#include "nrf52/BLELogInterface.h"
#elif defined(ESP32)
#include "esp32/BLELogInterface.h"
#endif

View file

@ -0,0 +1,132 @@
#pragma once
#include <Arduino.h>
#include <BLE2902.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
// Nordic UART Service UUIDs (standard, recognised by nRF Connect and bleak)
#define NUS_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define NUS_TX_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define BLE_LOG_ADVERT_RESTART_DELAY_MS 1000
/**
* Unsecured BLE UART (Nordic UART Service) logger for ESP32 platforms.
* Implements Print so it can be passed to Dispatcher::setPacketLogStream().
* Any BLE NUS client (e.g. nRF Connect, a Raspberry Pi running bleak) can
* connect without pairing and receive the log stream as plain text.
*
* Lines are buffered and flushed to BLE on each newline character.
* Call loop() from the Arduino loop() to handle advertising restart on disconnect.
*/
class BLELogInterface : public Print, BLEServerCallbacks {
BLEServer *_server;
BLECharacteristic *_tx_char;
bool _connected;
uint16_t _conn_id;
unsigned long _adv_restart_time;
char _line_buf[256];
int _line_len;
// Returns the max bytes per notification for the current connection.
// BLE notifications carry ATT_MTU-3 bytes of payload. Falls back to 20
// (the minimum guaranteed by the spec) if MTU has not been negotiated yet.
int notifyPayloadSize() const {
const uint16_t mtu = _server->getPeerMTU(_conn_id);
return (mtu > 3) ? mtu - 3 : 20;
}
void flushLine() {
if (_line_len == 0 || !_connected) return;
const int chunk = notifyPayloadSize();
int offset = 0;
while (offset < _line_len) {
int n = _line_len - offset;
if (n > chunk) n = chunk;
_tx_char->setValue((uint8_t *)_line_buf + offset, n);
_tx_char->notify();
offset += n;
}
_line_len = 0;
}
void onConnect(BLEServer *pServer) override {
_connected = true;
}
void onConnect(BLEServer *pServer, esp_ble_gatts_cb_param_t *param) override {
_connected = true;
_conn_id = param->connect.conn_id;
}
void onDisconnect(BLEServer *pServer) override {
_connected = false;
_line_len = 0; // discard partial line
_adv_restart_time = millis() + BLE_LOG_ADVERT_RESTART_DELAY_MS;
}
public:
BLELogInterface()
: _server(nullptr), _tx_char(nullptr), _connected(false), _conn_id(0), _adv_restart_time(0),
_line_len(0) {}
void begin(const char *device_name) {
BLEDevice::init(device_name);
BLEDevice::setMTU(256); // raise the ceiling; client may negotiate a larger MTU
// Explicitly disable bonding so the ESP32 does not send security requests.
// Without this the BLE stack initiates Just Works pairing by default, which
// fails with AUTH FAILED when no security callbacks are registered.
BLESecurity *pSecurity = new BLESecurity();
pSecurity->setAuthenticationMode(ESP_LE_AUTH_NO_BOND);
pSecurity->setCapability(ESP_IO_CAP_NONE);
_server = BLEDevice::createServer();
_server->setCallbacks(this);
BLEService *service = _server->createService(NUS_SERVICE_UUID);
_tx_char = service->createCharacteristic(NUS_TX_UUID, BLECharacteristic::PROPERTY_NOTIFY);
_tx_char->addDescriptor(new BLE2902());
// Characteristic Presentation Format (0x2904): declare value as UTF-8 string
// Format: format(1) exponent(1) unit(2) namespace(1) description(2)
static const uint8_t utf8_format[] = { 0x19, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00 };
BLEDescriptor *pFormat = new BLEDescriptor((uint16_t)0x2904);
pFormat->setValue(const_cast<uint8_t *>(utf8_format), sizeof(utf8_format));
_tx_char->addDescriptor(pFormat);
service->start();
BLEAdvertising *adv = BLEDevice::getAdvertising();
adv->addServiceUUID(NUS_SERVICE_UUID);
adv->setScanResponse(true);
adv->setMinPreferred(0x06); // helps iOS find and stay connected to the device
adv->setMaxPreferred(0x12);
BLEDevice::startAdvertising();
}
void loop() {
if (!_adv_restart_time || millis() < _adv_restart_time) return;
_adv_restart_time = 0;
if (_server->getConnectedCount() == 0) {
BLEDevice::startAdvertising();
}
}
size_t write(uint8_t c) override {
if (_line_len < (int)sizeof(_line_buf) - 1) {
_line_buf[_line_len++] = c;
}
if (c == '\n' || _line_len >= (int)sizeof(_line_buf) - 1) {
flushLine();
}
return 1;
}
size_t write(const uint8_t *buf, size_t size) override {
for (size_t i = 0; i < size; i++)
write(buf[i]);
return size;
}
};

View file

@ -0,0 +1,43 @@
#pragma once
#include <Arduino.h>
#include <bluefruit.h>
#ifndef BLE_LOG_TX_POWER
#define BLE_LOG_TX_POWER 4
#endif
/**
* Unsecured BLE UART (Nordic UART Service) logger for nRF52 platforms.
* Implements Print so it can be passed to Dispatcher::setPacketLogStream().
* Any BLE NUS client (e.g. nRF Connect, a Raspberry Pi running bleak) can
* connect without pairing and receive the log stream as plain text.
*/
class BLELogInterface : public Print {
BLEUart _uart;
public:
void begin(const char* device_name) {
Bluefruit.begin();
Bluefruit.setTxPower(BLE_LOG_TX_POWER);
Bluefruit.setName(device_name);
_uart.begin();
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addService(_uart);
Bluefruit.ScanResponse.addName();
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.start(0);
}
size_t write(uint8_t c) override {
return _uart.write(c);
}
size_t write(const uint8_t* buf, size_t size) override {
return _uart.write(buf, size);
}
};

View file

@ -179,6 +179,7 @@ build_flags =
-D ADMIN_PASSWORD='"password"'
-D ROOM_PASSWORD='"hello"'
; -D MESH_PACKET_LOGGING=1
; -D BLE_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_tracker_base.build_src_filter}
+<helpers/ui/ST7735Display.cpp>

View file

@ -27,6 +27,15 @@ build_src_filter = ${rp2040_base.build_src_filter}
+<PicoWBoard.cpp>
+<../variants/rpi_picow>
lib_deps = ${rp2040_base.lib_deps}
; Use chain+ so the LDF evaluates preprocessor conditions when scanning for
; library dependencies. Without this, the LDF (in default 'deep' mode) ignores
; #ifdef guards and follows all #include directives unconditionally. That causes
; 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.
lib_ldf_mode = chain+
[env:PicoW_repeater]
extends = rpi_picow

View file

@ -47,6 +47,7 @@ build_flags =
-D ADMIN_PASSWORD='"password"'
-D MAX_NEIGHBOURS=50
; -D MESH_PACKET_LOGGING=1
; -D BLE_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${SenseCap_Solar.build_src_filter}
+<../examples/simple_repeater/*.cpp>