Browse Source

feat(cli): add log tail on/off serial mirror

Co-authored-by: Cursor <[email protected]>
pull/2991/head
Ben Allfree 1 week ago
parent
commit
12ff56bef0
  1. 14
      docs/cli_commands.md
  2. 42
      examples/simple_repeater/MyMesh.cpp
  3. 6
      examples/simple_repeater/MyMesh.h
  4. 5
      examples/simple_repeater/main.cpp
  5. 42
      examples/simple_room_server/MyMesh.cpp
  6. 6
      examples/simple_room_server/MyMesh.h
  7. 5
      examples/simple_room_server/main.cpp
  8. 1
      examples/simple_sensor/SensorMesh.h
  9. 8
      src/helpers/CommonCLI.cpp
  10. 1
      src/helpers/CommonCLI.h

14
docs/cli_commands.md

@ -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. No-op unless `log start` is active. Serial-only (not available via remote admin).
---
### Stop live serial log mirror
**Usage:** `log tail off`
**Serial Only:** Yes
---
### Erase captured log ### Erase captured log
**Usage:** `log erase` **Usage:** `log erase`

42
examples/simple_repeater/MyMesh.cpp

@ -472,6 +472,38 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#endif #endif
} }
void MyMesh::mirrorPacketLogRxToSerial(mesh::Packet *pkt, int len, float score) {
Serial.print(getLogDateTime());
Serial.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,
(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=%d, route=%s, payload_len=%d)", len, pkt->getPayloadType(),
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=%d, route=%s, payload_len=%d)", len, pkt->getPayloadType(),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
Serial.print("\r\n");
}
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) { void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
#ifdef WITH_BRIDGE #ifdef WITH_BRIDGE
if (_prefs.bridge_pkt_src == 1) { if (_prefs.bridge_pkt_src == 1) {
@ -496,6 +528,9 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
f.close(); f.close();
} }
} }
if (_logging && _tailing) {
mirrorPacketLogRxToSerial(pkt, len, score);
}
} }
void MyMesh::logTx(mesh::Packet *pkt, int len) { void MyMesh::logTx(mesh::Packet *pkt, int len) {
@ -521,6 +556,9 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
f.close(); f.close();
} }
} }
if (_logging && _tailing) {
mirrorPacketLogTxToSerial(pkt, len);
}
} }
void MyMesh::logTxFail(mesh::Packet *pkt, int len) { void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
@ -533,6 +571,9 @@ void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
f.close(); f.close();
} }
} }
if (_logging && _tailing) {
mirrorPacketLogTxFailToSerial(pkt, len);
}
} }
int MyMesh::calcRxDelay(float score, uint32_t air_time) const { int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
@ -864,6 +905,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
dirty_contacts_expiry = 0; dirty_contacts_expiry = 0;
set_radio_at = revert_radio_at = 0; set_radio_at = revert_radio_at = 0;
_logging = false; _logging = false;
_tailing = false;
region_load_active = false; region_load_active = false;
recv_pkt_region = NULL; recv_pkt_region = NULL;

6
examples/simple_repeater/MyMesh.h

@ -86,6 +86,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
uint64_t uptime_millis; uint64_t uptime_millis;
unsigned long next_local_advert, next_flood_advert; unsigned long next_local_advert, next_flood_advert;
bool _logging; bool _logging;
bool _tailing;
NodePrefs _prefs; NodePrefs _prefs;
ClientACL acl; ClientACL acl;
CommonCLI _cli; CommonCLI _cli;
@ -129,6 +130,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
File openAppend(const char* fname); File openAppend(const char* fname);
bool isLooped(const mesh::Packet* packet, const uint8_t max_counters[]); 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: protected:
float getAirtimeBudgetFactor() const override { float getAirtimeBudgetFactor() const override {
@ -206,6 +210,8 @@ public:
void setLoggingOn(bool enable) override { _logging = enable; } void setLoggingOn(bool enable) override { _logging = enable; }
void setTailOn(bool enable) override { _tailing = enable; }
void eraseLogFile() override { void eraseLogFile() override {
_fs->remove(PACKET_LOG_FILE); _fs->remove(PACKET_LOG_FILE);
} }

5
examples/simple_repeater/main.cpp

@ -127,6 +127,11 @@ void loop() {
int len = strlen(command); int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) { while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read(); 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') { if (c != '\n') {
command[len++] = c; command[len++] = c;
command[len] = 0; command[len] = 0;

42
examples/simple_room_server/MyMesh.cpp

@ -208,6 +208,38 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) {
#endif #endif
} }
void MyMesh::mirrorPacketLogRxToSerial(mesh::Packet *pkt, int len, float score) {
Serial.print(getLogDateTime());
Serial.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,
(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=%d, route=%s, payload_len=%d)", len, pkt->getPayloadType(),
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=%d, route=%s, payload_len=%d)", len, pkt->getPayloadType(),
pkt->isRouteDirect() ? "D" : "F", pkt->payload_len);
Serial.print("\r\n");
}
void MyMesh::logRx(mesh::Packet *pkt, int len, float score) { void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
if (_logging) { if (_logging) {
File f = openAppend(PACKET_LOG_FILE); File f = openAppend(PACKET_LOG_FILE);
@ -226,6 +258,9 @@ void MyMesh::logRx(mesh::Packet *pkt, int len, float score) {
f.close(); f.close();
} }
} }
if (_logging && _tailing) {
mirrorPacketLogRxToSerial(pkt, len, score);
}
} }
void MyMesh::logTx(mesh::Packet *pkt, int len) { void MyMesh::logTx(mesh::Packet *pkt, int len) {
if (_logging) { if (_logging) {
@ -244,6 +279,9 @@ void MyMesh::logTx(mesh::Packet *pkt, int len) {
f.close(); f.close();
} }
} }
if (_logging && _tailing) {
mirrorPacketLogTxToSerial(pkt, len);
}
} }
void MyMesh::logTxFail(mesh::Packet *pkt, int len) { void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
if (_logging) { if (_logging) {
@ -255,6 +293,9 @@ void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
f.close(); f.close();
} }
} }
if (_logging && _tailing) {
mirrorPacketLogTxFailToSerial(pkt, len);
}
} }
int MyMesh::calcRxDelay(float score, uint32_t air_time) const { int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
@ -623,6 +664,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
next_local_advert = next_flood_advert = 0; next_local_advert = next_flood_advert = 0;
dirty_contacts_expiry = 0; dirty_contacts_expiry = 0;
_logging = false; _logging = false;
_tailing = false;
region_load_active = false; region_load_active = false;
set_radio_at = revert_radio_at = 0; set_radio_at = revert_radio_at = 0;
recv_pkt_region = NULL; recv_pkt_region = NULL;

6
examples/simple_room_server/MyMesh.h

@ -94,6 +94,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
uint64_t uptime_millis; uint64_t uptime_millis;
unsigned long next_local_advert, next_flood_advert; unsigned long next_local_advert, next_flood_advert;
bool _logging; bool _logging;
bool _tailing;
bool region_load_active; bool region_load_active;
NodePrefs _prefs; NodePrefs _prefs;
TransportKeyStore key_store; TransportKeyStore key_store;
@ -124,6 +125,9 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
bool processAck(const uint8_t *data); bool processAck(const uint8_t *data);
mesh::Packet* createSelfAdvert(); mesh::Packet* createSelfAdvert();
File openAppend(const char* fname); 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); int handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len);
protected: protected:
@ -200,6 +204,8 @@ public:
void setLoggingOn(bool enable) override { _logging = enable; } void setLoggingOn(bool enable) override { _logging = enable; }
void setTailOn(bool enable) override { _tailing = enable; }
void eraseLogFile() override { void eraseLogFile() override {
_fs->remove(PACKET_LOG_FILE); _fs->remove(PACKET_LOG_FILE);
} }

