mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-07-26 18:00:57 +00:00
Max retries is now a var that can be set between 1 to 15
This commit is contained in:
+36
-3
@@ -117,14 +117,21 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
### Get or set recent repeater fallback prefix/SNR
|
||||
**Usage:**
|
||||
- `recent.repeater`
|
||||
- `recent.repeater <prefix_hex> <snr_db>`
|
||||
- `get recent.repeater`
|
||||
- `get recent.repeater all`
|
||||
- `get recent.repeater first <count>`
|
||||
- `get recent.repeater last <count>`
|
||||
- `set recent.repeater <prefix_hex> <snr_db>`
|
||||
|
||||
**Parameters:**
|
||||
- `prefix_hex`: 1-3 bytes of next-hop prefix (hex)
|
||||
- `snr_db`: SNR in dB (supports decimals; stored at x4 precision)
|
||||
- `count`: number of entries to print
|
||||
|
||||
**Note:** `set` is rejected when the prefix already exists in neighbors.
|
||||
**Notes:**
|
||||
- `set` is rejected when the prefix already exists in neighbors.
|
||||
- `all` prints oldest to newest; `first` prints the oldest N; `last` prints the newest N.
|
||||
- Remote CLI replies include rows too, but may truncate when the packet payload limit is reached.
|
||||
|
||||
---
|
||||
|
||||
@@ -545,6 +552,32 @@ This document provides an overview of CLI commands that can be sent to MeshCore
|
||||
|
||||
---
|
||||
|
||||
#### View or change the number of direct retry attempts
|
||||
**Usage:**
|
||||
- `get direct.retry.count`
|
||||
- `set direct.retry.count <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Retry attempts after initial TX (`1`-`15`)
|
||||
|
||||
**Default:** `3`
|
||||
|
||||
---
|
||||
|
||||
#### View or change the base direct retry wait (milliseconds)
|
||||
**Usage:**
|
||||
- `get direct.retry.base`
|
||||
- `set direct.retry.base <value>`
|
||||
|
||||
**Parameters:**
|
||||
- `value`: Base wait in milliseconds (`10`-`5000`)
|
||||
|
||||
**Default:** `200`
|
||||
|
||||
**Note:** The actual first retry wait is `base + computed_echo_wait_from_live_phy`.
|
||||
|
||||
---
|
||||
|
||||
#### [Experimental] View or change the processing delay for received traffic
|
||||
**Usage:**
|
||||
- `get rxdelay`
|
||||
|
||||
@@ -627,16 +627,21 @@ bool MyMesh::allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_ho
|
||||
return recent != NULL && recent->snr_x4 >= min_snr_x4;
|
||||
}
|
||||
uint32_t MyMesh::getDirectRetryEchoDelay(const mesh::Packet* packet) const {
|
||||
uint32_t base_wait_millis = constrain((uint32_t)_prefs.direct_retry_base_ms, (uint32_t)10, (uint32_t)5000);
|
||||
// Approximate LoRa line rate in kilobits/sec from the live radio params the repeater is using now.
|
||||
float kbps = (((float) active_sf) * active_bw * ((float) active_cr)) / ((float) (1UL << active_sf));
|
||||
if (kbps <= 0.0f) {
|
||||
return HALO_DIRECT_RETRY_DELAY_MIN;
|
||||
return base_wait_millis;
|
||||
}
|
||||
|
||||
// Wait roughly long enough for our transmission, the next hop's receive/forward window, and its echo back.
|
||||
uint32_t bits = ((uint32_t) packet->getRawLength()) * 8;
|
||||
uint32_t scaled_wait_millis = (uint32_t) ((((float) bits) * 4.0f) / kbps);
|
||||
return max((uint32_t) HALO_DIRECT_RETRY_DELAY_MIN, scaled_wait_millis);
|
||||
return base_wait_millis + scaled_wait_millis;
|
||||
}
|
||||
uint8_t MyMesh::getDirectRetryMaxAttempts(const mesh::Packet* packet) const {
|
||||
(void)packet;
|
||||
return constrain(_prefs.direct_retry_attempts, (uint8_t)1, (uint8_t)15);
|
||||
}
|
||||
|
||||
bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
||||
@@ -970,6 +975,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.direct_tx_delay_factor = 0.3f; // was 0.2
|
||||
_prefs.direct_retry_recent_enabled = 0;
|
||||
_prefs.direct_retry_snr_margin_db = 5;
|
||||
_prefs.direct_retry_attempts = 3;
|
||||
_prefs.direct_retry_base_ms = 200;
|
||||
StrHelper::strncpy(_prefs.node_name, ADVERT_NAME, sizeof(_prefs.node_name));
|
||||
_prefs.node_lat = ADVERT_LAT;
|
||||
_prefs.node_lon = ADVERT_LON;
|
||||
@@ -1345,29 +1352,36 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
Serial.printf("\n");
|
||||
}
|
||||
reply[0] = 0;
|
||||
} else if (memcmp(command, "recent.repeater", 15) == 0) {
|
||||
const char* sub = command + 15;
|
||||
while (*sub == ' ') sub++;
|
||||
auto* tables = (SimpleMeshTables*)getTables();
|
||||
if (*sub == 0) {
|
||||
const auto* info = tables->getLatestRecentRepeater();
|
||||
if (info == NULL) {
|
||||
strcpy(reply, "> none");
|
||||
} else {
|
||||
char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1];
|
||||
mesh::Utils::toHex(hex, info->prefix, info->prefix_len);
|
||||
sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f));
|
||||
}
|
||||
} else if (strncmp(command, "get recent.repeater", 19) == 0
|
||||
|| strncmp(command, "set recent.repeater", 19) == 0
|
||||
|| strncmp(command, "recent.repeater", 15) == 0) {
|
||||
bool is_get = false;
|
||||
bool is_set = false;
|
||||
const char* sub = command;
|
||||
|
||||
if (strncmp(command, "get recent.repeater", 19) == 0) {
|
||||
is_get = true;
|
||||
sub = command + 19;
|
||||
} else if (strncmp(command, "set recent.repeater", 19) == 0) {
|
||||
is_set = true;
|
||||
sub = command + 19;
|
||||
} else {
|
||||
sub = command + 15; // legacy command format
|
||||
}
|
||||
while (*sub == ' ') sub++;
|
||||
|
||||
auto* tables = (SimpleMeshTables*)getTables();
|
||||
|
||||
if (is_set || (!is_get && *sub != 0 && strcmp(sub, "all") != 0 && strncmp(sub, "first ", 6) != 0 && strncmp(sub, "last ", 5) != 0)) {
|
||||
char* params = (char*) sub;
|
||||
char* arg_snr = strchr(params, ' ');
|
||||
if (arg_snr == NULL) {
|
||||
strcpy(reply, "Err - usage: recent.repeater <prefix_hex> <snr_db>");
|
||||
strcpy(reply, "Err - usage: set recent.repeater <prefix_hex> <snr_db>");
|
||||
} else {
|
||||
*arg_snr++ = 0;
|
||||
while (*arg_snr == ' ') arg_snr++;
|
||||
if (*arg_snr == 0) {
|
||||
strcpy(reply, "Err - usage: recent.repeater <prefix_hex> <snr_db>");
|
||||
strcpy(reply, "Err - usage: set recent.repeater <prefix_hex> <snr_db>");
|
||||
} else {
|
||||
int hex_len = strlen(params);
|
||||
int prefix_len = hex_len / 2;
|
||||
@@ -1386,6 +1400,94 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (*sub == 0) {
|
||||
const auto* info = tables->getLatestRecentRepeater();
|
||||
if (info == NULL) {
|
||||
strcpy(reply, "> none");
|
||||
} else {
|
||||
char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1];
|
||||
mesh::Utils::toHex(hex, info->prefix, info->prefix_len);
|
||||
sprintf(reply, "> %s,%s", hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f));
|
||||
}
|
||||
} else if (strcmp(sub, "all") == 0 || strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) {
|
||||
int total = tables->getRecentRepeaterCount();
|
||||
if (total <= 0) {
|
||||
strcpy(reply, "> none");
|
||||
} else {
|
||||
bool newest_first = false;
|
||||
int limit = total;
|
||||
const char* mode = "all";
|
||||
if (strncmp(sub, "first ", 6) == 0 || strncmp(sub, "last ", 5) == 0) {
|
||||
const char* nstr = sub + (sub[0] == 'f' ? 6 : 5);
|
||||
while (*nstr == ' ') nstr++;
|
||||
if (*nstr == 0) {
|
||||
strcpy(reply, "Err - usage: get recent.repeater first|last <count>");
|
||||
return;
|
||||
}
|
||||
char* end_ptr = NULL;
|
||||
long parsed = strtol(nstr, &end_ptr, 10);
|
||||
while (end_ptr != NULL && *end_ptr == ' ') end_ptr++;
|
||||
if (end_ptr == NULL || *end_ptr != 0 || parsed <= 0) {
|
||||
strcpy(reply, "Err - count must be > 0");
|
||||
return;
|
||||
}
|
||||
limit = (int)parsed;
|
||||
if (sub[0] == 'l') {
|
||||
newest_first = true;
|
||||
mode = "last";
|
||||
} else {
|
||||
mode = "first";
|
||||
}
|
||||
}
|
||||
if (limit > total) {
|
||||
limit = total;
|
||||
}
|
||||
|
||||
if (sender_timestamp == 0) {
|
||||
Serial.printf("Recent repeater table (%s %d/%d):\n", mode, limit, total);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i);
|
||||
if (info == NULL) {
|
||||
continue;
|
||||
}
|
||||
char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1];
|
||||
mesh::Utils::toHex(hex, info->prefix, info->prefix_len);
|
||||
Serial.printf("%02d: %s,%s\n", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f));
|
||||
}
|
||||
sprintf(reply, "> showing %d/%d (%s)", limit, total, mode);
|
||||
} else {
|
||||
// Remote CLI replies are packet-bound, so include as many rows as fit.
|
||||
int written = snprintf(reply, 160, "> showing %d/%d (%s)", limit, total, mode);
|
||||
bool truncated = false;
|
||||
if (written < 0) {
|
||||
reply[0] = 0;
|
||||
written = 0;
|
||||
}
|
||||
for (int i = 0; i < limit; i++) {
|
||||
const auto* info = newest_first ? tables->getRecentRepeaterNewestByIdx(i) : tables->getRecentRepeaterOldestByIdx(i);
|
||||
if (info == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (written >= 154) {
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
char hex[(MAX_ROUTE_HASH_BYTES * 2) + 1];
|
||||
mesh::Utils::toHex(hex, info->prefix, info->prefix_len);
|
||||
int n = snprintf(reply + written, 160 - written, "\n%02d:%s,%s", i + 1, hex, StrHelper::ftoa(((float)info->snr_x4) / 4.0f));
|
||||
if (n < 0 || n >= (160 - written)) {
|
||||
truncated = true;
|
||||
break;
|
||||
}
|
||||
written += n;
|
||||
}
|
||||
if (truncated && written < 156) {
|
||||
snprintf(reply + written, 160 - written, "\n...");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Err - usage: get recent.repeater [all|first <count>|last <count>]");
|
||||
}
|
||||
} else if (memcmp(command, "discover.neighbors", 18) == 0) {
|
||||
const char* sub = command + 18;
|
||||
|
||||
@@ -154,6 +154,7 @@ protected:
|
||||
uint32_t getDirectRetransmitDelay(const mesh::Packet* packet) override;
|
||||
bool allowDirectRetry(const mesh::Packet* packet, const uint8_t* next_hop_hash, uint8_t next_hop_hash_len) const override;
|
||||
uint32_t getDirectRetryEchoDelay(const mesh::Packet* packet) const override;
|
||||
uint8_t getDirectRetryMaxAttempts(const mesh::Packet* packet) const override;
|
||||
void onDirectRetryEvent(const char* event, const mesh::Packet* packet, uint32_t delay_millis) override;
|
||||
|
||||
int getInterferenceThreshold() const override {
|
||||
|
||||
+23
-7
@@ -3,8 +3,8 @@
|
||||
|
||||
namespace mesh {
|
||||
|
||||
static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS = 3;
|
||||
static const uint32_t DIRECT_RETRY_BACKOFF_MS[DIRECT_RETRY_MAX_ATTEMPTS] = { 200, 300, 400 };
|
||||
static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT = 3;
|
||||
static const uint8_t DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX = 15;
|
||||
|
||||
void Mesh::begin() {
|
||||
for (int i = 0; i < MAX_DIRECT_RETRY_SLOTS; i++) {
|
||||
@@ -57,6 +57,14 @@ uint32_t Mesh::getDirectRetryEchoDelay(const Packet* packet) const {
|
||||
// Keep the base fallback aligned with the repeater's minimum retry wait.
|
||||
return 200;
|
||||
}
|
||||
uint8_t Mesh::getDirectRetryMaxAttempts(const Packet* packet) const {
|
||||
return DIRECT_RETRY_MAX_ATTEMPTS_DEFAULT;
|
||||
}
|
||||
uint32_t Mesh::getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const {
|
||||
uint32_t base = getDirectRetryEchoDelay(packet);
|
||||
// Keep the historical linear spacing while allowing the base wait to vary by platform/profile.
|
||||
return base + ((uint32_t)attempt_idx * 100UL);
|
||||
}
|
||||
uint8_t Mesh::getExtraAckTransmitCount() const {
|
||||
return 0;
|
||||
}
|
||||
@@ -517,7 +525,13 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) {
|
||||
// The retry packet itself just finished transmitting; Dispatcher will release it after this hook.
|
||||
onDirectRetryEvent("resent", packet, 0);
|
||||
_direct_retries[i].retry_attempts_sent++;
|
||||
if (_direct_retries[i].retry_attempts_sent >= DIRECT_RETRY_MAX_ATTEMPTS) {
|
||||
uint8_t max_attempts = getDirectRetryMaxAttempts(packet);
|
||||
if (max_attempts < 1) {
|
||||
max_attempts = 1;
|
||||
} else if (max_attempts > DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX) {
|
||||
max_attempts = DIRECT_RETRY_MAX_ATTEMPTS_HARD_MAX;
|
||||
}
|
||||
if (_direct_retries[i].retry_attempts_sent >= max_attempts) {
|
||||
onDirectRetryEvent("failure", packet, 0);
|
||||
clearDirectRetrySlot(i);
|
||||
continue;
|
||||
@@ -532,7 +546,7 @@ void Mesh::armDirectRetryOnSendComplete(const Packet* packet) {
|
||||
}
|
||||
|
||||
*retry = *packet;
|
||||
uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[_direct_retries[i].retry_attempts_sent];
|
||||
uint32_t retry_delay = getDirectRetryAttemptDelay(packet, _direct_retries[i].retry_attempts_sent);
|
||||
sendPacket(retry, _direct_retries[i].priority, retry_delay);
|
||||
if (isDirectRetryQueued(retry)) {
|
||||
_direct_retries[i].packet = retry;
|
||||
@@ -612,7 +626,9 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h
|
||||
case PAYLOAD_TYPE_RESPONSE:
|
||||
case PAYLOAD_TYPE_TXT_MSG:
|
||||
case PAYLOAD_TYPE_ANON_REQ:
|
||||
if (packet->getPathHashCount() <= 1) {
|
||||
// Allow retries even when only one downstream hop remains so fixed direct paths
|
||||
// (e.g. remote admin/login over 2-hop chains) use the same retry policy.
|
||||
if (packet->getPathHashCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
next_hop_hash = packet->path;
|
||||
@@ -622,7 +638,7 @@ bool Mesh::getDirectRetryTarget(const Packet* packet, const uint8_t*& next_hop_h
|
||||
return true;
|
||||
|
||||
case PAYLOAD_TYPE_MULTIPART:
|
||||
if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() <= 1) {
|
||||
if (packet->payload_len < 1 || (packet->payload[0] & 0x0F) != PAYLOAD_TYPE_ACK || packet->getPathHashCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
next_hop_hash = packet->path;
|
||||
@@ -680,7 +696,7 @@ void Mesh::maybeScheduleDirectRetry(const Packet* packet, uint8_t priority) {
|
||||
}
|
||||
|
||||
// Only store retry metadata here; allocate the retry packet after the initial TX really completes.
|
||||
uint32_t retry_delay = DIRECT_RETRY_BACKOFF_MS[0];
|
||||
uint32_t retry_delay = getDirectRetryAttemptDelay(packet, 0);
|
||||
calculateDirectRetryKey(packet, _direct_retries[slot_idx].retry_key);
|
||||
_direct_retries[slot_idx].packet = NULL;
|
||||
_direct_retries[slot_idx].trigger_packet = const_cast<Packet*>(packet);
|
||||
|
||||
+10
@@ -107,6 +107,16 @@ protected:
|
||||
*/
|
||||
virtual uint32_t getDirectRetryEchoDelay(const Packet* packet) const;
|
||||
|
||||
/**
|
||||
* \returns maximum number of retry transmissions after the initial direct TX.
|
||||
*/
|
||||
virtual uint8_t getDirectRetryMaxAttempts(const Packet* packet) const;
|
||||
|
||||
/**
|
||||
* \returns delay before a specific retry attempt, where attempt_idx=0 is the first retry.
|
||||
*/
|
||||
virtual uint32_t getDirectRetryAttemptDelay(const Packet* packet, uint8_t attempt_idx) const;
|
||||
|
||||
/**
|
||||
* \returns number of extra (Direct) ACK transmissions wanted.
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,14 @@
|
||||
#define DIRECT_RETRY_RECENT_DEFAULT 0
|
||||
#define DIRECT_RETRY_SNR_MARGIN_DB_DEFAULT 5
|
||||
#define DIRECT_RETRY_SNR_MARGIN_DB_MAX 40
|
||||
#define DIRECT_RETRY_TIMING_MAGIC_0 0xD5
|
||||
#define DIRECT_RETRY_TIMING_MAGIC_1 0x54
|
||||
#define DIRECT_RETRY_COUNT_DEFAULT 3
|
||||
#define DIRECT_RETRY_COUNT_MIN 1
|
||||
#define DIRECT_RETRY_COUNT_MAX 15
|
||||
#define DIRECT_RETRY_BASE_MS_DEFAULT 200
|
||||
#define DIRECT_RETRY_BASE_MS_MIN 10
|
||||
#define DIRECT_RETRY_BASE_MS_MAX 5000
|
||||
|
||||
// Believe it or not, this std C function is busted on some platforms!
|
||||
static uint32_t _atoi(const char* sp) {
|
||||
@@ -97,7 +105,10 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
// next: 291
|
||||
file.read((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 291
|
||||
file.read((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 292
|
||||
file.read((uint8_t *)&_prefs->direct_retry_timing_magic[0], sizeof(_prefs->direct_retry_timing_magic)); // 294
|
||||
// next: 296
|
||||
|
||||
// sanitise bad pref values
|
||||
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
||||
@@ -121,6 +132,14 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
_prefs->direct_retry_recent_enabled = constrain(_prefs->direct_retry_recent_enabled, 0, 1);
|
||||
_prefs->direct_retry_snr_margin_db = constrain(_prefs->direct_retry_snr_margin_db, 0, DIRECT_RETRY_SNR_MARGIN_DB_MAX);
|
||||
}
|
||||
if (_prefs->direct_retry_timing_magic[0] != DIRECT_RETRY_TIMING_MAGIC_0
|
||||
|| _prefs->direct_retry_timing_magic[1] != DIRECT_RETRY_TIMING_MAGIC_1) {
|
||||
_prefs->direct_retry_attempts = DIRECT_RETRY_COUNT_DEFAULT;
|
||||
_prefs->direct_retry_base_ms = DIRECT_RETRY_BASE_MS_DEFAULT;
|
||||
} else {
|
||||
_prefs->direct_retry_attempts = constrain(_prefs->direct_retry_attempts, DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX);
|
||||
_prefs->direct_retry_base_ms = constrain(_prefs->direct_retry_base_ms, DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX);
|
||||
}
|
||||
|
||||
// sanitise bad bridge pref values
|
||||
_prefs->bridge_enabled = constrain(_prefs->bridge_enabled, 0, 1);
|
||||
@@ -201,7 +220,11 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
||||
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
// next: 291
|
||||
file.write((uint8_t *)&_prefs->direct_retry_attempts, sizeof(_prefs->direct_retry_attempts)); // 291
|
||||
file.write((uint8_t *)&_prefs->direct_retry_base_ms, sizeof(_prefs->direct_retry_base_ms)); // 292
|
||||
uint8_t retry_timing_magic[2] = { DIRECT_RETRY_TIMING_MAGIC_0, DIRECT_RETRY_TIMING_MAGIC_1 };
|
||||
file.write(retry_timing_magic, sizeof(retry_timing_magic)); // 294
|
||||
// next: 296
|
||||
|
||||
file.close();
|
||||
}
|
||||
@@ -363,6 +386,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
|
||||
sprintf(reply, "> %s", _prefs->direct_retry_recent_enabled ? "on" : "off");
|
||||
} else if (memcmp(config, "direct.retry.margin", 19) == 0) {
|
||||
sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_snr_margin_db);
|
||||
} else if (memcmp(config, "direct.retry.count", 18) == 0) {
|
||||
sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_attempts);
|
||||
} else if (memcmp(config, "direct.retry.base", 17) == 0) {
|
||||
sprintf(reply, "> %d", (uint32_t)_prefs->direct_retry_base_ms);
|
||||
} else if (memcmp(config, "owner.info", 10) == 0) {
|
||||
*reply++ = '>';
|
||||
*reply++ = ' ';
|
||||
@@ -633,6 +660,24 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
|
||||
} else {
|
||||
sprintf(reply, "Error, min 0 and max %d", DIRECT_RETRY_SNR_MARGIN_DB_MAX);
|
||||
}
|
||||
} else if (memcmp(config, "direct.retry.count ", 19) == 0) {
|
||||
int count = atoi(&config[19]);
|
||||
if (count >= DIRECT_RETRY_COUNT_MIN && count <= DIRECT_RETRY_COUNT_MAX) {
|
||||
_prefs->direct_retry_attempts = (uint8_t)count;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_COUNT_MIN, DIRECT_RETRY_COUNT_MAX);
|
||||
}
|
||||
} else if (memcmp(config, "direct.retry.base ", 18) == 0) {
|
||||
int delay_ms = atoi(&config[18]);
|
||||
if (delay_ms >= DIRECT_RETRY_BASE_MS_MIN && delay_ms <= DIRECT_RETRY_BASE_MS_MAX) {
|
||||
_prefs->direct_retry_base_ms = (uint16_t)delay_ms;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
sprintf(reply, "Error, min %d and max %d", DIRECT_RETRY_BASE_MS_MIN, DIRECT_RETRY_BASE_MS_MAX);
|
||||
}
|
||||
} else if (memcmp(config, "owner.info ", 11) == 0) {
|
||||
config += 11;
|
||||
char *dp = _prefs->owner_info;
|
||||
|
||||
@@ -63,6 +63,9 @@ struct NodePrefs { // persisted to file
|
||||
uint8_t rx_boosted_gain; // power settings
|
||||
uint8_t path_hash_mode; // which path mode to use when sending
|
||||
uint8_t loop_detect;
|
||||
uint8_t direct_retry_attempts;
|
||||
uint16_t direct_retry_base_ms;
|
||||
uint8_t direct_retry_timing_magic[2];
|
||||
};
|
||||
|
||||
class CommonCLICallbacks {
|
||||
|
||||
@@ -279,6 +279,51 @@ public:
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
int getRecentRepeaterCount() const {
|
||||
int count = 0;
|
||||
for (int i = 0; i < MAX_RECENT_REPEATERS; i++) {
|
||||
if (_recent_repeaters[i].prefix_len > 0) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
const RecentRepeaterInfo* getRecentRepeaterNewestByIdx(int idx_wanted) const {
|
||||
if (idx_wanted < 0) {
|
||||
return NULL;
|
||||
}
|
||||
int idx_seen = 0;
|
||||
for (int i = 0; i < MAX_RECENT_REPEATERS; i++) {
|
||||
int idx = (_next_recent_repeater_idx - 1 - i + MAX_RECENT_REPEATERS) % MAX_RECENT_REPEATERS;
|
||||
const RecentRepeaterInfo* info = &_recent_repeaters[idx];
|
||||
if (info->prefix_len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (idx_seen == idx_wanted) {
|
||||
return info;
|
||||
}
|
||||
idx_seen++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
const RecentRepeaterInfo* getRecentRepeaterOldestByIdx(int idx_wanted) const {
|
||||
if (idx_wanted < 0) {
|
||||
return NULL;
|
||||
}
|
||||
int idx_seen = 0;
|
||||
for (int i = 0; i < MAX_RECENT_REPEATERS; i++) {
|
||||
int idx = (_next_recent_repeater_idx + i) % MAX_RECENT_REPEATERS;
|
||||
const RecentRepeaterInfo* info = &_recent_repeaters[idx];
|
||||
if (info->prefix_len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (idx_seen == idx_wanted) {
|
||||
return info;
|
||||
}
|
||||
idx_seen++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const RecentRepeaterInfo* findRecentRepeaterByHash(const uint8_t* hash, uint8_t hash_len) const {
|
||||
if (hash == NULL || hash_len == 0) {
|
||||
|
||||
Reference in New Issue
Block a user