Home»Desarrollos»LoRa – Long Range (2/2)

LoRa – Long Range (2/2)

1
Shares
Pinterest WhatsApp

En esta segunda parte del proyecto de LoRa vamos a ver como es el montaje de los diferentes módulos y su programación. También veremos todo el conjunto en funcionamiento. Al final del post podréis encontrar el código fuente del proyecto.

Montaje del dispositivo emisor

El emisor va a estar compuesto por 3 módulos que tendremos que conectar a nuestro Arduino:

  • Módulo LoRa SX1278 conectado por SPI al Arduino
  • Sensor de temperatura DS18B20 conectado por 1-wire
  • Relé conectado por una salida digital
RA-01Arduino
NSSD53
MOSID51
MISOD50
SCKD52
DI00D02
RSTD03
3V33V3
GNDGND

En la primera parte de este proyecto ya se ha detallado los pines a utilizar en el Arduino Mega para el bus SPI:

El módulo SX1278 trabaja a 3V3, con lo que tened cuidado de conectarlo correctamente.

El sensor de temperatura trabaja entre 3V y 5V, así que lo conectaremos a 5V y a GND, y utilizaremos el PIN 4 digital para establecer la conexión 1-wire.

El relé lo conectaremos a 5v, GND y elegiremos el PIN 17 digital para poder activarlo y desactivarlo. Podéis elegir cualquiera de los pones digitales del Mega, recordad ajustarlo en el código.

Es importante conectar la salida DI00 a una entrada digital del arduino con interrupción, en nuestro caso hemos elegido la D02.

Programando nuestro emisor

Las librerías que vamos a incluir en nuestro proyecto serán las siguientes:

Definición de variables

Definiremos unas variables para indicar los pines digitales a utilizar, la clave para el cifrado AES128 y las instancias para la comunicación con el sensor DS.

// Pin donde se conecta el bus 1-Wire
const int pinDatosDQ = 4;

// Pin donde se conecta el rele
const int relePin = 17;
boolean releActivo = false;

// Instancia a las clases OneWire y DallasTemperature
OneWire oneWireObjeto(pinDatosDQ);
DallasTemperature sensorDS18B20(&oneWireObjeto);

//Clave AES128
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

Setup

En el setup inicializaremos los diferentes módulos:

//Inicialización de la terminal  
  Serial.begin(9600);
  while (!Serial);

  //Configuramos el pin del rele de salida
  pinMode(relePin, OUTPUT); 

  //Configuramos la conexión con el DS18B20
  sensorDS18B20.begin(); 

  //Configuramos la librería LoRa con los pines CS, RST, DST
  LoRa.setPins(53, 3, 2);

  //Esperamos que este activo el módulo LoRa
  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }

  //Configuramos el callback que se ejecutará cuando recibamos un mensaje
  LoRa.onReceive(onReceive);

  //Activamos el modo "escucha" del LoRa.
  LoRa.receive();  

A la hora de inicializar el módulo LoRa elegimos el modo 433E6 que representa la banda de trabajo de 433Mhz. Si nuestro módulo LoRa trabajase en otra banda tenemos más opciones: 866E6915E6

Loop

El método loop de nuestor emisor se encargará de enviar cada 5 segundos la temperatura al receptor, es muy sencillo.

 // Cada 5 segundos enviaremos la temperatura al receptor
 if (runEvery(5000)) { 
   sendTemperatura();
 }

Funciones de apoyo

Contaremos con 2 funciones que nos van a permitir encapsular mejor la funcionalidad: el callback de LoRa y la encargada de enviar la temperatura cifrada.

void sendTemperatura() {

    //Recuperamos la temperatura del sensor
    sensorDS18B20.requestTemperatures();

    //Cadena donde almacenaremos la temperatura en un array de char
    char buffn[10]= {};  

    //Convertimos la temperatura
    dtostrf(sensorDS18B20.getTempCByIndex(0),6,2,buffn); 

    //Preparamos un buffer para el mensaje: temperatura y estado de relé
    char buffer[16]= {};  
    sprintf(buffer, "%s %12s", releActivo?"On ":"Off",buffn);

    //Encriptamos el buffer con la clave definida
    aes128_enc_single(key, buffer);

    //Enviamos el mensaje
    LoRa.beginPacket();
    LoRa.print(buffer);
    LoRa.endPacket();

    //Volvemos a poner en escucha el módulo LoRa
    LoRa.receive();  
}

La otra función será el callback. Cuando el módulo LoRa detecte un mensaje llamará a esta función. En nuestro caso, este mensaje será una orden de activar/desactivar el relé.

void onReceive(int packetSize) {

  //Leemos el mensaje recibido.
  String message = "";
  while (LoRa.available()) {
    message += (char)LoRa.read();
  }

  //Verificamos que la orden sera "RELE" para cambiar el estado.
  if(message == "RELE") {
     if(releActivo) {
       digitalWrite(relePin, LOW);   
     } else {
       digitalWrite(relePin, HIGH);
     }
     releActivo = !releActivo;
     delay(1000);

     //Notificamos al receptor el nuevo estado del relé
     sendTemperatura();
  }
}

Por último, una pequeña función utilidad para realizar una acción dada «n» milisegundos:

boolean runEvery(unsigned long interval)
{
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    return true;
  }
  return false;
}

Montaje del dispositivo receptor

El receptor también va a estar compuesto por 2 módulos y un pequeño botón:

  • Módulo LoRa SX1278 conectado por SPI al Arduino
  • Pantalla LCD conectada por I2C
  • Pulsador conectado a una entrada analógica, la A0 en nuestro proyecto.

