LoRa – Long Range (2/2)
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-01 | Arduino |
---|---|
NSS | D53 |
MOSI | D51 |
MISO | D50 |
SCK | D52 |
DI00 | D02 |
RST | D03 |
3V3 | 3V3 |
GND | GND |
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:
- SPI.h : Para tener la implementación del bus SPI
- LoRa.h : Librería que nos va a facilitar trabajar con el SX1278
- OneWire.h : Protocolo para comunicarnos por bus 1-wire.
- DallasTemperature.h : Librería para trabajar con DS18B20
- AESLib.h : Arduino AES Lib para el encriptado de los mensajes con AES128.
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: 866E6
, 915E6
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.
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.
2 Comments
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
Me alegra que te haya sido de utilidad.