Skip to Content

Enviar datos ambientales con LoRa

Enviar datos ambientales con LoRa

En este tutorial aprenderás cómo enviar datos ambientales con LoRa. LoRa te permite transmitir datos de sensores a largas distancias (varios kilómetros) consumiendo muy poca energía. Usaremos esto para transmitir datos de Temperatura, Humedad y Presión Atmosférica medidos por un BME280 entre dos ESP32 conectados a módulos LoRa SX1276.

Si no estás familiarizado con LoRa, te sugiero leer nuestro Long range communication with LoRa SX1276 and ESP32 tutorial primero, ya que te proporciona la información básica necesaria para este tutorial.

Partes necesarias

A continuación encontrarás los componentes requeridos. Usé una placa ESP32 antigua, que ya está descontinuada pero aún puedes conseguirla a muy bajo precio. Sin embargo, cualquier otro ESP32 funcionará perfectamente. Solo recuerda que necesitarás dos de ellos y dos módulos SX1276: uno para enviar y otro para recibir.

En cuanto a los módulos transceptores LoRa SX1276, ¡cuidado con la versión que compres! Dependiendo del país, las frecuencias permitidas son diferentes (link). En Europa es 868MHz, en Norteamérica 915MHz y en Asia 433MHz.

La descripción del módulo suele indicar la frecuencia o tiene un número como 868 o 915 en el nombre. Yo elegí un módulo de 868MHz, ya que estoy en Europa. Pero también puedes conseguir este módulo para la banda de 915MHz.

Para el sensor ambiental, escogí el BME280, ya que tiene un tamaño pequeño, modo de suspensión y puede medir Temperatura, Humedad, Presión Atmosférica y Altitud. Ten en cuenta que existen versiones de la placa BME280 para 5V y 3.3V. Asegúrate de comprar la versión de 3.3V. Si solo necesitas Temperatura y Humedad, el Si7021 es una buena alternativa.

2 x Módulo LoRa 868/915M SX1276

ESP32 lite Lolin32

2 x ESP32 lite

BME280

Sensor BME280

USB data cable

Cable de datos USB

Dupont wire set

Juego de cables Dupont

Half_breadboard56a

2x Protoboard

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Módulo LoRa SX1276

El SX1276 es un transceptor LoRa fabricado por Semtech. La mayoría de los módulos (placas breakout) basados en este chip están configurados para 433 MHz (Asia), 868 MHz (Europa) o 915 MHz (Norteamérica), según tu región. En la parte trasera del módulo suele haber una pequeña tabla donde se indica la frecuencia de operación del módulo específico. La imagen a continuación muestra un módulo que opera en la banda de 915 MHz y por lo tanto podría usarse en Norteamérica:

Back of SX1276 LoRa Module
Parte trasera del módulo LoRa SX1276

Asegúrate de usar un módulo con la frecuencia de transmisión correcta para tu país. El módulo puede comunicarse a distancias de 2 a más de 15 kilómetros, dependiendo de la calidad de la antena, el terreno y la configuración. El SX1276 soporta velocidades de datos desde 0.018 kbps hasta 37.5 kbps. Para más detalles técnicos consulta la hoja de datos enlazada abajo:

El módulo tiene 16 pines. Uno para la antena (ANT), uno para alimentación (VCC), varios pines de tierra (GND), los pines para SPI (RST, NSS, SCK, MOSI, MISO) y seis pines configurables Digital IO (DIO).

Ten en cuenta que el SX1276 usa lógica de 3.3V, por lo que no puedes conectarlo directamente a un Arduino UNO, que opera con niveles lógicos de 5V. Si quieres usar un Arduino en lugar de un ESP32, debes usar un logic level shifter o una placa Arduino que use 3.3V logic.

Sensor BME280

Para medir datos ambientales como temperatura, humedad y presión atmosférica usaremos el BME280 sensor.

Una razón es que tiene un modo de bajo consumo, modo suspensión, donde consume solo 0.1µA (3.6 μA en modo normal). En combinación con un módulo LoRa y el ESP32-lite con un consumo en deep-sleep de 5.65mA (a 5V), esto resulta en un sistema de muy bajo consumo que puede funcionar mucho tiempo con batería.

El sensor BME280 es pequeño y normalmente viene en una placa breakout con interfaz I2C; mira la imagen a continuación.

BME280 breakout board
Placa breakout BME280

El sensor puede medir presión desde 300 hPa hasta 1100 hPa, temperatura de -40°C a +85°C, y humedad de 0% a 100%. Para más información sobre el BME280 lee nuestro tutorial sobre How To Use BME280 Pressure Sensor With Arduino o el Weather Station on e-Paper Display tutorial.

