2.2 LoRa Multipunto – HELTEC ESP32+LoRa Dispositivo.ino

Referencia: https://github.com/Heltec-Aaron-Lee/WiFi_Kit_series

Para prueba del concepto, se implementa un dispositivo simplificado, un dispositivo que emite un «parpadeo» binario como estado de sensor.

Estado del sensor

El estado del sensor se indica con los valores "ON" encendido y "OFF" para apagado. El parpadeo se realiza a intervalos de tiempo de duración aleatoria entre 1 a 3 segundos.

Direccionamiento

El direccionamiento se realiza usando un numero hexadecimal almacenado en un byte. Por facilidad de identificación, se usa como dirección :

  • Dispositivo usa "D1" por la inicial, los otros dispositivos serán "D2","D3", etc.
  • gateway usa la dirección "C1" cuya inicial es de Concentrador o coordinador, nombre también usado en otras tecnologías.

El algoritmo esta realizado para una placa de desarrollo LoRa, la disponible es de marca Heltec LoRa ESP32 que ofrece librerías simplificadas. Un siguiente paso de desarrollo consiste en usar un módulo LoRa y un Arduino Uno por ejemplo, realizado con librerías más generales.

Instrucciones

Las instrucciones de dividen en el bloque principal, el procedimiento de sensor, y los procedimientos LoRa para envío y recepcion, separados en cada pestaña.

Bloque principal

Declara las librerias para el módulo o placa de desarrollo Heltec, se indica los parámetros LoRa como la Banda ISM que para Ecuador es US915, también se establecen las variables para el manejo de los mensajes de envío y recepción, tiempo de lecturas del sensor «simulado» para la prueba.

El bucle de configuración setup() inicializa el módulo y el de operación loop() revisa los tiempos en los que se debe realizar la lectura del sensor y el envío del mensaje LoRa. Luego revisa si se ha recibido un mensaje LoRa para mostrarlo en la ventana del «monitor serie».

 

/* Dispositivo Sensor Blink Parpadeo ON/OFF
 * http://blog.espol.edu.ec/girni/lora-multipunto-esquema/
 * Referencia: Ejemplos de Aaron.Lee www.heltec.cn
*/
#include "heltec.h"

// LoRa Banda ISM en Región 915Mhz
#define BAND  915E6      // 433E6,868E6,915E6
byte spread_factor = 8;  // rango 6-12,default 7

// Mensaje LoRa a enviar por direcciones
byte dir_local   = 0xD1; // Dispositivo 1
byte dir_destino = 0xC1; // Dispositivo 2
byte id_msjLoRa  = 0;    // cuenta mensaje
String paqueteEnv= "";   // mensaje

// Mensaje LoRa recibido
byte dir_envio  = 0xD1; // receptor
byte dir_remite = 0xC1; // emisor
String paqueteRcb = ""; // mensaje LoRa
byte   paqRcb_ID  = 0;
byte   paqRcb_Estado = 0;
  // 0:vacio, 1: nuevo, 2:incompleto
  // 3:otro destinatario, 4:Broadcast

// Mensajes por Puerto Serial activa
boolean serial_msj = true;

// SENSOR Parpadeo
String sensorEstado = "ON"; // ON/OFF: 1/0
// tiempo entre lecturas
long tiempo_antes     = 0;
long tiempo_intervalo = 6000;
long tiempo_espera = tiempo_intervalo + random(3000);

void setup(){
  Heltec.begin(false /*DisplayEnable Enable*/,
    true /*Heltec.Heltec.Heltec.LoRa Disable*/,
    serial_msj /*Serial Enable*/,
    true /*PABOOST Enable*/,
    BAND /*long BAND*/);
  LoRa.setSpreadingFactor(spread_factor);

  //LoRa.onReceive(cbk);
  LoRa.receive();
}

void loop(){
  int rssi_lora = 0; // nivel de señal
  int snr_lora  = 0;
  
  // Enviar mensajes entre intervalos
  long tiempo_ahora   = millis();
  long t_transcurrido = tiempo_ahora - tiempo_antes;
  
  if (t_transcurrido >= tiempo_espera){

    sensor_revisa(); //actualiza sensor
    
    paqueteEnv = String(sensorEstado).c_str() ;
    envia_lora(dir_destino, dir_local,
               id_msjLoRa, paqueteEnv);
    id_msjLoRa = id_msjLoRa + 1;
     
    // mensaje a serial monitor
    if (serial_msj == true){
      Serial.print("Enviado:  ");
      Serial.print(String(dir_local,HEX));
      Serial.print(",");
      Serial.print(String(dir_destino,HEX));
      Serial.print(",");
      Serial.print(id_msjLoRa-1); 
      Serial.print(",");
      Serial.println(paqueteEnv);
    }
    
    tiempo_antes = millis();
    tiempo_espera = tiempo_intervalo + random(3000);
    
    // LED parpadea. Envio LoRa
    digitalWrite(LED, HIGH); delay(100);
    digitalWrite(LED, LOW ); delay(100);
  }

  // Revisar mensajes LoRa entrantes
  int msjRcbLoRa = LoRa.parsePacket();
  if (msjRcbLoRa !=0){
    recibe_lora(msjRcbLoRa);
    rssi_lora = LoRa.packetRssi();
    snr_lora  = LoRa.packetSnr();
    
    if (serial_msj == true){
      if (paqRcb_Estado == 1){
        Serial.print("Recibido: ");
        Serial.print(String(dir_remite,HEX)); 
        Serial.print(",");
        Serial.print(String(dir_envio,HEX));
        Serial.print(",");
        Serial.print(paqRcb_ID);  Serial.print(",");
        Serial.print(paqueteRcb); Serial.print(",");
        Serial.print(rssi_lora);  Serial.print(",");
        Serial.print(snr_lora);   Serial.print(",");
        Serial.println();
      }else{
        Serial.print("Paquete recibido Estado: ");
        Serial.println(paqRcb_Estado);
      }
    }
    
    // LED parpadea Rebibido Lora
    digitalWrite(LED, HIGH); delay(50);
    digitalWrite(LED, LOW ); delay(50);
    digitalWrite(LED, HIGH); delay(50);
    digitalWrite(LED, LOW );
  }
  delay(100);
}

