Créons une passerelle entre le Wi-Fi et LoRa pour UDP

J'avais un rêve d'enfant : donner à chaque appareil « sans Wi-Fi » un ticket pour le réseau, c'est-à-dire une adresse IP et un port. Après un certain temps, j'ai compris qu'il ne fallait pas procrastiner. Il fallait juste se lancer et le faire.
Cahier des charges
Faire de M5Stack une passerelle avec le module LoRa installé (voir Figure 1). La passerelle sera connectée au réseau Wi-Fi, où elle obtiendra une adresse IP locale via DHCP. La passerelle diffusera à intervalles réguliers dans l'air LoRa son nom (analogue à SSID pour le Wi-Fi) et la plage de ports autorisés, afin que d'autres appareils sachent qu'il existe un tel réseau auquel se connecter et dans quelle plage un port libre peut être choisi. Étant donné que ce sera un prototype, l'authentification n'est pas prévue cette fois. Les nouveaux appareils clients trouveront le réseau LoRa disponible et lui transmettront le port choisi. Une fois que la passerelle a reçu le port du nouveau client, elle vérifie s'il est libre ; si c'est le cas, elle enregistre le nouveau client et commence à écouter le port sur son propre serveur UDP asynchrone. Après l'enregistrement, le client recevra une autorisation ou un refus d'utiliser le port demandé. Le fonctionnement est illustré dans le tableau 1.

Figure 1
Tableau 1
côté
direction et donnée
côté
session
[ client ]
<— signal-balise —
[ passerelle ]
0xA1
[ client ]
— port choisi —>
[ passerelle ]
0xB1
[ client ]
<— autorisation ou refus —
[ passerelle ]
0xA2
[ client ]
— paquet UPD —>
[ passerelle ]
0xB2
[ client ]
<— paquet UPD —
[ passerelle ]
0xA3
[ réseau ]
<— paquet UPD —
[ passerelle ]
0xC1
Devant moi sur la table se trouvent tous les modules pour M5Stack qui s'ennuient. Prenons le LoRa et amusons-nous avec. Le concept même des modules est magnifique ! Que dire ? Mais, les modules que j'ai sont de première révision, avec une antenne intégrée horrible, réalisée sur un circuit imprimé flexible et collée à la paroi latérale du boîtier. Une fois, j'ai fait des tests de terrain avec de tels modules (vous pouvez voir cela sur une chaîne YouTube russophone) :

Naturellement, j'ai dû retirer ces reliques et souder des antennes spirales standard fournies avec le Ra-01. Après une telle personnalisation, la portée de la communication s'est nettement améliorée, mais il y a eu un effet secondaire - l'antenne a un diamètre plus grand que la distance autorisée entre les modules. J'ai dû renoncer au module final pendant la durée du projet.
Les premières difficultés de la rigidité synchronisée
On pourrait croire qu'il suffit de prendre la bibliothèque WiFiUdp.h, où tout est prévu pour le fonctionnement d'un serveur UDP, ce n'est pas le cas. La bibliothèque est conçue pour mettre en œuvre un serveur synchronisé, qui, à notre grand regret, ne peut pas gérer plusieurs connexions en même temps dans un même thread. Cette bibliothèque n'est pas adaptée à la tâche actuelle. J'ai dû consommer beaucoup de tasses de thé et chercher une bibliothèque qui permettrait de monter un serveur UDP asynchrone capable de prendre en charge de nombreuses connexions simultanément. Une telle bibliothèque a été trouvée — AsyncUDP.h. Quelle est la différence entre un serveur synchronisé et un serveur asynchrone ? Examinons six épisodes dans la figure 2, où les différentes façons de travailler avec les sockets sont illustrées de manière triviale.

Figure 2
Avec des rôles principaux :
Homme dans le rôle de Socket;
Colombe dans le rôle de Connexion;
Lettre dans le rôle de Données.
Épisode A. Socket synchronisé sans timeout
L'Homme restera debout jusqu'à ce que la Colombe lui apporte la Lettre.
Épisode B. Socket synchronisé avec timeout
L'Homme attend le temps convenu avec la Colombe et, si celle-ci n'arrive pas à l'heure, il partira.
Épisode C. Socket synchronisé avec multithreading
L'Homme ne fait rien et observe comment les Colombes livrent les Lettres d'elles-mêmes.
Épisode D. Socket asynchrone (lorsqu'il n'y a rien à recevoir)
L'Homme se consacre à ses activités préférées, mais n'oublie pas les Colombes.
Épisode E. Socket asynchrone (lorsqu'il y a quelque chose à recevoir)
L'Homme s'est momentanément détourné de ses affaires pour recevoir une lettre de la Colombe.
Épisode F. Socket asynchrone avec multithreading
L'Homme s'occupe de ses affaires et observe comment les Colombes livrent les Lettres d'elles-mêmes.
Si vous avez été attentif, vous devriez avoir remarqué que les colliers sur les Colombes dans chaque épisode ont une couleur spécifique. Ce n'est pas par hasard. Dans les épisodes A et B, un seul socket fonctionne sur le serveur. Dans l'épisode C, il y a déjà deux sockets en fonctionnement. Dans les épisodes D, E et F, il y a déjà trois sockets. « Pourquoi y a-t-il deux là, et trois ici ? » — pourriez-vous demander. C'est théorique, en réalité au lieu de 2, cela pourrait être 20, et au lieu de trois, 200. Le but est de montrer que les sockets asynchrones n'affectent pas le matériel aussi intensément que les sockets synchrones.
Quelle est la capacité de chaque élément ?
Considérons le tableau 1, qui présente la structure d'un paquet UDP et réfléchissons à ce que l'on peut en faire.
Tableau 1. Structure d'un paquet UDP
Bits
0 — 15
16 — 31
0-31
Port de l'expéditeur (Source port)
Port du destinataire (Destination port)
32-63
Longueur de la datagramme (Length)
Checksum
64-…
Données (Data)
Ajoutons un champ supplémentaire au début de ce tableau. Session (1 Octet). Cela suffira pour ce projet. En fonction de la session, l'appareil saura quoi faire avec le paquet par la suite. Maintenant, inventons des codes pour les sessions et enregistrons-les dans le tableau 2.
Tableau 2. Explication des sessions
Code
Nom
Explication
0xA1
Balise
La passerelle envoie le nom du réseau LoRa et la plage de ports disponibles à intervalles réguliers. Cela est nécessaire afin que les nouveaux clients puissent voir le réseau disponible, et que les clients existants puissent déterminer le niveau du signal lorsqu'il n'y a pas de transmissions.
0xB1
Demande
Lorsque le client détecte le réseau, il envoie le port préféré.
0xA2
Acceptation ou refus
Si le port demandé par le client est libre, le serveur répond par l'acceptation, sinon il refuse.
0xB2
Up-link
Lorsque le client envoie un paquet UDP à la passerelle.
0xA3
Down-link
Lorsque la passerelle envoie un paquet UDP au client.
0xC1
Continuation de l'Up-link
Lorsque la passerelle envoie un paquet UDP dans le réseau local.
Bien. Maintenant, discutons de la composition des sessions dans le tableau 3.
Tableau 3. Sessions
Nom de la session
Composition
Balise
Code de la session (1 Octet) + Nom du réseau LoRa (4 Octets) + Port de départ (2 Octets) + Port de fin (2 Octets)
Demande
Code de transmission (1 Octet) + Nom du réseau LoRa (4 Octets) + Port préféré (2 Octets)
Acceptation ou refus
Code de transmission (1 Octet) + Nom du réseau LoRa (4 Octets) + Port préféré (2 Octets) + Résultat (1 Octet)
Up-link
Code de transmission (1 Octet) + Nom du réseau LoRa (4 Octets) + Adresse IP distante (4 Octets) + Port distant (2 Octets) + Adresse IP locale (4 Octets) + Port local (2 Octets) + Taille des données (2 Octets) + Données
Down-link
Code de transmission (1 Octet) + Nom du réseau LoRa (4 Octets) + Adresse IP distante (4 Octets) + Port distant (2 Octets) + Adresse IP locale (4 Octets) + Port local (2 Octets) + Taille des données (2 Octets) + Données
Continuation de l'Up-link
Adresse IP distante (4 Octets) + Port distant (2 Octets) + Taille des données (2 Octets) + Données
J'ai écrit deux clients pour Arduino et pour M5Stack. Sur vous pouvez voir comment cela fonctionne. Il n'y a pas de problèmes dans l'appartement, je n'ai pas encore effectué de tests sur le terrain.
Le code source est disponible sur GitHub à
Pour en savoir plus sur l'appareil de base M5Stack et pour l'acheter, vous pouvez
Choisir des modules sans fil LoRa pour l'appareil de base est possible
Je serais ravi que ce projet vous soit utile. Merci beaucoup pour votre temps!
Liste de la littérature et (ou) des sources:
Source : habr.com