Conexión del SX1276 al ESP32

El SX1276 se controla mediante una interfaz SPI. Al conectarlo a un ESP32 debemos hacer las siguientes conexiones:

SX1276ESP32
MOSI23
MSIO19
SCK18
RST17
NSS5
DIO04
GNDGND
VCC3.3V

Asegúrate de conectar VCC al pin de salida de 3.3V de tu ESP32. Realmente no usamos DIO0, así que podrías omitir esa conexión. DIO0 es necesario si quieres añadir un manejador de interrupciones que se active cuando una medición se complete. La imagen a continuación muestra el diagrama completo de conexiones (incluyendo DIO0):

Connecting SX1276 to ESP32
Conexión del SX1276 al ESP32

Si usas una placa ESP32 diferente, los pines para SPI hardware, de los que depende el cableado anterior, pueden variar. Si no estás seguro de qué pines usar en tu placa, consulta el Find I2C and SPI default pins tutorial.

La foto a continuación muestra el cableado del receptor LoRa SX1276 con el ESP32 en una protoboard:

SX1276 LoRa Receiver with the ESP32 on a breadboard
Receptor LoRa SX1276 con ESP32 en protoboard

Ten en cuenta que el módulo SX1276 tiene un espaciado de pines de 2mm y no encaja directamente en una protoboard. Consulta el Long range communication with LoRa SX1276 and ESP32 tutorial para aprender a construir una versión compatible con protoboard del módulo.

Conexión del BME280 al ESP32

Queremos medir temperatura ambiente, humedad y presión atmosférica usando el sensor BME280. Como los pines hardware I2C/SPI del ESP32 ya están conectados al SX1276, usaremos I2C por software con los pines libres 33 para SDA y 25 para SCL, como se muestra a continuación.

Connecting BME280 to ESP32
Conexión del BME280 al ESP32

A continuación está la tabla de conexiones para el BME280 y el ESP32. Puedes usar otros pines para I2C, pero si lo haces, no olvides ajustar el código en la siguiente sección en consecuencia.

BME280 ESP32
SDA33
SCL25
GNDGND
VCC3.3V

Ten en cuenta que existen versiones de la placa breakout BME280 para 5V y 3.3V. Yo uso la versión de 3.3V y por eso conecto VCC al pin de 3.3V del ESP32.

Como el WEMOS LOLIN32 Lite tiene un puerto y cargador de batería incorporados, puedes alimentar todo el sistema con una batería LiPo. La imagen a continuación muestra el cableado completo con una batería LiPo conectada:

SX1276 LoRa Sender with ESP32 and LiPo on breadboard
Emisor LoRa SX1276 con ESP32 y LiPo en protoboard

Medí un consumo en deep sleep de 0.2mA y un consumo de 140mA al transmitir datos. Si usas una 18650 battery, que tiene una capacidad de alrededor de 3300mAh (según la marca específica), probablemente puedas hacer funcionar el sensor durante aproximadamente un año si mides y transmites datos cada minuto.

Código para el receptor LoRa

En esta sección escribimos primero el código para el receptor, ya que es más sencillo. El siguiente código recibe la temperatura, humedad y presión del sensor BME280 enviado por el módulo LoRa SX1276 y muestra la medición en el Monitor Serial:

#include <SPI.h>
#include <LoRa.h>

// WEMOS LOLIN32 Lite
// MOSI ->  23
// MISO ->  19
// SCK  ->  18
#define SS 5
#define RST 17
#define DIO0 4


void initLoRa() {
  LoRa.setPins(SS, RST, DIO0);
  // 433E6: Asia, 868E6: Europe, 915E6: North America
  while (!LoRa.begin(868E6)) {
    Serial.println(".");
    delay(500);
  }
  LoRa.setSyncWord(0xF3);          // 0-0xFF sync word to match the receiver
  LoRa.setSpreadingFactor(12);     // (6-12) higher value increases range but decreases data rate
  LoRa.setSignalBandwidth(125E3);  // lower value increases range but decreases data rate
  LoRa.setCodingRate4(8);          // higher value increases range but decreases data rate
  LoRa.enableCrc();
}

void readLoRaData() {
  static char data[128];
  int i = 0;
  while ((i < 128) && LoRa.available()) {
    data[i++] = LoRa.read();
  }
  data[i] = '\0';
  //Serial.println(data);

  float temp, hum, pres;
  sscanf(data, "%f|%f|%f", &temp, &hum, &pres);

  Serial.printf("%.1fC  %.0f%%  %.0fhPa  (%d)\n",
                temp, hum, pres, LoRa.packetRssi());
}

