La biblioteca Loramesher implementa un protocolo de enrutamiento de vector de distancia para comunicar mensajes entre nodos Lora. Para la interacción con el chip de radio Lora, aprovechamos a Radiolib, una biblioteca de comunicación versátil que admite diferentes módulos de la serie Lora.
En este momento, Loramesher ha sido probado dentro de los siguientes módulos:
Puede solicitar otro módulo para agregar a la biblioteca abriendo un problema.
Puede consultar library.json para obtener más detalles. Básicamente, usamos radiolib que implementa la comunicación de bajo nivel a los diferentes módulos Lora y Freertos para programar tareas de mantenimiento.
Hay, en los archivos fuente de esta primera implementación, un ejemplo para probar las nuevas funcionalidades. Este ejemplo es una implementación de un contador, que envía un mensaje de transmisión cada 10 segundos. Para que sea más fácil de entender, eliminaremos funciones adicionales que no son necesarias para que el microcontrolador funcione con la biblioteca Loramesher.
Como prueba de concepto, enviaremos un mostrador numérico sobre Lora. Su valor se incrementará cada 10 segundos, luego se transmitirá el paquete TE. Para comenzar, necesitamos implementar el tipo de datos que utilizaremos.
En este caso, solo enviaremos un uint32_t , que es el contador en sí.
uint32_t dataCounter = 0;
struct dataPacket {
uint32_t counter = 0;
};
dataPacket* helloPacket = new dataPacket;
Para inicializar la nueva implementación, puede configurar los parámetros Lora que usará la biblioteca. Si su nodo necesita recibir mensajes a la aplicación, consulte la sección de la función de paquetes recibidos.
Puede configurar diferentes parámetros para la configuración de Lora. Usando el LoRaMesherConfig , puede configurar los siguientes parámetros (obligatorios*):
Aquí hay un ejemplo sobre cómo configurar Loramesher utilizando esta configuración:
//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();
Tenga en cuenta las leyes locales que se aplican a las frecuencias de radio
Si su nodo necesita recibir paquetes de otros nodos, debe seguir los siguientes pasos:
La función que recibe una notificación cada vez que la biblioteca recibe un paquete para la aplicación se ve así:
/**
* @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);
}
}
}
Hay algunas cosas importantes de las que debemos tener en cuenta:
void* en los parámetros.ulTaskNotifyTake(pdPASS,portMAX_DELAY) o equivalente. Esta función permite que la biblioteca notifique a la función para procesar paquetes pendientes.radio.getReceivedQueueSize() .radio.getNextAppPacket<T>() donde t es el tipo de sus datos.radio.deletePacket(packet) . Liberará la memoria que se ha asignado para el paquete. Si no se ejecuta, puede causar fugas de memoria y errores fuera de la memoria. 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);
En esta sección le mostraremos lo que hay dentro de 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); }
};
Las funcionalidades se utilizarán después de obtener el paquete con AppPacket<T>* packet = radio.getNextAppPacket<T>() ::
packet->getPayloadLength() le dará el tamaño de carga útil en número de tradio.deletePacket(packet) Liberará la memoria asignada para este paquete. En esta sección presentaremos cómo puede crear y enviar paquetes. En este ejemplo, utilizaremos la estructura de datos 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);
}
En la figura anterior podemos ver que estamos usando el HelloPacket, agregamos el contador dentro de ella y creamos y enviamos el paquete usando el Loramesher.
La parte más importante de esta pieza de código es la función que llamamos en el radio.createPacketAndSend() ::
Al recibir el paquete, debemos entender qué nos devolverá la cola. Por esta razón, en la próxima subsección, explicaremos cómo implementar un procesamiento de paquetes simple.
/**
* @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() , llamamos a la función printDataPacket() .packet->payload .packet->getPayloadLength() . Esto nos hará saber qué tan grande es la carga útil, en los tipos de Datapackets, para un paquete determinado. En nuestro caso, siempre enviamos solo una compañía de datos.printPacket(dPacket[i]) , que imprimirá el contador recibido. Consulte nuestro documento de acceso abierto "Implementación de una biblioteca de malla Lora" para obtener una descripción detallada. Si usa la Biblioteca Loramesher, en el trabajo académico, cite lo siguiente:
@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}}