Supporting UART Boards - 2nd Part #47

Closed
opened 2026-10-27 09:30:07 +01:00 by adam · 0 comments
Owner

An improvement suggest by ChatGpt

<html>

Great — I’ll explain how to build a better UART driver layer that makes the Waveshare UART module behave much closer to a real SX1262 radio, so it works more reliably with pyMC_Repeater.

The goal is to solve the main problems:

  • module-added headers

  • missing RSSI

  • buffering latency

  • packet collisions

We do this by adding a shim layer between pyMC and the UART module.


Architecture of the improved driver

Instead of letting pyMC talk directly to the UART module:

pyMC
↓
UART driver
↓
LoRa module firmware

we insert a radio shim:

pyMC
↓
LoRa shim layer
↓
UART driver
↓
module firmware
↓
SX1262

The shim layer handles:

  • packet normalization

  • RSSI estimation

  • buffering

  • collision mitigation


1️⃣ Packet normalization (fix the hidden headers)

Many UART LoRa modules prepend data like:

[ADDR_H][ADDR_L][CHANNEL][PAYLOAD...]

But pyMC expects:

[PAYLOAD]

So we strip the header.

Example function:

def normalize_packet(data):

if len(data) > 3:
payload = data[3:]
else:
payload = data

return payload

Then in the RX loop:

payload = normalize_packet(frame)
callback(payload)

This prevents mesh packet parsing failures.


2️⃣ RSSI estimation

Many UART modules don’t provide RSSI.

But you can estimate link quality using:

  • packet success rate

  • packet timing

  • retransmissions

Example:

class LinkQuality:

def __init__(self):
self.success = 0
self.fail = 0

def update(self, ok):

if ok:
self.success += 1
else:
self.fail += 1

def get_rssi_estimate(self):

total = self.success + self.fail

if total == 0:
return -120

ratio = self.success / total

return int(-120 + ratio * 60)

This produces an approximate RSSI range:

-120 dBm (bad)
-60 dBm (excellent)

Enough for routing decisions.


3️⃣ Anti-collision buffer

UART modules often transmit immediately when data arrives.

That causes collisions in mesh networks.

Add a randomized transmit delay:

import random
import time

def send_with_backoff(send_func, payload):

delay = random.uniform(0.02, 0.15)

time.sleep(delay)

send_func(payload)

This dramatically reduces collisions when several nodes repeat packets.


4️⃣ Packet queue (important)

Instead of sending directly:

pyMC → UART

use a queue system.

Example:

import queue
import threading

tx_queue = queue.Queue()


def tx_worker():

while True:

pkt = tx_queue.get()

send_with_backoff(uart_send, pkt)

Start it:

threading.Thread(target=tx_worker, daemon=True).start()

This improves reliability when bursts occur.


5️⃣ Maximum payload protection

UART modules often limit payload size.

Add protection:

MAX_LORA_PAYLOAD = 200


def safe_send(data):

if len(data) > MAX_LORA_PAYLOAD:

chunks = [
data[i:i+MAX_LORA_PAYLOAD]
for i in range(0, len(data), MAX_LORA_PAYLOAD)
]

for c in chunks:
tx_queue.put(c)

else:
tx_queue.put(data)

This prevents silent packet truncation.


6️⃣ Latency reduction trick

UART firmware buffering can add 10–80 ms delay.

You can reduce perceived latency by polling faster:

time.sleep(0.002)

instead of:

0.01

in the RX loop.


7️⃣ Final improved pipeline

With these improvements the system behaves like:

pyMC
↓
mesh shim layer
↓
packet queue
↓
collision backoff
↓
UART driver
↓
LoRa module

This significantly improves mesh stability.

Typical improvements in small LoRa networks:

metric | improvement -- | -- packet delivery | 2–4× collision rate | −60% mesh stability | much higher

⚠️ One more important limitation

Even with improvements, UART LoRa modules still have a limitation:

they cannot expose the full SX1262 PHY layer.

That means you cannot control:

  • spreading factor

  • coding rate

  • bandwidth

  • precise timing

which are normally controlled over SPI.

That’s why most mesh systems (Meshtastic, pyMC, etc.) prefer SPI radios.

