Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ This document provides an overview of CLI commands that can be sent to MeshCore

---

### Mirror packet log to serial (live)
**Usage:** `log tail on`

**Note:** Streams RX/TX log lines to USB serial as they are captured. Enables logging if not already active (`log start`). Serial-only (not available via remote admin).

---

### Stop live serial log mirror
**Usage:** `log tail off`

**Serial Only:** Yes

---

### Erase captured log
**Usage:** `log erase`

Expand Down
58 changes: 54 additions & 4 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,14 @@ const char *MyMesh::getLogDateTime() {
return tmp;
}

namespace {
const char* packetLogTypeLabel(const mesh::Packet* pkt) {
static char label[40];
mesh::formatPayloadType(pkt->getPayloadType(), label, sizeof label);
return label;
}
}

void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Expand All @@ -472,6 +480,38 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#endif
}

void MyMesh::mirrorPacketLogRxToSerial(mesh::Packet *pkt, int len, float score) {
Serial.print(getLogDateTime());
Serial.printf(": RX, len=%d (type=%s, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", len,
packetLogTypeLabel(pkt), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score * 1000));

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]", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
}
Serial.print("\r\n");
}

void MyMesh::mirrorPacketLogTxToSerial(mesh::Packet *pkt, int len) {
Serial.print(getLogDateTime());
Serial.printf(": TX, len=%d (type=%s, route=%s, payload_len=%d)", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);

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]", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
}
Serial.print("\r\n");
}

void MyMesh::mirrorPacketLogTxFailToSerial(mesh::Packet *pkt, int len) {
Serial.print(getLogDateTime());
Serial.printf(": TX FAIL!, len=%d (type=%s, route=%s, payload_len=%d)", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
Serial.print("\r\n");
}

void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_BRIDGE
if (_prefs.bridge_pkt_src == 1) {
Expand All @@ -483,8 +523,8 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", len,
pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
f.printf(": RX, len=%d (type=%s, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", len,
packetLogTypeLabel(pkt), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score * 1000));

if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ ||
Expand All @@ -496,6 +536,9 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
f.close();
}
}
if (_logging && _tailing) {
mirrorPacketLogRxToSerial(pkt, len, score);
}
}

void MyMesh::logTx(mesh::Packet *pkt, int len) {
Expand All @@ -509,7 +552,7 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)", len, pkt->getPayloadType(),
f.printf(": TX, len=%d (type=%s, route=%s, payload_len=%d)", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);

if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ ||
Expand All @@ -521,18 +564,24 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
f.close();
}
}
if (_logging && _tailing) {
mirrorPacketLogTxToSerial(pkt, len);
}
}

void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": TX FAIL!, len=%d (type=%d, route=%s, payload_len=%d)\n", len, pkt->getPayloadType(),
f.printf(": TX FAIL!, len=%d (type=%s, route=%s, payload_len=%d)\n", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
f.close();
}
}
if (_logging && _tailing) {
mirrorPacketLogTxFailToSerial(pkt, len);
}
}

int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
Expand Down Expand Up @@ -864,6 +913,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
dirty_contacts_expiry = 0;
set_radio_at = revert_radio_at = 0;
_logging = false;
_tailing = false;
region_load_active = false;
recv_pkt_region = NULL;

Expand Down
6 changes: 6 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
uint64_t uptime_millis;
unsigned long next_local_advert, next_flood_advert;
bool _logging;
bool _tailing;
NodePrefs _prefs;
ClientACL acl;
CommonCLI _cli;
Expand Down Expand Up @@ -129,6 +130,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {

File openAppend(const char* fname);
bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]);
void mirrorPacketLogRxToSerial(mesh::Packet* pkt, int len, float score);
void mirrorPacketLogTxToSerial(mesh::Packet* pkt, int len);
void mirrorPacketLogTxFailToSerial(mesh::Packet* pkt, int len);