Programando nuestro receptor

Para el receptor, las librerías que vamos a incluir van a ser las mismas que en el emisor, sustituyendo la del sensor de temperatura por la de la pantalla LCD con I2C.

#include <SPI.h>
#include <Wire.h> 
#include <LoRa.h>
#include <LiquidCrystal_I2C.h>
#include <AESLib.h>

Hay muchas librerías de LCD, una de ellas: https://github.com/johnrickman/LiquidCrystal_I2C

Definición de variables

//Pin analógico A0
const int buttonPin = A0;

//Clave AES128
uint8_t key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};

//Instancia de la clase LiquidCrystal_I2C 
LiquidCrystal_I2C lcd(0x27, 16, 2); 

En nuestro proyecto usaremos una LCD 16×2 con el ID 0x27 que lo identifica dentro de bus I2C.

Recordad poner la misma clave en ambos dispositivos !!!!!!

Setup

Iremos inicializando cada uno de los componentes.

  //Inicialización de la terminal  
  Serial.begin(9600);
  while (!Serial);

  //Configuramos la entrada analógica para el botón
  pinMode(buttonPin, OUTPUT); 
  digitalWrite(buttonPin, HIGH);

  //Inicializamos el LCD.
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("LoRa Receiver");    
 
  
  //Configuramos la librería LoRa con los pines CS, RST, DST
  LoRa.setPins(53, 3, 2);
  
  //Esperamos que este activo el módulo LoRa
  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    lcd.setCursor(0, 0);
    lcd.print("Starting LoRa failed!");   
    while (1);
  }

  //Activamos el modo "escucha" del LoRa.
  LoRa.receive();

Loop

El método Loop va a estar continuamente viendo si se ha recibido algo del emisor, si recibe un mensaje lo decodifica y lo muestra por el LCD.

Por otro lado, también estará checkeando si el usuario a pulsado el botón para enviar el comando al emisor.

//Se intenta parsear los mensajes recibidos por el módulo
  int packetSize = LoRa.parsePacket();
 
  //Si tenemos un nuevo mensaje
  if (packetSize) {
      
    //Preparamos el array para almacenarlo
    char message[16] = {};
   
    //Recivimos el paquete y lo almacenamos
    while (LoRa.available()) {
      for (int i = 0; i < 16; i++)  {
       // Message = Message + (char)LoRa.read();
        message[i] = (char)LoRa.read();
      }
    }
    
    //Se decodifica el mensaje
    aes128_dec_single(key, message);
    
    //Si es un mensaje correcto comenzará por la letra 'O'
    if(message[0] == 'O') {
       //Se pinta en el LCD.
       lcd.setCursor(0, 1);
       lcd.print("            ");
       lcd.setCursor(0, 1);
       lcd.print(message);
    }
    
    //Mostramos por el LCD la fuerza de la señal recibida.
    lcd.setCursor(0, 0);
    lcd.print("                  ");
    lcd.setCursor(0, 0);
    lcd.print("RSSI ");
    lcd.print(LoRa.packetRssi());   
    
  }
  //Leemos el botón por si el usuario lo ha pulsado
  int buttonState = analogRead(buttonPin);

  //Si el valor leido es menor de 100 enviamos mensaje al emisor
  if(buttonState  < 100) {

    //Informamos por LCD de la acción  
    lcd.setCursor(0, 0);
    lcd.print("Enviando !!      ");

    //Trasmitimos el comando 'RELE'
    LoRa.beginPacket();               
    LoRa.print("RELE");                 
    LoRa.endPacket();
    delay(2000);
     
    lcd.setCursor(0, 0);
    lcd.print("                  ");

    //Volvemos a poner el módulo LoRa en escucha
    LoRa.receive();      
  }

El valor de la entrada analógica A0 sera de 1023 mientras no se pulse el botón. Cuando el usuario lo presiona, el valor será inferior a 100.

El proyecto funcionando

Unas fotos de ambos dispositivos montados.

Emisor con rele, termómetro y modulo Lora
Receptor con LCD, botón y módulo LoRa

En este caso tenía ambos dispositivos alimentados por baterías para poderlos mover.

Unos pequeños vídeos donde vemos el funcionamiento del proyecto y los mensajes por consola que van generado.

Código Fuente

El código fuente del emisor y del receptor podéis encontrarlo en mi github:

https://github.com/gonzalogalvan/loraarduino

Podéis colaborar en mejorar el código, seguro que hay cientos de puntos donde arañar algunos bytes.

Espero que os haya gustado este pequeño proyecto que permite trabajar con diferentes buses, empezar a aprender de LoRa y mantener la privacidad de nuestras emisiones. En un futuro post intentaré montar todo este conjunto con un Arduino más pequeño (y por tanto con menos consumo) y alimentarlo por batería. Así podré realizar pruebas de campo y ver el alcance y potencia de esta tecnología.

Previous post

LoRa - Long Range (1/2)

Next post

This is the most recent story.

2 Comments

  1. Osvaldo Pucciarelli
    28 julio, 2020 at 11:58 am — Responder

    Muy bueno tu proyecto , ha sidode mucha utilidad en el aprendizaje de la comunicacion y realmente funciona hice las pruebas con un arduino uno como emisor y un mega como receptor

    • gonzalo
      7 noviembre, 2023 at 6:29 pm — Responder

      Me alegra que te haya sido de utilidad.

Leave a reply

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *