Creiamo un gateway tra Wi-Fi e LoRa per UDP

Da bambino avevo un sogno: fornire a ogni dispositivo domestico 'senza Wi-Fi' un biglietto per la rete, ovvero un indirizzo IP e una porta. Dopo un po', ho capito che non era il caso di rimandare. Dovevo semplicemente farlo.
Specifiche tecniche
Creare un gateway M5Stack con il modulo LoRa installato (figura 1). Il gateway si connetterà a una rete Wi-Fi, da cui riceverà un indirizzo IP locale tramite DHCP. A intervalli regolari, il gateway trasmetterà nel "LoRa-space" il suo nome (simile all'SSID per il Wi-Fi) e l'intervallo di porte disponibili, affinché altri dispositivi possano sapere che esiste una rete a cui possono connettersi e in quale intervallo poter scegliere una porta libera. Poiché si tratta di un prototipo, in questa fase l'autenticazione non è prevista. Nuovi dispositivi-clienti troveranno la rete LoRa disponibile e le invieranno la porta selezionata. Dopo che il gateway ha ricevuto dal nuovo cliente la porta, verifica se è libera; se sì, registra il nuovo cliente e inizia ad ascoltare su quella porta nel proprio server UDP asincrono. Dopo la registrazione, il cliente riceverà un'approvazione o un rifiuto per l'uso della porta richiesta. Il funzionamento è riportato nella tabella 1.

Figura 1
Tabella 1
lato
direzione e dati
lato
sessione
[ cliente ]
<— segnale-faro —
[ gateway ]
0xA1
[ cliente ]
— porta selezionata —>
[ gateway ]
0xB1
[ cliente ]
<— approvazione o rifiuto —
[ gateway ]
0xA2
[ cliente ]
— pacchetto UDP —>
[ gateway ]
0xB2
[ cliente ]
<— pacchetto UDP —
[ gateway ]
0xA3
[ rete ]
<— pacchetto UDP —
[ gateway ]
0xC1
Sulla mia scrivania ci sono vari moduli per M5Stack che si annoiano. Prendiamo il LoRa e divertiamoci un po' con esso. La concezione dei moduli è fantastica! Cosa si può dire? Tuttavia, i moduli che ho sono della prima revisione, i quali hanno un'orrenda antenna integrata, realizzata con una scheda a circuito flessibile e attaccata al lato del rivestimento. Una volta ho effettuato dei test sul campo con questi moduli (puoi guardare sul canale YouTube in lingua russa):

Naturalmente, ho dovuto rimuovere questi rudimenti e saldare antenne standard a spirale, che vengono fornite con il Ra-01. Dopo questa personalizzazione, la portata della comunicazione è notevolmente migliorata, ma è emerso un inconveniente: l'antenna ha un diametro maggiore della distanza permessa tra i moduli. Ho dovuto rinunciare al modulo finale durante il progetto.
Le prime difficoltà della rigidità sincronizzata
A prima vista, basta prendere la libreria WiFiUdp.h, dove c'è tutto il necessario per un esistente server UDP, ma non c'è. La libreria è progettata per l'avvio di un server sincrono, che, a grande rammarico, non può gestire più connessioni in un solo thread. Questa libreria non è adatta per l'operazione attuale. Ho dovuto sorseggiare molte tazze di tè e cercare una libreria che consentisse di avviare un server UDP asincrono capace di supportare molte connessioni contemporaneamente. È stata trovata una tale libreria — AsyncUDP.h. Qual è la differenza tra un server sincrono e uno asincrono? Esaminiamo sei episodi nella figura 2, dove sono mostrati in modo triviale i vari modi in cui funzionano i socket.

Figura 2
Con i seguenti attori:
Uomo nel ruolo di Socket;
Piccione nel ruolo di Connessione;
Lettera nel ruolo di Dati.
Episodio A. Socket sincrono senza timeout
L'Uomo rimarrà in attesa fino a quando il Piccione non gli porterà la Lettera.
Episodio B. Socket sincrono con timeout
L'Uomo attende il tempo stabilito con il Piccione e, se questo non arriva in tempo, l'Uomo se ne andrà.
Episodio C. Socket sincrono con multithreading
L'Uomo si rilassa e osserva come i Piccioni consegnano da soli le Lettere.
Episodio D. Socket asincrono (quando non c'è nulla da ricevere)
Una persona si dedica alle proprie attività preferite, ma non dimentica i Piccioni.
Episodio E. Socket asincrono (quando c'è qualcosa da ricevere)
Una persona si è distratta brevemente dalle sue faccende per ricevere una lettera dal Piccione.
Episodio F. Socket asincrono con multithreading
Una persona si occupa delle proprie attività e osserva come i Piccioni consegnano da soli le Lettere.
Se siete stati attenti, avrete sicuramente notato che i collari dei Piccioni in ogni episodio hanno un certo colore. E questo non è casuale. Negli episodi A e B funziona solo un socket sul server. Nell'episodio C funzionano già due socket. Negli episodi D, E e F ce ne sono tre. "Perché ci sono due là e qui tre?" - chiederete voi. Questi sono convenzionalmente 2 e 3, in realtà 20 potrebbe sostituire 2 e 200 sostituire tre. L'obiettivo è mostrare che i socket asincroni non sovraccaricano l'hardware tanto quanto quelli sincroni.
Quanto spazio c'è per cosa?
Esaminiamo la Tabella 1, che mostra la struttura del pacchetto UDP, e pensiamo a cosa si può fare con essa.
Tabella 1. Struttura del pacchetto UDP
Bit
0 — 15
16 — 31
0-31
Porta mittente (Source port)
Porta destinatario (Destination port)
32-63
Lunghezza del datagramma (Length)
Checksum
64-…
Dati
Aggiungiamo un altro campo all’inizio di questa tabella Sessione (1 Byte). Questo è sufficiente per questo progetto. Basandosi sulla Sessione, il dispositivo saprà come gestire ulteriormente il pacchetto. Adesso inventiamo i codici per le sessioni e li registriamo nella Tabella 2.
Tabella 2. Spiegazione delle sessioni
Codice
Nome
Spiegazione
0xA1
Beacon
Il gateway trasmette il nome della rete LoRa e l’intervallo di porte consentite a determinate scadenze. Questo è necessario affinché i nuovi clienti possano vedere la rete disponibile e gli attuali clienti possano determinare il livello del segnale quando non ci sono trasmissioni.
0xB1
Richiesta
Quando il cliente scopre la rete, invia la porta preferita.
0xA2
Consenso o rifiuto
Se la porta richiesta dal cliente è libera, il server risponde con consenso, altrimenti con rifiuto.
0xB2
Up-link
Quando il cliente invia un pacchetto UDP al gateway.
0xA3
Down-link
Quando il gateway invia un pacchetto UDP al cliente.
0xC1
Continuazione dell’Up-link
Quando il gateway invia un pacchetto UDP nella rete locale.
Bene. Ora discutiamo la composizione delle sessioni nella Tabella 3.
Tabella 3. Sessioni
Nome della sessione
Composizione
Beacon
Codice della sessione (1 Byte) + Nome della rete LoRa (4 Byte) + Porta iniziale (2 Byte) + Porta finale (2 Byte)
Richiesta
Codice di trasmissione (1 Byte) + Nome della rete LoRa (4 Byte) + Porta preferita (2 Byte)
Consenso o rifiuto
Codice di trasmissione (1 Byte) + Nome della rete LoRa (4 Byte) + Porta preferita (2 Byte) + Risultato (1 Byte)
Up-link
Codice di trasmissione (1 Byte) + Nome della rete LoRa (4 Byte) + Indirizzo IP remoto (4 Byte) + Porta remota (2 Byte) + Indirizzo IP locale (4 Byte) + Porta locale (2 Byte) + Dimensione dei dati (2 Byte) + Dati
Down-link
Codice di trasmissione (1 Byte) + Nome della rete LoRa (4 Byte) + Indirizzo IP remoto (4 Byte) + Porta remota (2 Byte) + Indirizzo IP locale (4 Byte) + Porta locale (2 Byte) + Dimensione dei dati (2 Byte) + Dati
Continuazione dell’Up-link
Indirizzo IP remoto (4 Byte) + Porta remota (2 Byte) + Dimensione dei dati (2 Byte) + Dati
Ho scritto due client per Arduino e per M5Stack. Puoi vedere come funziona. All'interno dell'appartamento non ci sono problemi, i test sul campo non sono stati ancora effettuati.
Il codice sorgente è disponibile su GitHub a
Puoi scoprire di più sul dispositivo M5Stack Base e acquistarlo
Puoi scegliere i moduli wireless LoRa per il dispositivo Base
Sarei felice se questo progetto ti fosse utile. Grazie mille per il tempo dedicato!
Elenco di letteratura e (o) fonti:
Fonte: habr.com
