From 65d8497fa902848ec647edd057078a9ce195a345 Mon Sep 17 00:00:00 2001 From: DatanoiseTV <6614616+DatanoiseTV@users.noreply.github.com> Date: Fri, 8 May 2026 21:33:47 +0200 Subject: [PATCH] mesh: add exponential backoff and jitter to retransmits setNextTx scheduled every retry at the same base offset returned by getRetransmissionMsec, which is driven by channel utilization rather than per-attempt history. Two nodes that simultaneously NAK each other re-collide on near-identical schedules and waste airtime. Use a 1x/2x/4x backoff (with the multiplier capped at 8x for safety) plus random half-base jitter so concurrent attempts spread out. --- src/mesh/NextHopRouter.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mesh/NextHopRouter.cpp b/src/mesh/NextHopRouter.cpp index e8613d45729..48a5175813b 100644 --- a/src/mesh/NextHopRouter.cpp +++ b/src/mesh/NextHopRouter.cpp @@ -349,9 +349,16 @@ int32_t NextHopRouter::doRetransmissions() void NextHopRouter::setNextTx(PendingPacket *pending) { assert(iface); - auto d = iface->getRetransmissionMsec(pending->packet); + // Two nodes that NAK each other will otherwise re-collide on near-identical + // schedules; spread successive attempts with exponential backoff plus jitter. + uint32_t base = iface->getRetransmissionMsec(pending->packet); + uint8_t attempt = (NUM_RELIABLE_RETX - 1) - pending->numRetransmissions; + uint8_t shift = attempt < 3 ? attempt : 3; // cap multiplier at 8x + uint32_t backoff = base << shift; + uint32_t jitter = base ? random(base / 2 + 1) : 0; + uint32_t d = backoff + jitter; pending->nextTxMsec = millis() + d; - LOG_DEBUG("Setting next retransmission in %u msecs: ", d); + LOG_DEBUG("Setting next retransmission in %u msecs (attempt %u): ", d, attempt); printPacket("", pending->packet); setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time }