Die Loramesher-Bibliothek implementiert ein Distanzvektor-Routing-Protokoll zur Kommunikation von Nachrichten zwischen Lora-Knoten. Für die Interaktion mit dem LORA -Radio -Chip nutzen wir Radiolib, eine vielseitige Kommunikationsbibliothek, die verschiedene LORA -Serienmodule unterstützt.
In diesem Moment wurde Loramesher in den folgenden Modulen getestet:
Sie können anfordern, dass ein anderes Modul der Bibliothek durch Öffnen eines Problems hinzugefügt wird.
Sie können library.json für weitere Details überprüfen. Grundsätzlich verwenden wir Radiolib, das die Kommunikation mit niedriger Ebene mit den verschiedenen LORA -Modulen und Freertos für die Planung von Wartungsaufgaben implementiert.
In den Quelldateien dieser ersten Implementierung gibt es ein Beispiel zum Testen der neuen Funktionen. Dieses Beispiel ist eine Implementierung eines Zählers, das alle 10 Sekunden eine Sendungsnachricht sendet. Um das Verständnis zu erleichtern, werden wir zusätzliche Funktionen entfernen, die nicht erforderlich sind, damit der Mikrocontroller mit der Loramesher -Bibliothek funktioniert.
Als Proof of Concept werden wir einen numerischen Zähler über Lora senden. Sein Wert wird alle 10 Sekunden erhöht, dann wird das TE -Paket übertragen. Zu Beginn müssen wir den von uns verwendeten Datentyp implementieren.
In diesem Fall senden wir nur einen uint32_t , der der Zähler selbst ist.
uint32_t dataCounter = 0;
struct dataPacket {
uint32_t counter = 0;
};
dataPacket* helloPacket = new dataPacket;
Um die neue Implementierung zu initialisieren, können Sie die LORA -Parameter konfigurieren, die die Bibliothek verwendet. Wenn Ihr Knoten Nachrichten an die Anwendung empfangen muss, siehe Abschnitt "Empfangene Pakete".
Sie können verschiedene Parameter für die LORA -Konfiguration konfigurieren. Mit der LoRaMesherConfig können Sie die folgenden Parameter (obligatorisch*) konfigurieren:
Hier ist ein Beispiel, wie Sie Loramesher mit dieser Konfiguration konfigurieren:
//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();
Beachten Sie die lokalen Gesetze, die für Funkfrequenzen gelten
Wenn Ihr Knoten Pakete von anderen Knoten empfangen muss, sollten Sie die nächsten Schritte befolgen:
Die Funktion, die jedes Mal eine Benachrichtigung erhält, wenn die Bibliothek ein Paket für die App erhält, sieht so aus:
/**
* @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);
}
}
}
Es gibt einige wichtige Dinge, die wir uns bewusst sein müssen:
void* haben.ulTaskNotifyTake(pdPASS,portMAX_DELAY) oder gleichwertig ist. Mit dieser Funktion kann die Bibliothek die Funktion benachrichtigen, um anhängige Pakete zu verarbeiten.radio.getReceivedQueueSize() zu erhalten.radio.getNextAppPacket<T>() erhalten, wobei T der Typ Ihrer Daten ist.radio.deletePacket(packet) anrufen. Es wird den Speicher, der für das Paket zugewiesen wurde, freigeben. Wenn dies nicht ausgeführt wird, kann dies Speicherlecks und keine Speicherfehler verursachen. 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);
In diesem Abschnitt zeigen wir Ihnen, was sich in einem AppPacket befindet.
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); }
};
Funktionen zu verwenden, nachdem das Paket mit AppPacket<T>* packet = radio.getNextAppPacket<T>() :
packet->getPayloadLength() erhalten Sie die Nutzlastgröße in der Anzahl von tradio.deletePacket(packet) Es wird den für dieses Paket zugewiesenen Speicher veröffentlicht. In diesem Abschnitt präsentieren wir, wie Sie Pakete erstellen und senden können. In diesem Beispiel verwenden wir die AppPacket -Datenstruktur.
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);
}
In der vorherigen Abbildung können wir sehen, dass wir die Hellopacket verwenden, den Zähler darin hinzufügen und das Paket mit dem Loramesher erstellen und senden.
Der wichtigste Teil dieses Codes ist die Funktion, die wir im radio.createPacketAndSend() :
Beim Empfangen des Pakets müssen wir verstehen, was uns die Warteschlange zurücksetzt. Aus diesem Grund werden wir im nächsten Unterabschnitt erläutern, wie eine einfache Paketverarbeitung implementiert wird.
/**
* @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() erhalten haben, rufen wir die Funktion printDataPacket() auf.packet->payload erhalten.packet->getPayloadLength() . Auf diese Weise werden wir wissen, wie groß die Nutzlast bei Datapackets -Typen für ein bestimmtes Paket ist. In unserem Fall senden wir immer nur eine DataPacket.printPacket(dPacket[i]) an, die den empfangenen Zähler drucken. In unserem Open -Access -Papier "Implementierung einer Lora Mesh Library" finden Sie eine detaillierte Beschreibung. Wenn Sie die Loramesher -Bibliothek in akademischen Arbeiten verwenden, geben Sie Folgendes an:
@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}}