Adds serial commands to get stats

- Added formatStatsReply, formatRadioStatsReply, and formatPacketStatsReply methods in MyMesh for both simple_repeater, simple_room_server, and simple_sensor.
- Updated CommonCLI to handle new stats commands.
This commit is contained in:
Michael Hart 2025-10-07 09:45:45 -07:00
parent cb4468bd5d
commit 81ab944682
9 changed files with 114 additions and 0 deletions

View file

@ -787,6 +787,19 @@ void MyMesh::removeNeighbor(const uint8_t *pubkey, int key_len) {
#endif
}
void MyMesh::formatStatsReply(char *reply) {
StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr);
}
void MyMesh::formatRadioStatsReply(char *reply) {
StatsFormatHelper::formatRadioStats(reply, _radio, radio_driver, getTotalAirTime(), getReceiveAirTime());
}
void MyMesh::formatPacketStatsReply(char *reply) {
StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(),
getNumRecvFlood(), getNumRecvDirect());
}
void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
self_id = new_id;
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)

View file

@ -30,6 +30,7 @@
#include <helpers/IdentityStore.h>
#include <helpers/SimpleMeshTables.h>
#include <helpers/StaticPoolPacketManager.h>
#include <helpers/StatsFormatHelper.h>
#include <helpers/TxtDataHelpers.h>
#ifdef WITH_BRIDGE
@ -183,6 +184,9 @@ public:
void setTxPower(uint8_t power_dbm) override;
void formatNeighborsReply(char *reply) override;
void removeNeighbor(const uint8_t* pubkey, int key_len) override;
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
mesh::LocalIdentity& getSelfId() override { return self_id; }

View file

@ -729,6 +729,19 @@ void MyMesh::clearStats() {
((SimpleMeshTables *)getTables())->resetStats();
}
void MyMesh::formatStatsReply(char *reply) {
StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr);
}
void MyMesh::formatRadioStatsReply(char *reply) {
StatsFormatHelper::formatRadioStats(reply, _radio, radio_driver, getTotalAirTime(), getReceiveAirTime());
}
void MyMesh::formatPacketStatsReply(char *reply) {
StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(),
getNumRecvFlood(), getNumRecvDirect());
}
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
while (*command == ' ')
command++; // skip leading spaces

View file

@ -18,6 +18,7 @@
#include <helpers/AdvertDataHelpers.h>
#include <helpers/TxtDataHelpers.h>
#include <helpers/CommonCLI.h>
#include <helpers/StatsFormatHelper.h>
#include <helpers/ClientACL.h>
#include <RTClib.h>
#include <target.h>
@ -192,6 +193,9 @@ public:
void formatNeighborsReply(char *reply) override {
strcpy(reply, "not supported");
}
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
mesh::LocalIdentity& getSelfId() override { return self_id; }

View file

@ -769,6 +769,19 @@ void SensorMesh::setTxPower(uint8_t power_dbm) {
radio_set_tx_power(power_dbm);
}
void SensorMesh::formatStatsReply(char *reply) {
StatsFormatHelper::formatCoreStats(reply, board, *_ms, _err_flags, _mgr);
}
void SensorMesh::formatRadioStatsReply(char *reply) {
StatsFormatHelper::formatRadioStats(reply, _radio, radio_driver, getTotalAirTime(), getReceiveAirTime());
}
void SensorMesh::formatPacketStatsReply(char *reply) {
StatsFormatHelper::formatPacketStats(reply, radio_driver, getNumSentFlood(), getNumSentDirect(),
getNumRecvFlood(), getNumRecvDirect());
}
float SensorMesh::getTelemValue(uint8_t channel, uint8_t type) {
auto buf = telemetry.getBuffer();
uint8_t size = telemetry.getSize();

View file

@ -20,6 +20,7 @@
#include <helpers/AdvertDataHelpers.h>
#include <helpers/TxtDataHelpers.h>
#include <helpers/CommonCLI.h>
#include <helpers/StatsFormatHelper.h>
#include <helpers/ClientACL.h>
#include <RTClib.h>
#include <target.h>
@ -69,6 +70,9 @@ public:
void formatNeighborsReply(char *reply) override {
strcpy(reply, "not supported");
}
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
mesh::LocalIdentity& getSelfId() override { return self_id; }
void saveIdentity(const mesh::LocalIdentity& new_id) override;
void clearStats() override { }

View file

@ -663,6 +663,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
} else if (sender_timestamp == 0 && memcmp(command, "log", 3) == 0) {
_callbacks->dumpLogFile();
strcpy(reply, " EOF");
} else if (sender_timestamp == 0 && memcmp(command, "stats-packets", 13) == 0 && (command[13] == 0 || command[13] == ' ')) {
_callbacks->formatPacketStatsReply(reply);
} else if (sender_timestamp == 0 && memcmp(command, "stats-radio", 11) == 0 && (command[11] == 0 || command[11] == ' ')) {
_callbacks->formatRadioStatsReply(reply);
} else if (sender_timestamp == 0 && memcmp(command, "stats-core", 10) == 0 && (command[10] == 0 || command[10] == ' ')) {
_callbacks->formatStatsReply(reply);
} else {
strcpy(reply, "Unknown command");
}

View file

@ -66,6 +66,9 @@ public:
virtual void removeNeighbor(const uint8_t* pubkey, int key_len) {
// no op by default
};
virtual void formatStatsReply(char *reply) = 0;
virtual void formatRadioStatsReply(char *reply) = 0;
virtual void formatPacketStatsReply(char *reply) = 0;
virtual mesh::LocalIdentity& getSelfId() = 0;
virtual void saveIdentity(const mesh::LocalIdentity& new_id) = 0;
virtual void clearStats() = 0;

View file

@ -0,0 +1,54 @@
#pragma once
#include "Mesh.h"
class StatsFormatHelper {
public:
static void formatCoreStats(char* reply,
mesh::MainBoard& board,
mesh::MillisecondClock& ms,
uint16_t err_flags,
mesh::PacketManager* mgr) {
sprintf(reply,
"{\"battery_mv\":%u,\"uptime_secs\":%u,\"errors\":%u,\"queue_len\":%u}",
board.getBattMilliVolts(),
ms.getMillis() / 1000,
err_flags,
mgr->getOutboundCount(0xFFFFFFFF)
);
}
template<typename RadioDriverType>
static void formatRadioStats(char* reply,
mesh::Radio* radio,
RadioDriverType& driver,
uint32_t total_air_time_ms,
uint32_t total_rx_air_time_ms) {
sprintf(reply,
"{\"noise_floor\":%d,\"last_rssi\":%d,\"last_snr\":%.2f,\"tx_air_secs\":%u,\"rx_air_secs\":%u}",
(int16_t)radio->getNoiseFloor(),
(int16_t)driver.getLastRSSI(),
driver.getLastSNR(),
total_air_time_ms / 1000,
total_rx_air_time_ms / 1000
);
}
template<typename RadioDriverType>
static void formatPacketStats(char* reply,
RadioDriverType& driver,
uint32_t n_sent_flood,
uint32_t n_sent_direct,
uint32_t n_recv_flood,
uint32_t n_recv_direct) {
sprintf(reply,
"{\"recv\":%u,\"sent\":%u,\"flood_tx\":%u,\"direct_tx\":%u,\"flood_rx\":%u,\"direct_rx\":%u}",
driver.getPacketsRecv(),
driver.getPacketsSent(),
n_sent_flood,
n_sent_direct,
n_recv_flood,
n_recv_direct
);
}
};