protected:
float getAirtimeBudgetFactor() const override {
Expand Down Expand Up @@ -206,6 +210,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {

void setLoggingOn(bool enable) override { _logging = enable; }

void setTailOn(bool enable) override { _tailing = enable; }

void eraseLogFile() override {
_fs->remove(PACKET_LOG_FILE);
}
Expand Down
5 changes: 5 additions & 0 deletions examples/simple_repeater/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ void loop() {
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
if (c == 3) { // Ctrl+C — stop live log tail
the_mesh.setTailOn(false);
Serial.print("\r\n -> tail off\r\n");
continue;
}
if (c != '\n') {
command[len++] = c;
command[len] = 0;
Expand Down
58 changes: 54 additions & 4 deletions examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,14 @@ int MyMesh::handleRequest(ClientInfo *sender, uint32_t sender_timestamp, uint8_t
return 0; // unknown command
}

namespace {
const char* packetLogTypeLabel(const mesh::Packet* pkt) {
static char label[40];
mesh::formatPayloadType(pkt->getPayloadType(), label, sizeof label);
return label;
}
}

void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Expand All @@ -208,13 +216,45 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#endif
}

void MyMesh::mirrorPacketLogRxToSerial(mesh::Packet *pkt, int len, float score) {
Serial.print(getLogDateTime());
Serial.printf(": RX, len=%d (type=%s, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", len,
packetLogTypeLabel(pkt), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score * 1000));

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]", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
}
Serial.print("\r\n");
}

void MyMesh::mirrorPacketLogTxToSerial(mesh::Packet *pkt, int len) {
Serial.print(getLogDateTime());
Serial.printf(": TX, len=%d (type=%s, route=%s, payload_len=%d)", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);

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]", (uint32_t)pkt->payload[1], (uint32_t)pkt->payload[0]);
}
Serial.print("\r\n");
}

void MyMesh::mirrorPacketLogTxFailToSerial(mesh::Packet *pkt, int len) {
Serial.print(getLogDateTime());
Serial.printf(": TX FAIL!, len=%d (type=%s, route=%s, payload_len=%d)", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
Serial.print("\r\n");
}

void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", len,
pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
f.printf(": RX, len=%d (type=%s, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d", len,
packetLogTypeLabel(pkt), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
(int)_radio->getLastSNR(), (int)_radio->getLastRSSI(), (int)(score * 1000));

if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ ||
Expand All @@ -226,13 +266,16 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
f.close();
}
}
if (_logging && _tailing) {
mirrorPacketLogRxToSerial(pkt, len, score);
}
}
void MyMesh::logTx(mesh::Packet *pkt, int len) {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": TX, len=%d (type=%d, route=%s, payload_len=%d)", len, pkt->getPayloadType(),
f.printf(": TX, len=%d (type=%s, route=%s, payload_len=%d)", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);

if (pkt->getPayloadType() == PAYLOAD_TYPE_PATH || pkt->getPayloadType() == PAYLOAD_TYPE_REQ ||
Expand All @@ -244,17 +287,23 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
f.close();
}
}
if (_logging && _tailing) {
mirrorPacketLogTxToSerial(pkt, len);
}
}
void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": TX FAIL!, len=%d (type=%d, route=%s, payload_len=%d)\n", len, pkt->getPayloadType(),
f.printf(": TX FAIL!, len=%d (type=%s, route=%s, payload_len=%d)\n", len, packetLogTypeLabel(pkt),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
f.close();
}
}
if (_logging && _tailing) {
mirrorPacketLogTxFailToSerial(pkt, len);
}
}

int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
Expand Down Expand Up @@ -623,6 +672,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
next_local_advert = next_flood_advert = 0;
dirty_contacts_expiry = 0;
_logging = false;
_tailing = false;
region_load_active = false;
set_radio_at = revert_radio_at = 0;
recv_pkt_region = NULL;
Expand Down
6 changes: 6 additions & 0 deletions examples/simple_room_server/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
uint64_t uptime_millis;
unsigned long next_local_advert, next_flood_advert;
bool _logging;
bool _tailing;
bool region_load_active;
NodePrefs _prefs;
TransportKeyStore key_store;
Expand Down Expand Up @@ -124,6 +125,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
bool processAck(const uint8_t *data);
mesh::Packet* createSelfAdvert();
File openAppend(const char* fname);
void mirrorPacketLogRxToSerial(mesh::Packet* pkt, int len, float score);
void mirrorPacketLogTxToSerial(mesh::Packet* pkt, int len);
void mirrorPacketLogTxFailToSerial(mesh::Packet* pkt, int len);
int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len);

