Da dove partire tra le 470 risorse di awesome-meshcore
L'elenco ha superato le 470 voci. Abbiamo scelto quelle che servono davvero, divise per quello che vuoi fare: usare un nodo, gestire un repeater, pianificare una tratta o scrivere codice.
Blog
Quando si parla di limiti si guarda sempre il 10% di duty cycle del singolo nodo. Ma tutta la mesh trasmette sulla stessa frequenza, e il limite che arriva prima è un altro: il tempo d'aria condiviso.
La normativa ci dà il 10% di duty cycle: al massimo 6 minuti di trasmissione all'ora per dispositivo. È il numero che tutti controllano, ed è giusto farlo.
Però c'è un dettaglio che il limite per nodo non racconta. Tutti i nodi italiani usano lo stesso preset: 869.618 MHz, 62,5 kHz, SF8. Non ci sono canali alternativi su cui spostarsi. Quando un nodo trasmette, tutti quelli che lo sentono devono stare zitti o rischiare una collisione. Il canale è uno solo, e il suo tempo si divide fra tutti.
Abbiamo aperto il codice del firmware (tag repeater-v1.17.1) per fare i
conti: quanto pesa ogni pacchetto, quante copie ne girano e quando due
trasmissioni si pestano i piedi.
Il tempo in aria si calcola con la formula del datasheet SX1262, la stessa che usiamo nella pagina sul link budget. Con il nostro preset un simbolo dura 4,1 ms. Il firmware usa un preambolo di 32 simboli quando SF è 8 o meno: prima ancora del primo byte utile passano 148 ms.
Poi servono le dimensioni dei pacchetti, e quelle si leggono nel codice:
IT-Torino-RPT-01 con la posizione impostata
arriva a circa 127 byte, e ogni hop aggiunge un byte al percorso;nome: testo, arrotondato a blocchi di
16 byte. Un messaggio di 40 caratteri sta intorno ai 70 byte;| Pacchetto | Byte (circa) | Tempo in aria |
|---|---|---|
| ACK | 6 | 0,25 s |
| Messaggio di canale, 40 caratteri | 71 | 0,80 s |
| Advert di un repeater, dopo 2 hop | 129 | 1,26 s |
| Pacchetto più lungo possibile | 255 | 2,31 s |
L'advert è il pacchetto più pesante fra quelli di routine: più di un secondo e un quarto, quasi tutto per la firma e la chiave. Sono i byte che rendono l'identità di un nodo verificabile senza un server, e non si possono togliere.
Advert e messaggi di canale viaggiano in flood. Ogni repeater che riceve un pacchetto flood lo ritrasmette una volta sola: il firmware tiene una tabella dei pacchetti già visti e scarta i doppioni. È quello che impedisce alla rete di andare in loop.
Una volta sola per repeater, però, vuol dire che un singolo messaggio costa alla rete tante trasmissioni quanti sono i repeater che raggiunge. E chi sta in ascolto in un punto qualunque riceve una copia da ogni repeater che sente.
Da qui la distinzione che conta:
Il primo numero può essere basso mentre il secondo è alto. Un repeater che ritrasmette 30 messaggi di canale all'ora usa lo 0,7% del suo 10%. Se però sente altri 19 repeater che fanno la stessa cosa, il canale intorno a lui è occupato per il 13%. E nessuno ha violato niente.
Proviamo con un modello semplice. In una zona ci sono N repeater
collegati fra loro, e chi ascolta ne sente k direttamente. Ogni repeater
manda un advert flood ogni T ore. Gli advert dei repeater non vengono
ripetuti oltre 8 hop (flood.max.advert, default 8), quindi in una regione
grande N è il numero di repeater entro 8 hop, non quello di tutta Italia.
Ogni advert arriva in k copie, ognuna da 1,26 secondi:
| Repeater in zona (N) | Ne sento (k) | Advert ogni 12 h | Advert ogni 47 h |
|---|---|---|---|
| 50 | 5 | 0,7% | 0,2% |
| 150 | 8 | 3,5% | 0,9% |
| 150 | 20 | 8,8% | 2,2% |
Perché due colonne? Fino alla versione 1.15 il default di
flood.advert.interval era di 12 ore. Dalla 1.16.0 un repeater appena
installato manda l'advert flood ogni 47 ore: in pratica un quarto del
traffico di advert. Il default vale solo per le installazioni nuove o
resettate. Un repeater aggiornato conserva le impostazioni che aveva, quindi
molti nodi in giro mandano ancora l'advert ogni 12 ore.
L'ultima riga è il caso del repeater in cima a una collina, che sente 20 vicini. Con il vecchio intervallo spende quasi un decimo dell'aria solo per ascoltare gli advert altrui.
Gli advert si possono diradare. I messaggi di canale no: li manda la gente, quando ha qualcosa da dire. Un messaggio di canale va in flood fino a 64 hop, quindi attraversa tutta la parte di rete collegata, salvo che il canale sia limitato a una regione (lo vediamo più avanti).
Con messaggi di 40 caratteri, ognuno da 0,8 secondi:
| Messaggi all'ora sul canale | Repeater che sento (k) | Canale occupato |
|---|---|---|
| 30 | 8 | 5,4% |
| 30 | 20 | 13,4% |
| 60 | 20 | 26,8% |
Un messaggio ogni due minuti su un canale pubblico regionale non è tanto. Una serata vivace in chat lo supera facilmente. Ed è qui che la differenza fra un messaggio diretto e uno di canale diventa concreta. Come spieghiamo nella pagina sull'app, il primo messaggio diretto passa in flood, ma poi il percorso viene imparato e i successivi passano solo per i repeater che servono. Un canale, invece, paga il flood ogni volta.
Prima di trasmettere il firmware controlla se il radio sta ricevendo qualcosa. Se sì, aspetta qualche centinaio di millisecondi e riprova. È un "ascolta prima di parlare" e funziona bene fra nodi che si sentono fra loro: con 148 ms di preambolo c'è tutto il tempo per accorgersi che qualcuno ha cominciato.
Non funziona fra nodi che non si sentono. Due repeater in due valli diverse possono trasmettere nello stesso istante, convinti di avere il canale libero. Il repeater sulla collina in mezzo li sente entrambi e riceve solo rumore. È il classico problema del nodo nascosto.
Per chi non si sente vale il modello ALOHA: un pacchetto arriva intatto con probabilità di circa e^(−2G), dove G è il carico dei trasmettitori nascosti. Se l'aria è occupata per il 10% da nodi che non si sentono fra loro, circa 18 pacchetti su 100 si perdono. Al 20%, uno su tre.
C'è un'attenuante: se uno dei due segnali è più forte di qualche dB, il ricevitore LoRa spesso riesce a decodificarlo lo stesso (effetto cattura). E in una mesh lo stesso pacchetto arriva da più strade. Il conto resta pessimista, ma la direzione è chiara.
Ecco il paradosso: il repeater migliore è quello che soffre di più. Più è alto, più vicini sente; più vicini sente, più sono quelli che non si sentono fra loro. Le collisioni si concentrano proprio nei nodi da cui dipende la copertura di tutti.
C'è un momento in cui le collisioni sono quasi certe: subito dopo una trasmissione. Un repeater manda un pacchetto, e tutti i suoi vicini lo ricevono nello stesso istante. Se lo ritrasmettessero subito, partirebbero tutti insieme.
Per questo, prima di ritrasmettere un flood, ogni repeater aspetta un tempo
casuale. Nel codice la finestra va da zero a 5 × txdelay × tempo in aria
del pacchetto. Con il default txdelay 0,5, la finestra è di 2,5 volte la
durata del pacchetto. A parità di ritardo, inoltre, passano prima i
pacchetti che hanno fatto meno hop.
Fra vicini che si sentono basta che uno parta per primo: gli altri lo sentono e aspettano. Fra vicini nascosti decide solo il caso. Per due repeater nascosti la probabilità che le loro trasmissioni si sovrappongano dipende dalla larghezza della finestra:
txdelay |
Finestra | Due nascosti si sovrappongono | Ritardo medio per hop (canale) |
|---|---|---|---|
| 0,5 (default) | 2,5 × pacchetto | 64% | 1,0 s |
| 1,0 | 5 × pacchetto | 36% | 2,0 s |
| 2,0 (massimo) | 10 × pacchetto | 19% | 4,0 s |
È uno scambio diretto: meno collisioni in cambio di più latenza, e la
latenza si somma a ogni hop. Su cinque hop, passare da 0,5 a 1,0 aggiunge
in media 5 secondi a un messaggio di canale. Per i messaggi diretti c'è un
parametro separato, direct.txdelay, con default 0,3.
Nessuna di queste è una regola della community: sono conseguenze dei numeri.
set flood.advert.interval 47. Un repeater fisso non cambia identità:
farsi riannunciare spesso non serve a nessuno.txdelay più alto sui repeater che sentono molto. Un nodo in collina
che sente tanti vicini nascosti è il candidato naturale. Alzalo con
cautela, e coordinati con chi gestisce i repeater vicini.stats-radio mostra il tempo
in aria e il noise floor del repeater, e stats-packets i contatori dei
pacchetti. Un repeater che passa molto tempo a ricevere ti sta dicendo
quanto è occupato il canale intorno a lui.Per chi vuole andare oltre, il firmware ha altre due leve, spente di
default: il rilevamento di attività in hardware prima di trasmettere
(set cad on) e una soglia di interferenza basata sull'RSSI
(set int.thresh). Non le abbiamo provate e non diamo valori consigliati:
chi le usa ci racconti com'è andata.
Il modello è volutamente semplice. Considera i repeater distribuiti in modo uniforme e un traffico costante. Non conta i tentativi ripetuti dopo un messaggio non confermato, i pacchetti di percorso e gli ACK. Tratta le collisioni come se fossero tutte distruttive, ignorando l'effetto cattura. Il traffico vero arriva a raffiche: i picchi contano più della media oraria.
Gli ordini di grandezza però reggono. Per la mesh italiana, il primo collo di bottiglia non è il 10% del singolo nodo: è il secondo d'aria condiviso da tutti quelli che si sentono. Gestire bene quel secondo, con meno advert, meno repeater inutili e più messaggi diretti, è qualcosa che possiamo fare subito, senza aspettare un nuovo firmware.
src/Mesh.cpp
e limite di 32 byte di dati in
src/MeshCore.h.txdelay 0,5, direct.txdelay 0,3,
flood.advert.interval 47, flood.max.advert 8) e finestra del ritardo
casuale:
examples/simple_repeater/MyMesh.cpp.src/Dispatcher.cpp.src/helpers/radiolib/RadioLibWrappers.h.