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
101 changes: 98 additions & 3 deletions docs/cli_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,27 +133,80 @@ This document provides an overview of CLI commands that can be sent to MeshCore
### Clear Stats
**Usage:** `clear stats`

Clears the node's runtime counters, including packet totals, radio counters, duplicate counters, error flags, and MAC/CAD diagnostic counters. It does not erase stored packet logs or change configuration.

---

### System Stats - Battery, Uptime, Queue Length and Debug Flags
**Usage:**
- `stats-core`

**Serial Only:** Yes
**Serial Only:** No

Returns JSON with:
- `battery_mv`: current board battery reading in millivolts
- `uptime_secs`: node uptime in seconds
- `errors`: dispatcher error bitmask
- `queue_len`: total queued outbound packets

---

### Radio Stats - Noise floor, Last RSSI/SNR, Airtime, Receive errors
**Usage:** `stats-radio`

**Serial Only:** Yes
**Serial Only:** No

Returns JSON with:
- `noise_floor`: current radio noise floor estimate in dBm
- `last_rssi`: RSSI from the most recent received packet
- `last_snr`: SNR from the most recent received packet
- `tx_air_secs`: accumulated transmit airtime in seconds
- `rx_air_secs`: accumulated receive airtime estimate in seconds

---

### Packet stats - Packet counters: Received, Sent
**Usage:** `stats-packets`

**Serial Only:** Yes
**Serial Only:** No

Returns JSON with:
- `recv` / `sent`: radio driver packet totals
- `flood_tx` / `direct_tx`: dispatcher transmit totals by route type
- `flood_rx` / `direct_rx`: dispatcher receive totals by route type
- `recv_errors`: radio driver receive error count

---

### MAC CAD stats - CAD deferral and timeout counters
**Usage:** `stats-mac-cad`

**Serial Only:** No

Returns JSON with:
- `busy`: local radio/CAD busy deferrals before the timeout threshold
- `timeouts`: times local CAD busy state exceeded the maximum busy duration
- `forced_tx`: CAD timeout events that force-transmitted because fail-open mode was selected
- `deferred_tx`: CAD timeout events that deferred and retried later
- `dropped_tx`: packets dropped after CAD timeout policy
- `expired_tx`: packets dropped because the maximum CAD deferral age or timeout count was reached

---

### MAC TX stats - TX, retransmit and queue counters
**Usage:** `stats-mac-tx`

**Serial Only:** No

Returns JSON with:
- `started`: transmit attempts started by the dispatcher
- `completed`: transmit completions reported by the radio
- `start_fail`: transmit attempts that failed to start
- `timeouts`: transmit operations that timed out
- `rx_delay`: received packets delayed before local processing
- `retransmits`: packets scheduled for retransmit or forward
- `pool_full`: packet allocation failures caused by pool exhaustion
- `bad_queue`: invalid queued packets dropped before transmit

---

Expand Down Expand Up @@ -512,6 +565,48 @@ This document provides an overview of CLI commands that can be sent to MeshCore

---

#### View or change CAD timeout policy
**Usage:**
- `get cad.timeout.policy`
- `set cad.timeout.policy <policy>`

**Parameters:**
- `policy`: `defer`, `drop`, or `force`

**Default:** `defer`

**Note:** CAD means Channel Activity Detection. When the radio reports the channel busy for longer than the CAD timeout, `defer` keeps the queued packet and retries later, `drop` removes the next due packet, and `force` preserves the older fail-open behaviour of transmitting anyway. `force` should only be used for diagnostics or radio-state fallback because it can worsen congestion.

---

#### View or change maximum CAD deferral age
**Usage:**
- `get cad.max.defer`
- `set cad.max.defer <seconds>`

**Parameters:**
- `seconds`: Maximum total age of the currently CAD-deferred packet, from 0 to 3600. `0` disables the age limit.

**Default:** `30`

**Note:** This prevents a packet from remaining queued indefinitely when the local radio keeps reporting the channel as busy. When the limit is reached, the next due packet is dropped and the `stats-mac` `ce` counter is incremented.

---

#### View or change maximum CAD timeout count
**Usage:**
- `get cad.max.timeouts`
- `set cad.max.timeouts <count>`

**Parameters:**
- `count`: Maximum number of CAD timeout cycles allowed for the currently deferred packet, from 0 to 255. `0` disables the count limit.

**Default:** `3`

**Note:** With the default CAD timeout of about 4 seconds, a value of `3` means a packet can survive roughly three confirmed busy timeout cycles before it is dropped. The age and count limits are both active; whichever limit is reached first wins.

---

#### [Experimental] View or change the processing delay for received traffic
**Usage:**
- `get rxdelay`
Expand Down
37 changes: 37 additions & 0 deletions examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,32 @@ void MyMesh::logTxFail(mesh::Packet *pkt, int len) {
}
}

void MyMesh::logMacEvent(const char* event, mesh::Packet* pkt, int len, uint8_t priority,
uint32_t delay_millis, uint32_t airtime_millis, uint32_t value) {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
if (f) {
f.print(getLogDateTime());
f.printf(": MAC, ms=%lu event=%s len=%d pri=%u delay=%lu airtime=%lu value=%lu q=%d free=%d nf=%d rssi=%d snr=%d",
_ms->getMillis(), event, len, (uint32_t)priority, (unsigned long)delay_millis,
(unsigned long)airtime_millis, (unsigned long)value,
_mgr->getOutboundTotal(), _mgr->getFreeCount(), (int)_radio->getNoiseFloor(),
(int)radio_driver.getLastRSSI(), (int)(radio_driver.getLastSNR() * 4));

if (pkt) {
uint8_t packet_hash[MAX_HASH_SIZE];
pkt->calculatePacketHash(packet_hash);
f.printf(" type=%d route=%s payload_len=%d path_count=%d path_hash_size=%d hash=",
pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
pkt->getPathHashCount(), pkt->getPathHashSize());
mesh::Utils::printHex(f, packet_hash, 4);
}
f.printf("\n");
f.close();
}
}
}