Procedimiento de envío de paquete LoRa

Se toman los valores para destino, remitente, identificador de paquete y el mensaje o paquete a enviar, para realizar el paso de envio del mensaje con la instrucción LoRa.write().

void envia_lora(byte destino, byte remite,
                byte paqueteID, String paquete){
                  
  // espera radio para enviar un paquete
  while(LoRa.beginPacket() == 0){
    if (serial_msj==true){
      Serial.println("Esperando radio disponible...");
    }
    delay(100);
  }
  
  // envio del mensaje LoRa
  LoRa.beginPacket();
  LoRa.write(destino);
  LoRa.write(remite);
  LoRa.write(paqueteID);
  LoRa.write(paquete.length());
  LoRa.print(paquete);
  LoRa.endPacket();
}

Procedimiento de recepción de paquete LoRa

Para recibir un paquete LoRa se procede en el mismo orden realizado para el envío, es decir primero se recibe la dirección de envío, la dirección del remitente, el identificador del mensaje, tamaño del mensaje en bytes, todo lo que viene luego es el mensaje transmitido.

Luego se revisa el tamaño del paquete recibido y se compara con el valor de la variable de tamaño del mensaje. Esto permite validar si el mensaje se ha recibido completo o requiere alguna retransmisión.

void recibe_lora(int tamano){
  if (tamano == 0){ 
    paqRcb_Estado = 0; //vacio
    return;
    }
    
  // lectura de paquete
  paqueteRcb = "";
  dir_envio  = LoRa.read();
  dir_remite = LoRa.read();
  paqRcb_ID  = LoRa.read();
  
  byte paqRcb_Tamano = LoRa.read();
  while(LoRa.available()){
    paqueteRcb += (char)LoRa.read();
  }
  
  if (paqRcb_Tamano != paqueteRcb.length()){
    paqRcb_Estado = 2; // Tamaño incompleto
    return;
  }
  if (dir_envio != dir_local){
    paqRcb_Estado = 3; // otro destino
    return;
  }
  if (dir_envio == 0xFF) {
    paqRcb_Estado = 4; //Broadcast, difusion
    return;
  }
  paqRcb_Estado = 1;   // mensaje Nuevo
}

Procedimiento para el sensor

En el ejercicio el procedimiento para el sensor consiste en altenar los valores entre encendido y apagado, el valor cambia cada vez que se use el procedimiento sensor_revisa()

// Sensor Simulado
void sensor_revisa(){
    if (sensorEstado == "ON"){
      sensorEstado = "OFF";
    }else{
      sensorEstado = "ON";
    }
}

Para subir todas las instrucciones recuerde primero seleccionar la placa de desarrollo correspondiente y verificar el puerto com al que se conecta

Resultados en monitor serie

El resultado observable en monitor serie es semejante al mostrado:

Serial initial done
LoRa Initial success!
Enviado:  d1,c1,0,OFF
Enviado:  d1,c1,1,ON
Enviado:  d1,c1,2,OFF
Enviado:  d1,c1,3,ON

Pruebas punto a punto

Para realizar pruebas punto a punto, es necesario usar todas las instrucciones anteriores y cambiar solamente la dirección local y destino del mensaje y  en el bloque principal siguiendo el esquema:

// LoRa envia paquete, direccion, contador
byte dir_local    = 0xC1; // Dispositivo 1
byte dir_destino  = 0xD1; // Dispositivo 2

En el caso de recepción, los valores se actualizan en el procedimiento, dado que las direcciones son parte de la trama de datos, por lo que los valores declarados son solo referenciales.

// LoRa recibe paquete
byte dir_envio  = 0xC1;  // receptor
byte dir_remite = 0xD1;  // emisor

Con lo que se pueden intercambiar mensajes entre dispositivos, modalidad punto a punto, obteniendo el siguiente resultado si están conectados las dos placas de desarrollo.