void setup() {
  Serial.begin(115200);
  initLoRa();
}

void loop() {
  if (LoRa.parsePacket()) {
    readLoRaData();
  }
}

Librerías

Para empezar, el código incluye las librerías necesarias. La librería SPI.h maneja la comunicación entre el ESP32 y el módulo LoRa, mientras que LoRa.h nos ofrece una forma sencilla de interactuar con el módulo SX1276.

#include <SPI.h>
#include <LoRa.h>

Puedes instalar la librería LoRa library de Sandeep Mistry usando el Library Manager en el IDE de Arduino. La imagen a continuación muestra una instalación exitosa de la librería:

LoRa library in LIBRARY MANAGER
Librería LoRa en LIBRARY MANAGER

Constantes

Luego, el código define los pines SPI específicos para el WEMOS LOLIN32 Lite, que es una placa compacta de desarrollo ESP32. Los pines SS, RST y DIO0 son críticos para la comunicación LoRa y deben configurarse correctamente para coincidir con tu cableado.

#define SS 5
#define RST 17
#define DIO0 4

initLoRa

Ahora viene la función initLoRa(). Esta función inicializa el radio LoRa y lo configura para tu región y necesidades de rendimiento. Primero, establece los pines de control usando LoRa.setPins().

LoRa.setPins(SS, RST, DIO0);

Después, el código intenta iniciar el radio LoRa a una frecuencia de 868 MHz, que se usa en Europa. Si la inicialización falla, reintenta cada 500 milisegundos mientras imprime puntos en el Monitor Serial.

while (!LoRa.begin(868E6)) {
  Serial.println(".");
  delay(500);
}

Una vez que el módulo LoRa arranca, se configuran varios parámetros para maximizar el alcance. La palabra de sincronización se establece en 0xF3, lo que ayuda a filtrar paquetes que no son de tu propia red. El factor de dispersión se incrementa a 12 para mayor alcance, y el ancho de banda de señal se reduce a 125 kHz para aumentar aún más el alcance. La tasa de codificación se establece en 4/8, priorizando el alcance sobre la velocidad. Finalmente, se habilita la verificación CRC para asegurar la integridad de los datos.

LoRa.setSyncWord(0xF3);
LoRa.setSpreadingFactor(12);
LoRa.setSignalBandwidth(125E3);
LoRa.setCodingRate4(8);
LoRa.enableCrc();

readLoRaData

La función readLoRaData() se activa cada vez que llega un paquete. Lee los bytes disponibles del módulo LoRa en un buffer de caracteres, asegurándose de no exceder los 128 caracteres. Al terminar, termina la cadena con un carácter nulo.

static char data[128];
int i = 0;
while ((i &lt; 128) &amp;&amp; LoRa.available()) {
  data[i++] = LoRa.read();
}
data[i] = '\0';

Luego, usa sscanf() para extraer tres valores de punto flotante — temperatura, humedad y presión — usando el símbolo pipe (|) como delimitador. Estos valores se imprimen en el Monitor Serial en un formato legible, junto con el RSSI (Indicador de Intensidad de Señal Recibida), que indica la fuerza de la señal recibida.

float temp, hum, pres;
sscanf(data, "%f|%f|%f", &amp;temp, &amp;hum, &amp;pres);

Serial.printf("%.1fC  %.0f%%  %.0fhPa  (%d)\n",
              temp, hum, pres, LoRa.packetRssi());

setup

La función setup() es corta y sencilla. Inicializa la interfaz Serial a 115200 baudios para depuración y llama a initLoRa() para preparar el radio.

void setup() {
  Serial.begin(115200);
  initLoRa();
}

loop

Finalmente, la función loop() revisa continuamente si hay paquetes LoRa entrantes. Cuando detecta un paquete usando LoRa.parsePacket(), llama a readLoRaData() para procesar la información.

void loop() {
  if (LoRa.parsePacket()) {
    readLoRaData();
  }
}

En lugar de imprimir en el Monitor Serial, podrías mostrar los datos ambientales en una pantalla OLED o e-Paper, registrarlos en una tarjeta SD o subirlos a la nube.

Código para el emisor LoRa

Esta sección contiene el código para el emisor. El código complementa al receptor descrito arriba. El emisor mide datos ambientales como temperatura, humedad y presión atmosférica, transmite los datos cada minuto vía LoRa y pone el ESP32 en modo deep-sleep entre transmisiones para conservar la batería:

#include <Wire.h>
#include <SPI.h>
#include <LoRa.h>
#include <Adafruit_BME280.h>

// WEMOS LOLIN32 Lite
// MOSI ->  23
// MISO ->  19
// SCK  ->  18
#define SS 5
#define RST 17
#define DIO0 4

Adafruit_BME280 bme;

void initBMESensor() {
  Wire.begin(33, 25);  // Software I2C for BME280
  bme.begin(0x76, &Wire);
  bme.setSampling(Adafruit_BME280::MODE_FORCED,
                  Adafruit_BME280::SAMPLING_X1,  // temperature
                  Adafruit_BME280::SAMPLING_X1,  // pressure
                  Adafruit_BME280::SAMPLING_X1,  // humidity
                  Adafruit_BME280::FILTER_OFF);
}

void initLoRa() {
  LoRa.setPins(SS, RST, DIO0);
  // 433E6: Asia, 868E6: Europe, 915E6: North America
  while (!LoRa.begin(868E6)) {
    Serial.println(".");
    delay(500);
  }
  LoRa.setSyncWord(0xF3);          // 0-0xFF sync word to match the receiver
  LoRa.setSpreadingFactor(12);     // (6-12) higher value increases range but decreases data rate
  LoRa.setSignalBandwidth(125E3);  // lower value increases range but decreases data rate
  LoRa.setCodingRate4(8);          // higher value increases range but decreases data rate
  LoRa.enableCrc();
}

void sendLoRaData() {
  static char data[128];

  bme.takeForcedMeasurement();
  float temp = bme.readTemperature();
  float hum = bme.readHumidity();
  float pres = bme.readPressure() / 100.0;

  sprintf(data, "%.2f|%.2f|%.2f", temp, hum, pres);
  Serial.print(data);

  LoRa.beginPacket();
  LoRa.print(data);
  LoRa.endPacket();
}

void shutdownPeripherals() {
  LoRa.sleep();

  Wire.end();
  pinMode(33, INPUT);
  pinMode(25, INPUT);

  SPI.end();
  pinMode(23, INPUT); // MOSI
  pinMode(19, INPUT); // MISO
  pinMode(18, INPUT); // SCK
  pinMode(SS, INPUT);  // SS
  pinMode(RST, INPUT); // RST
  pinMode(DIO0, INPUT);  // DIO0
}

void setup() {
  Serial.begin(115200);
  initBMESensor();
  initLoRa();
  sendLoRaData();
  shutdownPeripherals();
  esp_sleep_enable_timer_wakeup(60 * 1000 * 1000);  // 1 min
  esp_deep_sleep_start();
}

void loop() {
}

Librerías

Primero, el código incluye librerías tanto para la comunicación LoRa como para el acceso al sensor BME280. Aunque ya hemos hablado de SPI.h y LoRa.h, este sketch también usa Wire.h para comunicación I2C y Adafruit_BME280.h, que proporciona una interfaz de alto nivel para el sensor.

#include <Wire.h>
#include <SPI.h>
#include <LoRa.h>
#include <Adafruit_BME280.h>

Puedes instalar la librería Adafruit_BME280 vía Library Manager y tras una instalación exitosa debería verse así:

Adafruit_BME280 library in Library Manager
Librería Adafruit_BME280 en Library Manager

Objetos

El sensor BME280 se instancia usando el driver de Adafruit. Este objeto, bme, manejará todas las lecturas.

Adafruit_BME280 bme;

initBMESensor

La función initBMESensor() inicializa el BME280 usando I2C por software. En lugar de los pines I2C por defecto del ESP32, especifica GPIO 33 para SDA y GPIO 25 para SCL, lo que puede ser útil cuando el bus I2C por defecto está en uso en otro lugar.

Wire.begin(33, 25);  // Software I2C for BME280
bme.begin(0x76, &Wire);

Ten en cuenta que uso la dirección I2C 0x76 al configurar el sensor vía bme.begin(0x76, &Wire). Tu sensor puede tener una dirección I2C diferente. Por ejemplo, 0x77 también es común para el BME280.

El sensor se configura en modo forzado, lo que significa que solo mide cuando se le indica explícitamente. Este enfoque ahorra energía. El muestreo se establece al mínimo (1x) para temperatura, presión y humedad, y el filtro interno está desactivado.

bme.setSampling(Adafruit_BME280::MODE_FORCED,
                Adafruit_BME280::SAMPLING_X1,
                Adafruit_BME280::SAMPLING_X1,
                Adafruit_BME280::SAMPLING_X1,
                Adafruit_BME280::FILTER_OFF);

initLora

La función initLoRa() es idéntica al sketch del receptor y configura el módulo LoRa para comunicación de largo alcance y baja tasa de bits. Si necesitas un repaso de cómo funciona, sube a la explicación anterior.

sendLoRaData

Ahora el corazón del sketch: sendLoRaData(). Esta función lee los últimos valores ambientales del BME280 y los envía por LoRa. Primero, fuerza al sensor a tomar una nueva medición.

bme.takeForcedMeasurement();

Luego lee los valores de temperatura, humedad y presión. La presión se divide por 100 para convertir de Pascales a hPa, coincidiendo con el formato esperado por el receptor.

float temp = bme.readTemperature();
float hum = bme.readHumidity();
float pres = bme.readPressure() / 100.0;

Los datos del sensor se formatean en una sola cadena, usando el carácter | como delimitador. Esto coincide con el formato esperado en el receptor:

sprintf(data, "%.2f|%.2f|%.2f", temp, hum, pres);
Serial.print(data);

Una cadena típica se ve así: 25.59|54.93|1001.23. En lugar de enviar una cadena, también podríamos enviar los datos como bytes. Cada float tiene 4 bytes y enviar 3×4 = 12 bytes reduciría la cantidad de datos a enviar. Por otro lado, añadir más datos y desempaquetarlos en el receptor sería más complejo.

Luego, el código inicia un paquete LoRa, escribe los datos y cierra el paquete para transmitirlo.

LoRa.beginPacket();
LoRa.print(data);
LoRa.endPacket();

shutdownPeripherals

Una vez enviados los datos, la función shutdownPeripherals() apaga todo lo que no se necesita. Pone el módulo LoRa en modo suspensión, termina los buses I2C y SPI, y reconfigura todos los pines GPIO asociados como entradas para evitar consumo innecesario.

LoRa.sleep();

Wire.end();
pinMode(33, INPUT);
pinMode(25, INPUT);

SPI.end();
pinMode(23, INPUT);
pinMode(19, INPUT);
pinMode(18, INPUT);
pinMode(SS, INPUT);
pinMode(RST, INPUT);
pinMode(DIO0, INPUT);

setup

La función setup() une todo. Inicializa la interfaz Serial, el sensor BME280 y el módulo LoRa. Luego envía los datos y apaga todo. Finalmente, configura el ESP32 para despertarse del deep sleep tras 60 segundos usando un temporizador, y entra en modo deep sleep inmediatamente.

esp_sleep_enable_timer_wakeup(60 * 1000 * 1000);  // 1 min
esp_deep_sleep_start();

loop

La función loop() queda vacía porque el ESP32 nunca la alcanza. Después de enviar datos, el microcontrolador duerme hasta que se despierta para repetir el ciclo.

void loop() {
}

Y eso es todo. Con el código para el emisor y receptor tienes un sistema para medir datos ambientales con un sensor que está a kilómetros de distancia y puede funcionar mucho tiempo con batería.

Conclusiones

En este tutorial aprendiste cómo medir datos ambientales como temperatura, humedad y presión atmosférica con un sensor BME280 y cómo transmitir estos datos a largas distancias usando LoRa y el módulo transceptor SX1276.

A diferencia de WiFi o Bluetooth, LoRa te permite enviar datos desde sensores que están a kilómetros de distancia y necesitan funcionar con batería durante mucho tiempo. No dediqué mucho tiempo a minimizar el consumo de batería y el siguiente BME280 Sleep Mode video puede tener información adicional útil.

Si transmites datos solo entre unos pocos sensores, LoRa en bruto está bien. Pero si tienes muchos sensores que gestionar y quieres enviar mediciones a internet, LoRaWAN es una mejor opción. Consulta el LoRaWAN with Thinknode G1 Gateway tutorial para más información sobre este tema.

Imprimimos las mediciones en el Monitor Serial, pero podrías extender fácilmente el código para mostrar los datos en una pantalla. Echa un vistazo a los tutoriales Weather Station on e-Paper Display y How to configure TFT_eSPI Library for TFT display.

Si también quieres mostrar datos meteorológicos de internet o la hora actual, te sugiero los tutoriales Simple ESP32 Internet Weather Station y Automatic Daylight Savings Time Clock.

Si tienes alguna pregunta, no dudes en dejarla en la sección de comentarios.

¡Feliz bricolaje ; )