This page explains how MeshCore repeater rxdelay, txdelay, and direct.txdelay work, what the numbers mean, and why some values should be avoided.
These settings are useful on repeater networks where several nodes may hear and forward the same packet. They are not magic range boosters. They trade timing and latency against collision reduction and duplicate suppression.
Source note: this page is based on MeshCore repeater firmware v1.16.0 behaviour, checked against
examples/simple_repeater/MyMesh.cpp,src/Dispatcher.cpp,src/helpers/radiolib/RadioLibWrappers.cpp, and the MeshCore CLI docs.
| Setting | Applies to | What it does | Normal range to try |
|---|---|---|---|
rxdelay |
Received flood packets | Delays processing of weak flood packets so stronger copies can be handled first | 0, or values greater than 1 |
txdelay |
Flood retransmits | Adds a random delay before forwarding flood traffic | 0.4 to 0.8 typical starting range |
direct.txdelay |
Direct routed retransmits | Adds a random delay before direct/routed retransmits | Usually lower than txdelay |
Practical defaults from the repeater firmware:
rxdelay 0.0 disabled
txdelay 0.5
direct.txdelay 0.3
Use:
set rxdelay 0
to disable the feature, or use values greater than 1 to enable the intended behaviour.
Avoid values between 0 and 1.
Values between 0 and 1 invert the rxdelay curve. Instead of weak packets being delayed and strong packets being processed first, weak packets can process immediately while very strong packets can be delayed. That changes the intended priority from strong-before-weak to weak-before-strong.
Also note:
set rxdelay 1
is effectively a no-op. The formula always produces zero delay when the base is 1.
rxdelay is only applied to received flood packets.
When a repeater receives a flood packet, MeshCore calculates a packet score. Stronger, cleaner, shorter packets get a higher score. Weaker packets get a lower score.
The idea is:
This is most useful where high or overlapping repeaters hear many copies of the same flood packet.
Small local infill repeaters should usually keep rxdelay disabled, because they may be the only local copy of a packet.
In the repeater firmware, the rxdelay calculation is:
delay_ms = (rxdelay ^ (0.85 - packet_score) - 1) * packet_airtime_ms
Implementation details:
rxdelay 0 disables the feature.rxdelay 1 produces zero delay.rxdelay.For values greater than 1:
0.85 gives a positive delay0.85 gives little or no delay0.85 gives a negative calculated delay, which is processed immediately because it is under the 50 ms thresholdFor values between 0 and 1, that behaviour reverses.
MeshCore derives packet_score from SNR margin, spreading factor, and packet length.
The source uses approximate SNR thresholds by spreading factor:
| Spreading factor | Approx SNR threshold |
|---|---|
| SF7 | -7.5 dB |
| SF8 | -10 dB |
| SF9 | -12.5 dB |
| SF10 | -15 dB |
| SF11 | -17.5 dB |
| SF12 | -20 dB |
Simplified:
snr_margin = packet_snr - sf_threshold
score ~= clamp((snr_margin / 10) * length_factor, 0, 1)
where length_factor is approximately:
1 - (packet_length / 256)
So a short packet with plenty of SNR margin scores high. A long or marginal packet scores lower.
Approximate calculated delay multiplier compared with packet airtime:
| Packet score | rxdelay 0.5 |
rxdelay 2 |
rxdelay 4 |
rxdelay 8 |
|---|---|---|---|---|
1.00 |
+0.11x |
-0.10x |
-0.19x |
-0.27x |
0.90 |
+0.04x |
-0.03x |
-0.07x |
-0.10x |
0.85 |
0.00x |
0.00x |
0.00x |
0.00x |
0.80 |
-0.03x |
+0.04x |
+0.07x |
+0.11x |
0.70 |
-0.10x |
+0.11x |
+0.23x |
+0.37x |
0.60 |
-0.16x |
+0.19x |
+0.41x |
+0.68x |
0.50 |
-0.22x |
+0.27x |
+0.62x |
+1.07x |
0.30 |
-0.32x |
+0.46x |
+1.14x |
+2.14x |
Negative values and delays under 50 ms are processed immediately. The rxdelay 0.5 column shows the inverted behaviour: stronger packets can receive a positive calculated delay, while weaker packets calculate negative delay and process immediately.
Example with 1000 ms packet airtime and packet score 0.50:
| rxdelay | Approx result |
|---|---|
0 |
disabled, immediate |
0.5 |
inverted curve, immediate |
1 |
no-op, immediate |
2 |
about 275 ms delay |
4 |
about 625 ms delay |
8 |
about 1070 ms delay |
txdelay is different from rxdelay.
txdelay is applied when a repeater is going to retransmit a flood packet. It adds a random transmit delay so that several nearby repeaters do not all retransmit at exactly the same time.
The repeater firmware calculates approximately:
base_time_ms = packet_airtime_ms * txdelay
random_delay_ms = random value from 0 to 5 * base_time_ms
So:
random_delay_ms ~= random(0, 5 * packet_airtime_ms * txdelay)
This is a random collision-avoidance window, not a fixed delay.
Higher txdelay means:
Lower txdelay means:
txdelay 0 disables this random window.
direct.txdelay uses the same calculation as txdelay, but for direct/routed retransmits instead of flood retransmits.
Direct traffic is usually aimed at a specific next hop, so fewer repeaters should be competing to retransmit it. That is why direct.txdelay is usually lower than txdelay.
Approximate random window for a packet with 1000 ms airtime:
| Setting | Random transmit delay window | Approx average delay |
|---|---|---|
txdelay 0 |
0 ms | 0 ms |
txdelay 0.2 |
0 to 1000 ms | 500 ms |
txdelay 0.3 |
0 to 1500 ms | 750 ms |
txdelay 0.5 |
0 to 2500 ms | 1250 ms |
txdelay 0.8 |
0 to 4000 ms | 2000 ms |
txdelay 1.0 |
0 to 5000 ms | 2500 ms |
The real airtime depends on spreading factor, bandwidth, coding rate, path length, and payload length.
They happen at different points:
packet received
-> rxdelay may delay processing of weak flood packets
-> repeater decides whether to forward
-> txdelay may add a random delay before retransmit
-> packet is transmitted
Use rxdelay to bias processing order between strong and weak received copies.
Use txdelay to spread retransmit timing between repeaters that are going to forward.
A node can use both, but increasing both also increases end-to-end latency.
These are starting points, not hard rules.
| Repeater type | rxdelay | txdelay | direct.txdelay | Notes |
|---|---|---|---|---|
| Mountaintop / Core | 2 to 4 |
0.6 to 0.8 |
0.25 to 0.3 |
Use only if duplicate weak-path forwarding is observed |
| Coverage / Distribution | 0 to 2 |
0.5 to 0.6 |
0.2 to 0.3 |
Conservative values are usually enough |
| Small infill / Edge | 0 |
0.3 to 0.5 |
0.2 |
Often the only local copy; avoid delaying receive processing |
Only try rxdelay 8 after observing a real duplicate-forwarding problem on a high site where stronger alternate paths are available.
Use the CLI to check the configured values:
get rxdelay
get txdelay
get direct.txdelay
Useful operational checks:
stats-core
stats-radio
If changing values for field testing, record before and after behaviour:
rxdelay values between 0 and 1.rxdelay is better.rxdelay on edge/infill repeaters unless there is a specific reason.txdelay gradually if many nearby repeaters collide while forwarding the same flood packet.