Max retries is now a var that can be set between 1 to 15

This commit is contained in:
mikecarper committed 2026-04-24 00:08:10 -07:00
1 parent 577433ce47
commit 9c2ac5aa1c
8 files changed
+284 -29

No files matched your search

+23 -7
View File
@@ -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
View File
@@ -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.
*/
+47 -2
View File
@@ -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;
+3
View File
@@ -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 {
+45
View File
@@ -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) {