protected:
Expand Down Expand Up @@ -200,6 +204,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {

void setLoggingOn(bool enable) override { _logging = enable; }

void setTailOn(bool enable) override { _tailing = enable; }

void eraseLogFile() override {
_fs->remove(PACKET_LOG_FILE);
}
Expand Down
5 changes: 5 additions & 0 deletions examples/simple_room_server/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ void loop() {
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
if (c == 3) { // Ctrl+C — stop live log tail
the_mesh.setTailOn(false);
Serial.print("\r\n -> tail off\r\n");
continue;
}
if (c != '\n') {
command[len++] = c;
command[len] = 0;
Expand Down
1 change: 1 addition & 0 deletions examples/simple_sensor/SensorMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class SensorMesh : public mesh::Mesh, public CommonCLICallbacks {
void updateAdvertTimer() override;
void updateFloodAdvertTimer() override;
void setLoggingOn(bool enable) override { }
void setTailOn(bool enable) override { }
void eraseLogFile() override { }
void dumpLogFile() override { }
void setTxPower(int8_t power_dbm) override;
Expand Down
40 changes: 40 additions & 0 deletions src/Packet.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,49 @@
#include "Packet.h"
#include <stdio.h>
#include <string.h>
#include <SHA256.h>

namespace mesh {

namespace {

struct PayloadTypeName { uint8_t type; const char* name; };

static const PayloadTypeName PAYLOAD_TYPE_NAMES[] = {
{ PAYLOAD_TYPE_REQ, "PAYLOAD_TYPE_REQ" },
{ PAYLOAD_TYPE_RESPONSE, "PAYLOAD_TYPE_RESPONSE" },
{ PAYLOAD_TYPE_TXT_MSG, "PAYLOAD_TYPE_TXT_MSG" },
{ PAYLOAD_TYPE_ACK, "PAYLOAD_TYPE_ACK" },
{ PAYLOAD_TYPE_ADVERT, "PAYLOAD_TYPE_ADVERT" },
{ PAYLOAD_TYPE_GRP_TXT, "PAYLOAD_TYPE_GRP_TXT" },
{ PAYLOAD_TYPE_GRP_DATA, "PAYLOAD_TYPE_GRP_DATA" },
{ PAYLOAD_TYPE_ANON_REQ, "PAYLOAD_TYPE_ANON_REQ" },
{ PAYLOAD_TYPE_PATH, "PAYLOAD_TYPE_PATH" },
{ PAYLOAD_TYPE_TRACE, "PAYLOAD_TYPE_TRACE" },
{ PAYLOAD_TYPE_MULTIPART, "PAYLOAD_TYPE_MULTIPART" },
{ PAYLOAD_TYPE_CONTROL, "PAYLOAD_TYPE_CONTROL" },
{ PAYLOAD_TYPE_OTA, "PAYLOAD_TYPE_OTA" },
{ PAYLOAD_TYPE_RAW_CUSTOM, "PAYLOAD_TYPE_RAW_CUSTOM" },
};

} // namespace

const char* payloadTypeName(uint8_t type) {
for (unsigned i = 0; i < sizeof(PAYLOAD_TYPE_NAMES) / sizeof(PAYLOAD_TYPE_NAMES[0]); i++) {
if (PAYLOAD_TYPE_NAMES[i].type == type) return PAYLOAD_TYPE_NAMES[i].name;
}
return nullptr;
}

void formatPayloadType(uint8_t type, char* buf, size_t cap) {
const char* name = payloadTypeName(type);
if (name) {
snprintf(buf, cap, "%s (0x%02X)", name, type);
} else {
snprintf(buf, cap, "UNKNOWN (0x%02X)", type);
}
}

Packet::Packet() {
header = 0;
path_len = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/Packet.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,7 @@ class Packet {
bool readFrom(const uint8_t src[], uint8_t len);
};

const char* payloadTypeName(uint8_t type);
void formatPayloadType(uint8_t type, char* buf, size_t cap);

}
Loading