Files
ZephCore/docs/adr/0007-adaptive-contention-window.md
liquidraverandClaude Opus 5.5 9fc1eaf18a docs: publish the system design and architecture decision records
DESIGN.md is the reconciled high-level design after the 2026-09 restructure:
system context, roles, build-time composition, layers and dependency rules
(with the known exceptions), threading and event loop, end-to-end flows,
cross-cutting concerns and the upstream alignment policy. docs/adr/ holds the
nine decision records (ESP32 boot layout, S3 native USB, overlay precedence,
BLE privacy, Zephyr main pin, SX126x duty cycle and RX-busy gating, adaptive
contention window, storage backend, WiFi companion boards).
ARCHITECTURE.md stays the detailed component reference and links both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:01:28 +02:00

1.9 KiB

ADR 0007 — Adaptive contention window instead of Arduino's static tx/rx delays

Status: accepted (recorded 2026-09-25; ZephCore-only)

Context

Replaces Arduino's static txdelay/rxdelay with three mechanisms:

  • EMA-based flood delay factor: scales flood TX retransmit jitter adaptively based on local contention history. Jitter is double-capped: min(2000ms, 6·airtime) for repeaters — the airtime-scaled ceiling keeps SF7/narrow-BW configs from wasting time in oversized windows, while the absolute cap bounds per-hop latency in dense areas. Ring buffer holds 24 concurrent tracked retransmits (sized for ~50-neighbor hilltops).
  • Per-dupe reactive backoff: each heard duplicate of a pending TX packet delays transmission by backoff_multiplier * airtime from NOW. Capped at min(2000ms, 12·airtime) total reactive extension per packet. After the cap, CAD handles channel-busy detection like Arduino.
  • Initial-flood jitter (companion-only): companions don't forward floods (when client_repeat=0) and use a fixed 20..150ms window for originated TX — small enough to feel responsive, large enough to avoid lockstep collisions with nearby repeaters. Passive flood tracking is disabled in that mode (no EMA warming needed). When client_repeat=1, the EMA is warmed naturally via the forwarding path and getRetransmitDelay() falls back to the same adaptive math as repeaters.
  • Direct packets: use minimal fixed jitter only (no adaptive scaling).

Decision

set backoff.multiplier X controls per-dupe delay (default 0.2; 0.0 disables reactive backoff). The old CLI commands (txdelay, rxdelay, direct.txdelay) are accepted for prefs compatibility but ignored; get txdelay shows the adaptive state.

Consequences

A divergence from upstream by design (memory acw-reactive-backoff); ports touching Mesh/Dispatcher timing must keep it.