int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
if (_prefs.rx_delay_base <= 0.0f) return 0;
return (int)((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time);
Expand Down Expand Up @@ -893,6 +919,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_max_unscoped = 64;
_prefs.flood_max_advert = 8;
_prefs.interference_threshold = 0; // disabled
_prefs.cad_timeout_policy = CAD_TIMEOUT_DEFER;
_prefs.cad_max_defer_secs = DEFAULT_CAD_MAX_DEFER_SECS;
_prefs.cad_max_timeouts = DEFAULT_CAD_MAX_TIMEOUTS;

// bridge defaults
_prefs.bridge_enabled = 1; // enabled
Expand Down Expand Up @@ -1152,6 +1181,14 @@ void MyMesh::formatPacketStatsReply(char *reply) {
getNumRecvFlood(), getNumRecvDirect());
}

void MyMesh::formatMacCadStatsReply(char *reply) {
StatsFormatHelper::formatMacCadStats(reply, getMacStats());
}

void MyMesh::formatMacTxStatsReply(char *reply) {
StatsFormatHelper::formatMacTxStats(reply, getMacStats());
}

void MyMesh::saveIdentity(const mesh::LocalIdentity &new_id) {
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
IdentityStore store(*_fs, "");
Expand Down
13 changes: 13 additions & 0 deletions examples/simple_repeater/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
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;
void logMacEvent(const char* event, mesh::Packet* pkt, int len, uint8_t priority,
uint32_t delay_millis, uint32_t airtime_millis, uint32_t value) override;
int calcRxDelay(float score, uint32_t air_time) const override;

uint32_t getRetransmitDelay(const mesh::Packet* packet) override;
Expand All @@ -153,6 +155,15 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
}
uint8_t getCADTimeoutPolicy() const override {
return _prefs.cad_timeout_policy;
}
uint32_t getCADMaxDeferralMs() const override {
return ((uint32_t)_prefs.cad_max_defer_secs) * 1000UL;
}
uint8_t getCADMaxTimeouts() const override {
return _prefs.cad_max_timeouts;
}
uint8_t getExtraAckTransmitCount() const override {
return _prefs.multi_acks;
}
Expand Down Expand Up @@ -214,6 +225,8 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
void formatStatsReply(char *reply) override;
void formatRadioStatsReply(char *reply) override;
void formatPacketStatsReply(char *reply) override;
void formatMacCadStatsReply(char *reply) override;
void formatMacTxStatsReply(char *reply) override;
void startRegionsLoad() override;
bool saveRegions() override;
void onDefaultRegionChanged(const RegionEntry* r) override;
Expand Down
3 changes: 3 additions & 0 deletions examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_prefs.flood_max_unscoped = 64;
_prefs.flood_max_advert = 8;
_prefs.interference_threshold = 0; // disabled
_prefs.cad_timeout_policy = CAD_TIMEOUT_DEFER;
_prefs.cad_max_defer_secs = DEFAULT_CAD_MAX_DEFER_SECS;
_prefs.cad_max_timeouts = DEFAULT_CAD_MAX_TIMEOUTS;
#ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
#endif
Expand Down
9 changes: 9 additions & 0 deletions examples/simple_room_server/MyMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
}
uint8_t getCADTimeoutPolicy() const override {
return _prefs.cad_timeout_policy;
}
uint32_t getCADMaxDeferralMs() const override {
return ((uint32_t)_prefs.cad_max_defer_secs) * 1000UL;
}
uint8_t getCADMaxTimeouts() const override {
return _prefs.cad_max_timeouts;
}
uint8_t getExtraAckTransmitCount() const override {
return _prefs.multi_acks;
}
Expand Down
3 changes: 3 additions & 0 deletions examples/simple_sensor/SensorMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,9 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
_prefs.disable_fwd = true;
_prefs.flood_max = 64;
_prefs.interference_threshold = 0; // disabled
_prefs.cad_timeout_policy = CAD_TIMEOUT_DEFER;
_prefs.cad_max_defer_secs = DEFAULT_CAD_MAX_DEFER_SECS;
_prefs.cad_max_timeouts = DEFAULT_CAD_MAX_TIMEOUTS;

// GPS defaults
_prefs.gps_enabled = 0;
Expand Down
9 changes: 9 additions & 0 deletions examples/simple_sensor/SensorMesh.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ class SensorMesh : public mesh::Mesh, public CommonCLICallbacks {
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
int getInterferenceThreshold() const override;
int getAGCResetInterval() const override;
uint8_t getCADTimeoutPolicy() const override {
return _prefs.cad_timeout_policy;
}
uint32_t getCADMaxDeferralMs() const override {
return ((uint32_t)_prefs.cad_max_defer_secs) * 1000UL;
}
uint8_t getCADMaxTimeouts() const override {
return _prefs.cad_max_timeouts;
}
void onAnonDataRecv(mesh::Packet* packet, const uint8_t* secret, const mesh::Identity& sender, uint8_t* data, size_t len) override;
int searchPeersByHash(const uint8_t* hash) override;
void getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) override;
Expand Down
Loading