5
examples/simple_room_server/main.cpp

@ -107,6 +107,11 @@ void loop() {
int len = strlen(command); int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) { while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read(); 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') { if (c != '\n') {
command[len++] = c; command[len++] = c;
command[len] = 0; command[len] = 0;

1
examples/simple_sensor/SensorMesh.h

@ -65,6 +65,7 @@ public:
void updateAdvertTimer() override; void updateAdvertTimer() override;
void updateFloodAdvertTimer() override; void updateFloodAdvertTimer() override;
void setLoggingOn(bool enable) override { } void setLoggingOn(bool enable) override { }
void setTailOn(bool enable) override { }
void eraseLogFile() override { } void eraseLogFile() override { }
void dumpLogFile() override { } void dumpLogFile() override { }
void setTxPower(int8_t power_dbm) override; void setTxPower(int8_t power_dbm) override;

8
src/helpers/CommonCLI.cpp

@ -461,6 +461,14 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
} else { } else {
strcpy(reply, "off"); strcpy(reply, "off");
} }
} else if (sender_timestamp == 0 && memcmp(command, "log tail on", 11) == 0
&& (command[11] == 0 || command[11] == ' ')) {
_callbacks->setTailOn(true);
strcpy(reply, " tail on");
} else if (sender_timestamp == 0 && memcmp(command, "log tail off", 12) == 0
&& (command[12] == 0 || command[12] == ' ')) {
_callbacks->setTailOn(false);
strcpy(reply, " tail off");
} else if (memcmp(command, "log start", 9) == 0) { } else if (memcmp(command, "log start", 9) == 0) {
_callbacks->setLoggingOn(true); _callbacks->setLoggingOn(true);
strcpy(reply, " logging on"); strcpy(reply, " logging on");

1
src/helpers/CommonCLI.h

@ -78,6 +78,7 @@ public:
virtual void updateAdvertTimer() = 0; virtual void updateAdvertTimer() = 0;
virtual void updateFloodAdvertTimer() = 0; virtual void updateFloodAdvertTimer() = 0;
virtual void setLoggingOn(bool enable) = 0; virtual void setLoggingOn(bool enable) = 0;
virtual void setTailOn(bool enable) = 0;
virtual void eraseLogFile() = 0; virtual void eraseLogFile() = 0;
virtual void dumpLogFile() = 0; virtual void dumpLogFile() = 0;
virtual void setTxPower(int8_t power_dbm) = 0; virtual void setTxPower(int8_t power_dbm) = 0;

Loading…
Cancel
Save