mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-04-20 22:13:47 +00:00
Add BLE mesh logging
Made with Claude AI.
This commit is contained in:
parent
aedc00e16a
commit
9f7d644a0c
9 changed files with 217 additions and 18 deletions
|
|
@ -2,6 +2,11 @@
|
|||
#include <Mesh.h>
|
||||
|
||||
#include "MyMesh.h"
|
||||
#include <helpers/BLELogInterface.h>
|
||||
|
||||
#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 +100,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 +163,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)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
#include <Mesh.h>
|
||||
|
||||
#include "MyMesh.h"
|
||||
#include <helpers/BLELogInterface.h>
|
||||
|
||||
#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 +77,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 +123,7 @@ void loop() {
|
|||
ui_task.loop();
|
||||
#endif
|
||||
rtc_clock.tick();
|
||||
#if MESH_PACKET_LOGGING && BLE_PACKET_LOGGING && defined(ESP32)
|
||||
ble_log.loop();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -217,21 +217,24 @@ 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,
|
||||
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000), air_time);
|
||||
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "%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);
|
||||
_packet_log->print(buf);
|
||||
}
|
||||
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]);
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), " [%02X -> %02X]\n", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
|
||||
_packet_log->print(buf);
|
||||
} else {
|
||||
Serial.printf("\n");
|
||||
_packet_log->print("\n");
|
||||
}
|
||||
#endif
|
||||
logRx(pkt, pkt->getRawLength(), score); // hook for custom logging
|
||||
|
|
@ -338,14 +341,19 @@ 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);
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "%s: TX, len=%d (type=%d, route=%s, payload_len=%d)",
|
||||
getLogDateTime(), len, outbound->getPayloadType(), outbound->isRouteDirect() ? "D" : "F", outbound->payload_len);
|
||||
_packet_log->print(buf);
|
||||
}
|
||||
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]);
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), " [%02X -> %02X]\n", (uint32_t)outbound->payload[1], (uint32_t)outbound->payload[0]);
|
||||
_packet_log->print(buf);
|
||||
} else {
|
||||
Serial.printf("\n");
|
||||
_packet_log->print("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <MeshCore.h>
|
||||
#if MESH_PACKET_LOGGING && ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
#include <Identity.h>
|
||||
#include <Packet.h>
|
||||
#include <Utils.h>
|
||||
|
|
@ -129,6 +132,9 @@ class Dispatcher {
|
|||
|
||||
void processRecvPacket(Packet* pkt);
|
||||
void updateTxBudget();
|
||||
#if MESH_PACKET_LOGGING
|
||||
Print* _packet_log;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
PacketManager* _mgr;
|
||||
|
|
@ -150,6 +156,9 @@ protected:
|
|||
tx_budget_ms = 0;
|
||||
last_budget_update = 0;
|
||||
duty_cycle_window_ms = 3600000;
|
||||
#if MESH_PACKET_LOGGING
|
||||
_packet_log = &Serial;
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
|
||||
|
|
@ -172,6 +181,9 @@ protected:
|
|||
public:
|
||||
void begin();
|
||||
void loop();
|
||||
#if MESH_PACKET_LOGGING
|
||||
void setPacketLogStream(Print* s) { if (s) _packet_log = s; }
|
||||
#endif
|
||||
|
||||
Packet* obtainNewPacket();
|
||||
void releasePacket(Packet* packet);
|
||||
|
|
|
|||
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
17
src/helpers/BLELogInterface.h
Normal file
17
src/helpers/BLELogInterface.h
Normal 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
|
||||
93
src/helpers/esp32/BLELogInterface.h
Normal file
93
src/helpers/esp32/BLELogInterface.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.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;
|
||||
unsigned long _adv_restart_time;
|
||||
char _line_buf[256];
|
||||
int _line_len;
|
||||
|
||||
void flushLine() {
|
||||
if (_line_len > 0 && _connected) {
|
||||
_tx_char->setValue((uint8_t*)_line_buf, _line_len);
|
||||
_tx_char->notify();
|
||||
}
|
||||
_line_len = 0;
|
||||
}
|
||||
|
||||
void onConnect(BLEServer* pServer) override {
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
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), _adv_restart_time(0), _line_len(0) {}
|
||||
|
||||
void begin(const char* device_name) {
|
||||
BLEDevice::init(device_name);
|
||||
|
||||
_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());
|
||||
service->start();
|
||||
|
||||
_server->getAdvertising()->addServiceUUID(NUS_SERVICE_UUID);
|
||||
_server->getAdvertising()->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (_adv_restart_time && millis() >= _adv_restart_time) {
|
||||
if (_server->getConnectedCount() == 0) {
|
||||
_server->getAdvertising()->start();
|
||||
}
|
||||
_adv_restart_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
43
src/helpers/nrf52/BLELogInterface.h
Normal file
43
src/helpers/nrf52/BLELogInterface.h
Normal 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);
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue