La bibliothèque Loramesher met en œuvre un protocole de routage à distance à distance pour communiquer les messages entre les nœuds LORA. Pour l'interaction avec la puce Radio Lora, nous tirons parti de Radiolib, une bibliothèque de communication polyvalente qui prend en charge différents modules de la série LORA.
En ce moment, Loramesher a été testé dans les modules suivants:
Vous pouvez demander un autre module à ajouter à la bibliothèque en ouvrant un problème.
Vous pouvez consulter library.json pour plus de détails. Fondamentalement, nous utilisons le radiolib qui met en œuvre la communication de bas niveau avec les différents modules LORA et Freertos pour planifier les tâches de maintenance.
Il existe, dans les fichiers source de cette première implémentation, un exemple pour tester les nouvelles fonctionnalités. Cet exemple est une implémentation d'un compteur, envoyant un message de diffusion toutes les 10 secondes. Pour faciliter la compréhension, nous supprimerons les fonctions supplémentaires qui ne sont pas nécessaires pour faire fonctionner le microcontrôleur avec la bibliothèque Loramesher.
En tant que preuve de concept, nous enverrons un compteur numérique sur Lora. Sa valeur sera incrémentée toutes les 10 secondes, puis le paquet TE sera transmis. Pour commencer, nous devons implémenter le type de données que nous utiliserons.
Dans ce cas, nous n'enverrons qu'un uint32_t , qui est le compteur lui-même.
uint32_t dataCounter = 0;
struct dataPacket {
uint32_t counter = 0;
};
dataPacket* helloPacket = new dataPacket;
Pour initialiser la nouvelle implémentation, vous pouvez configurer les paramètres LORA que la bibliothèque utilisera. Si votre nœud doit recevoir des messages à l'application, voir la section Fonction des paquets reçus.
Vous pouvez configurer différents paramètres pour la configuration LORA. À l'aide du LoRaMesherConfig vous pouvez configurer les paramètres suivants (obligatoire *):
Voici un exemple sur la façon de configurer Loramesher à l'aide de cette configuration:
//Get the LoraMesher instance
LoraMesher& radio = LoraMesher::getInstance();
//Get the default configuration
LoraMesher::LoraMesherConfig config = LoraMesher::LoraMesherConfig();
//Change some parameters to the configuration
//(TTGO T-Beam v1.1 pins)
config.loraCS = 18
config.loraRst = 23
config.loraIrq = 26
config.loraIo1 = 33
config.module = LoraMesher::LoraModules::SX1276_MOD;
//Initialize the LoraMesher. You can specify the LoRa parameters here or later with their respective functions
radio.begin(config);
//After initializing you need to start the radio with
radio.start();
//You can pause and resume at any moment with
radio.standby();
//And then
radio.start();
Être conscient des lois locales qui s'appliquent aux fréquences radio
Si votre nœud doit recevoir des paquets d'autres nœuds, vous devez suivre les étapes suivantes:
La fonction qui obtient une notification chaque fois que la bibliothèque reçoit un paquet pour l'application ressemble à celle-ci:
/**
* @brief Function that process the received packets
*
*/
void processReceivedPackets(void*) {
for (;;) {
/* Wait for the notification of processReceivedPackets and enter blocking */
ulTaskNotifyTake(pdPASS, portMAX_DELAY);
//Iterate through all the packets inside the Received User Packets FiFo
while (radio.getReceivedQueueSize() > 0) {
Serial.println("ReceivedUserData_TaskHandle notify received");
Serial.printf("Queue receiveUserData size: %dn", radio.getReceivedQueueSize());
//Get the first element inside the Received User Packets FiFo
AppPacket<dataPacket>* packet = radio.getNextAppPacket<dataPacket>();
//Print the data packet
printDataPacket(packet);
//Delete the packet when used. It is very important to call this function to release the memory of the packet.
radio.deletePacket(packet);
}
}
}
Il y a des choses importantes dont nous devons être conscients:
void* dans les paramètres.ulTaskNotifyTake(pdPASS,portMAX_DELAY) ou équivalent. Cette fonction permet à la bibliothèque d'informer la fonction pour traiter les paquets en attente.radio.getReceivedQueueSize() .radio.getNextAppPacket<T>() où t est le type de vos données.radio.deletePacket(packet) . Il libérera la mémoire qui a été allouée au paquet. S'il n'est pas exécuté, il peut provoquer des fuites de mémoire et des erreurs de mémoire. TaskHandle_t receiveLoRaMessage_Handle = NULL;
/**
* @brief Create a Receive Messages Task and add it to the LoRaMesher
*
*/
void createReceiveMessages() {
int res = xTaskCreate(
processReceivedPackets,
"Receive App Task",
4096,
(void*) 1,
2,
&receiveLoRaMessage_Handle);
if (res != pdPASS) {
Serial.printf("Error: Receive App Task creation gave error: %dn", res);
}
}
radio.setReceiveAppDataTaskHandle(receiveLoRaMessage_Handle);
Dans cette section, nous vous montrerons ce qu'il y a à l'intérieur d'un AppPacket .
class AppPacket {
uint16_t dst; //Destination address, normally it will be local address or BROADCAST_ADDR
uint16_t src; //Source address
uint32_t payloadSize = 0; //Payload size in bytes
T payload[]; //Payload
size_t getPayloadLength() { return this->payloadSize / sizeof(T); }
};
Fonctionnalités à utiliser après avoir obtenu le paquet avec AppPacket<T>* packet = radio.getNextAppPacket<T>() :
packet->getPayloadLength() Il vous permettra de la taille de la charge utile en nombre de tradio.deletePacket(packet) Il publiera la mémoire allouée pour ce paquet. Dans cette section, nous présenterons comment créer et envoyer des paquets. Dans cet exemple, nous utiliserons la structure des données AppPacket .
void loop() {
helloPacket->counter = dataCounter++;
//Create packet and send it.
radio.createPacketAndSend(BROADCAST_ADDR, helloPacket, 1);
//Or if you want to send large and reliable payloads you can call this function too.
radio.sendReliable(dstAddr, helloPacket, 1);
//Wait 10 seconds to send the next packet
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
Dans la figure précédente, nous pouvons voir que nous utilisons le HelloPacket, nous ajoutons le compteur à l'intérieur, et nous créons et envoyons le paquet à l'aide du Loramesher.
La partie la plus importante de ce morceau de code est la fonction que nous appelons dans la radio.createPacketAndSend() :
Lors de la réception du paquet, nous devons comprendre ce que la file d'attente nous rendra. Pour cette raison, dans la prochaine sous-section, nous expliquerons comment implémenter un simple traitement de paquets.
/**
* @brief Print the counter of the packet
*
* @param data
*/
void printPacket(dataPacket data) {
Serial.printf("Hello Counter received n %dn", data.counter);
}
/**
* @brief Iterate through the payload of the packet and print the counter of the packet
*
* @param packet
*/
void printDataPacket(AppPacket<dataPacket>* packet) {
//Get the payload to iterate through it
dataPacket* dPacket = packet->payload;
size_t payloadLength = packet->getPayloadLength();
for (size_t i = 0; i < payloadLength; i++) {
//Print the packet
printPacket(dPacket[i]);
}
}
processReceivedPackets() , nous appelons la fonction printDataPacket() .packet->payload .packet->getPayloadLength() . Cela nous permettra de savoir quelle est la taille de la charge utile, dans les types de cackts de données, pour un paquet donné. Dans notre cas, nous n'envoyons toujours qu'un seul sac de données.printPacket(dPacket[i]) , qui imprimera le compteur reçu. Veuillez consulter notre document en libre accès "Implémentation d'une bibliothèque Lora Mesh" pour une description détaillée. Si vous utilisez la bibliothèque Loramesher, dans le travail académique, veuillez citer ce qui suit:
@ARTICLE{9930341,
author={Solé, Joan Miquel and Centelles, Roger Pueyo and Freitag, Felix and Meseguer, Roc},
journal={IEEE Access},
title={Implementation of a LoRa Mesh Library},
year={2022},
volume={10},
number={},
pages={113158-113171},
doi={10.1109/ACCESS.2022.3217215}}