</html>
An improvement suggest by ChatGpt <html> <body> <!--StartFragment--><p data-start="0" data-end="218">Great — I’ll explain how to build a <strong data-start="36" data-end="64">better UART driver layer</strong> that makes the Waveshare UART module behave much closer to a <strong data-start="126" data-end="147">real SX1262 radio</strong>, so it works more reliably with <span class="hover:entity-accent entity-underline inline cursor-pointer align-baseline"><span class="whitespace-normal">pyMC_Repeater</span></span>.</p> <p data-start="220" data-end="259">The goal is to solve the main problems:</p> <ul data-start="261" data-end="346"> <li data-section-id="e7kr3n" data-start="261" data-end="285"> <p data-start="263" data-end="285">module-added headers</p> </li> <li data-section-id="wnp6c7" data-start="286" data-end="302"> <p data-start="288" data-end="302">missing RSSI</p> </li> <li data-section-id="1e7wf9c" data-start="303" data-end="324"> <p data-start="305" data-end="324">buffering latency</p> </li> <li data-section-id="1slp299" data-start="325" data-end="346"> <p data-start="327" data-end="346">packet collisions</p> </li> </ul> <p data-start="348" data-end="419">We do this by adding a <strong data-start="371" data-end="385">shim layer</strong> between pyMC and the UART module.</p> <hr data-start="421" data-end="424"> <h1 data-section-id="1rpw4qr" data-start="426" data-end="463">Architecture of the improved driver</h1> <p data-start="465" data-end="522">Instead of letting pyMC talk directly to the UART module:</p> <pre class="overflow-visible! px-0!" data-start="524" data-end="575"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>pyMC</span><br><span> ↓</span><br><span>UART driver</span><br><span> ↓</span><br><span>LoRa module firmware</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="577" data-end="604">we insert a <strong data-start="589" data-end="603">radio shim</strong>:</p> <pre class="overflow-visible! px-0!" data-start="606" data-end="681"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>pyMC</span><br><span> ↓</span><br><span>LoRa shim layer</span><br><span> ↓</span><br><span>UART driver</span><br><span> ↓</span><br><span>module firmware</span><br><span> ↓</span><br><span>SX1262</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="683" data-end="706">The shim layer handles:</p> <ul data-start="708" data-end="791"> <li data-section-id="aizrx9" data-start="708" data-end="732"> <p data-start="710" data-end="732">packet normalization</p> </li> <li data-section-id="stka94" data-start="733" data-end="752"> <p data-start="735" data-end="752">RSSI estimation</p> </li> <li data-section-id="lriig8" data-start="753" data-end="766"> <p data-start="755" data-end="766">buffering</p> </li> <li data-section-id="r9ekqd" data-start="767" data-end="791"> <p data-start="769" data-end="791">collision mitigation</p> </li> </ul> <hr data-start="793" data-end="796"> <h1 data-section-id="1bi8o1z" data-start="798" data-end="849">1️⃣ Packet normalization (fix the hidden headers)</h1> <p data-start="851" data-end="892">Many UART LoRa modules prepend data like:</p> <pre class="overflow-visible! px-0!" data-start="894" data-end="939"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>[ADDR_H][ADDR_L][CHANNEL][PAYLOAD...]</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="941" data-end="958">But pyMC expects:</p> <pre class="overflow-visible! px-0!" data-start="960" data-end="977"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>[PAYLOAD]</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="979" data-end="1002">So we strip the header.</p> <p data-start="1004" data-end="1021">Example function:</p> <pre class="overflow-visible! px-0!" data-start="1023" data-end="1167"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼ8">def</span><span> </span><span class="ͼe">normalize_packet</span><span>(</span><span class="ͼe">data</span><span>):</span><br><br><span> </span><span class="ͼ8">if</span><span> </span><span class="ͼe">len</span><span>(</span><span class="ͼe">data</span><span>) </span><span class="ͼ8">&gt;</span><span> </span><span class="ͼb">3</span><span>:</span><br><span> </span><span class="ͼe">payload</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">data</span><span>[</span><span class="ͼb">3</span><span>:]</span><br><span> </span><span class="ͼ8">else</span><span>:</span><br><span> </span><span class="ͼe">payload</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">data</span><br><br><span> </span><span class="ͼ8">return</span><span> </span><span class="ͼe">payload</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="1169" data-end="1189">Then in the RX loop:</p> <pre class="overflow-visible! px-0!" data-start="1191" data-end="1256"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼe">payload</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">normalize_packet</span><span>(</span><span class="ͼe">frame</span><span>)</span><br><span class="ͼe">callback</span><span>(</span><span class="ͼe">payload</span><span>)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="1258" data-end="1305">This prevents <strong data-start="1272" data-end="1304">mesh packet parsing failures</strong>.</p> <hr data-start="1307" data-end="1310"> <h1 data-section-id="djn7g8" data-start="1312" data-end="1333">2️⃣ RSSI estimation</h1> <p data-start="1335" data-end="1372">Many UART modules don’t provide RSSI.</p> <p data-start="1374" data-end="1418">But you can estimate <strong data-start="1395" data-end="1411">link quality</strong> using:</p> <ul data-start="1420" data-end="1479"> <li data-section-id="1gz0eo1" data-start="1420" data-end="1443"> <p data-start="1422" data-end="1443">packet success rate</p> </li> <li data-section-id="1co0a8" data-start="1444" data-end="1461"> <p data-start="1446" data-end="1461">packet timing</p> </li> <li data-section-id="ea5rfe" data-start="1462" data-end="1479"> <p data-start="1464" data-end="1479">retransmissions</p> </li> </ul> <p data-start="1481" data-end="1489">Example:</p> <pre class="overflow-visible! px-0!" data-start="1491" data-end="1910"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼ8">class</span><span> </span><span class="ͼd">LinkQuality</span><span>:</span><br><br><span> </span><span class="ͼ8">def</span><span> </span><span class="ͼe">__init__</span><span>(</span><span class="ͼe">self</span><span>):</span><br><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>success </span><span class="ͼ8">=</span><span> </span><span class="ͼb">0</span><br><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>fail </span><span class="ͼ8">=</span><span> </span><span class="ͼb">0</span><br><br><span> </span><span class="ͼ8">def</span><span> </span><span class="ͼe">update</span><span>(</span><span class="ͼe">self</span><span>, </span><span class="ͼe">ok</span><span>):</span><br><br><span> </span><span class="ͼ8">if</span><span> </span><span class="ͼe">ok</span><span>:</span><br><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>success </span><span class="ͼ8">+=</span><span> </span><span class="ͼb">1</span><br><span> </span><span class="ͼ8">else</span><span>:</span><br><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>fail </span><span class="ͼ8">+=</span><span> </span><span class="ͼb">1</span><br><br><span> </span><span class="ͼ8">def</span><span> </span><span class="ͼe">get_rssi_estimate</span><span>(</span><span class="ͼe">self</span><span>):</span><br><br><span> </span><span class="ͼe">total</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>success </span><span class="ͼ8">+</span><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>fail</span><br><br><span> </span><span class="ͼ8">if</span><span> </span><span class="ͼe">total</span><span> </span><span class="ͼ8">==</span><span> </span><span class="ͼb">0</span><span>:</span><br><span> </span><span class="ͼ8">return</span><span> </span><span class="ͼ8">-</span><span class="ͼb">120</span><br><br><span> </span><span class="ͼe">ratio</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">self</span><span class="ͼ8">.</span><span>success </span><span class="ͼ8">/</span><span> </span><span class="ͼe">total</span><br><br><span> </span><span class="ͼ8">return</span><span> </span><span class="ͼe">int</span><span>(</span><span class="ͼ8">-</span><span class="ͼb">120</span><span> </span><span class="ͼ8">+</span><span> </span><span class="ͼe">ratio</span><span> </span><span class="ͼ8">*</span><span> </span><span class="ͼb">60</span><span>)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="1912" data-end="1952">This produces an approximate RSSI range:</p> <pre class="overflow-visible! px-0!" data-start="1954" data-end="1996"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>-120 dBm (bad)</span><br><span>-60 dBm (excellent)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="1998" data-end="2031">Enough for <strong data-start="2009" data-end="2030">routing decisions</strong>.</p> <hr data-start="2033" data-end="2036"> <h1 data-section-id="z7qsbc" data-start="2038" data-end="2065">3️⃣ Anti-collision buffer</h1> <p data-start="2067" data-end="2125">UART modules often transmit immediately when data arrives.</p> <p data-start="2127" data-end="2167">That causes collisions in mesh networks.</p> <p data-start="2169" data-end="2205">Add a <strong data-start="2175" data-end="2204">randomized transmit delay</strong>:</p> <pre class="overflow-visible! px-0!" data-start="2207" data-end="2377"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼ8">import</span><span> </span><span class="ͼe">random</span><br><span class="ͼ8">import</span><span> </span><span class="ͼe">time</span><br><br><span class="ͼ8">def</span><span> </span><span class="ͼe">send_with_backoff</span><span>(</span><span class="ͼe">send_func</span><span>, </span><span class="ͼe">payload</span><span>):</span><br><br><span> </span><span class="ͼe">delay</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">random</span><span class="ͼ8">.</span><span>uniform(</span><span class="ͼb">0.02</span><span>, </span><span class="ͼb">0.15</span><span>)</span><br><br><span> </span><span class="ͼe">time</span><span class="ͼ8">.</span><span>sleep(</span><span class="ͼe">delay</span><span>)</span><br><br><span> </span><span class="ͼe">send_func</span><span>(</span><span class="ͼe">payload</span><span>)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="2379" data-end="2450">This dramatically reduces collisions when several nodes repeat packets.</p> <hr data-start="2452" data-end="2455"> <h1 data-section-id="149boe8" data-start="2457" data-end="2487">4️⃣ Packet queue (important)</h1> <p data-start="2489" data-end="2517">Instead of sending directly:</p> <pre class="overflow-visible! px-0!" data-start="2519" data-end="2538"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>pyMC → UART</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="2540" data-end="2563">use a <strong data-start="2546" data-end="2562">queue system</strong>.</p> <p data-start="2565" data-end="2573">Example:</p> <pre class="overflow-visible! px-0!" data-start="2575" data-end="2753"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼ8">import</span><span> </span><span class="ͼe">queue</span><br><span class="ͼ8">import</span><span> </span><span class="ͼe">threading</span><br><br><span class="ͼe">tx_queue</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">queue</span><span class="ͼ8">.</span><span>Queue()</span><br><br><br><span class="ͼ8">def</span><span> </span><span class="ͼe">tx_worker</span><span>():</span><br><br><span> </span><span class="ͼ8">while</span><span> </span><span class="ͼb">True</span><span>:</span><br><br><span> </span><span class="ͼe">pkt</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼe">tx_queue</span><span class="ͼ8">.</span><span>get()</span><br><br><span> </span><span class="ͼe">send_with_backoff</span><span>(</span><span class="ͼe">uart_send</span><span>, </span><span class="ͼe">pkt</span><span>)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="2755" data-end="2764">Start it:</p> <pre class="overflow-visible! px-0!" data-start="2766" data-end="2835"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼe">threading</span><span class="ͼ8">.</span><span>Thread(</span><span class="ͼe">target</span><span class="ͼ8">=</span><span class="ͼe">tx_worker</span><span>, </span><span class="ͼe">daemon</span><span class="ͼ8">=</span><span class="ͼb">True</span><span>)</span><span class="ͼ8">.</span><span>start()</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="2837" data-end="2881">This improves reliability when bursts occur.</p> <hr data-start="2883" data-end="2886"> <h1 data-section-id="1l0qsu2" data-start="2888" data-end="2920">5️⃣ Maximum payload protection</h1> <p data-start="2922" data-end="2960">UART modules often limit payload size.</p> <p data-start="2962" data-end="2977">Add protection:</p> <pre class="overflow-visible! px-0!" data-start="2979" data-end="3296"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼe">MAX_LORA_PAYLOAD</span><span> </span><span class="ͼ8">=</span><span> </span><span class="ͼb">200</span><br><br><br><span class="ͼ8">def</span><span> </span><span class="ͼe">safe_send</span><span>(</span><span class="ͼe">data</span><span>):</span><br><br><span> </span><span class="ͼ8">if</span><span> </span><span class="ͼe">len</span><span>(</span><span class="ͼe">data</span><span>) </span><span class="ͼ8">&gt;</span><span> </span><span class="ͼe">MAX_LORA_PAYLOAD</span><span>:</span><br><br><span> </span><span class="ͼe">chunks</span><span> </span><span class="ͼ8">=</span><span> [</span><br><span> </span><span class="ͼe">data</span><span>[</span><span class="ͼe">i</span><span>:</span><span class="ͼe">i</span><span class="ͼ8">+</span><span class="ͼe">MAX_LORA_PAYLOAD</span><span>]</span><br><span> </span><span class="ͼ8">for</span><span> </span><span class="ͼe">i</span><span> </span><span class="ͼ8">in</span><span> </span><span class="ͼe">range</span><span>(</span><span class="ͼb">0</span><span>, </span><span class="ͼe">len</span><span>(</span><span class="ͼe">data</span><span>), </span><span class="ͼe">MAX_LORA_PAYLOAD</span><span>)</span><br><span> ]</span><br><br><span> </span><span class="ͼ8">for</span><span> </span><span class="ͼe">c</span><span> </span><span class="ͼ8">in</span><span> </span><span class="ͼe">chunks</span><span>:</span><br><span> </span><span class="ͼe">tx_queue</span><span class="ͼ8">.</span><span>put(</span><span class="ͼe">c</span><span>)</span><br><br><span> </span><span class="ͼ8">else</span><span>:</span><br><span> </span><span class="ͼe">tx_queue</span><span class="ͼ8">.</span><span>put(</span><span class="ͼe">data</span><span>)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="3298" data-end="3337">This prevents silent packet truncation.</p> <hr data-start="3339" data-end="3342"> <h1 data-section-id="c1j6dm" data-start="3344" data-end="3373">6️⃣ Latency reduction trick</h1> <p data-start="3375" data-end="3426">UART firmware buffering can add <strong data-start="3407" data-end="3425">10–80 ms delay</strong>.</p> <p data-start="3428" data-end="3479">You can reduce perceived latency by polling faster:</p> <pre class="overflow-visible! px-0!" data-start="3481" data-end="3512"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light"></div></div></div><div class=""><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span class="ͼe">time</span><span class="ͼ8">.</span><span>sleep(</span><span class="ͼb">0.002</span><span>)</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="3514" data-end="3525">instead of:</p> <pre class="overflow-visible! px-0!" data-start="3527" data-end="3539"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>0.01</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="3541" data-end="3556">in the RX loop.</p> <hr data-start="3558" data-end="3561"> <h1 data-section-id="1k11k3x" data-start="3563" data-end="3592">7️⃣ Final improved pipeline</h1> <p data-start="3594" data-end="3642">With these improvements the system behaves like:</p> <pre class="overflow-visible! px-0!" data-start="3644" data-end="3742"><div class="relative w-full mt-4 mb-1"><div class=""><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl"><div class="h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback"><div class="pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1"></div><div class="pt-3"><div class="relative z-0 flex max-w-full"><div id="code-block-viewer" dir="ltr" class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj"><div class="cm-scroller"><div class="cm-content q9tKkq_readonly"><span>pyMC</span><br><span> ↓</span><br><span>mesh shim layer</span><br><span> ↓</span><br><span>packet queue</span><br><span> ↓</span><br><span>collision backoff</span><br><span> ↓</span><br><span>UART driver</span><br><span> ↓</span><br><span>LoRa module</span></div></div></div></div></div></div></div></div></div><div class=""><div class=""></div></div></div></div></div></pre> <p data-start="3744" data-end="3787">This significantly improves mesh stability.</p> <p data-start="3789" data-end="3833">Typical improvements in small LoRa networks:</p> <div class="TyagGW_tableContainer"><div tabindex="-1" class="group TyagGW_tableWrapper flex flex-col-reverse w-fit"> metric | improvement -- | -- packet delivery | 2–4× collision rate | −60% mesh stability | much higher </div></div> <hr data-start="3957" data-end="3960"> <h1 data-section-id="17xe1bq" data-start="3962" data-end="3996">⚠️ One more important limitation</h1> <p data-start="3998" data-end="4064">Even with improvements, UART LoRa modules still have a limitation:</p> <p data-start="4066" data-end="4115">they <strong data-start="4071" data-end="4114">cannot expose the full SX1262 PHY layer</strong>.</p> <p data-start="4117" data-end="4147">That means you cannot control:</p> <ul data-start="4149" data-end="4218"> <li data-section-id="m8p7w4" data-start="4149" data-end="4169"> <p data-start="4151" data-end="4169">spreading factor</p> </li> <li data-section-id="yp434y" data-start="4170" data-end="4185"> <p data-start="4172" data-end="4185">coding rate</p> </li> <li data-section-id="avsop3" data-start="4186" data-end="4199"> <p data-start="4188" data-end="4199">bandwidth</p> </li> <li data-section-id="1uewyhv" data-start="4200" data-end="4218"> <p data-start="4202" data-end="4218">precise timing</p> </li> </ul> <p data-start="4220" data-end="4259">which are normally controlled over SPI.</p> <p data-start="4261" data-end="4337">That’s why most mesh systems (Meshtastic, pyMC, etc.) prefer <strong data-start="4322" data-end="4336">SPI radios</strong>.</p> <!--EndFragment--> </body> </html> <!-- github-sync-id:4036489804 -->
adam closed this issue 2026-10-27 09:30:07 +01:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: adam/pyMC_Repeater#47