Skip to Content

Enviar Dados Ambientais com LoRa

Enviar Dados Ambientais com LoRa

Neste tutorial, vais aprender como enviar dados ambientais com LoRa. O LoRa permite transmitir dados de sensores a longas distâncias (vários quilómetros) consumindo muito pouca energia. Vamos usar isto para transmitir dados de Temperatura, Humidade e Pressão do Ar medidos por um BME280 entre dois ESP32 ligados a módulos LoRa SX1276.

Se não estás familiarizado com LoRa, sugiro que leias primeiro o nosso Long range communication with LoRa SX1276 and ESP32 tutorial, pois fornece a informação básica necessária para este tutorial.

Peças Necessárias

Abaixo encontras os componentes necessários. Usei uma placa ESP32 mais antiga, que já está descontinuada, mas ainda podes encontrá-la a um preço muito baixo. No entanto, qualquer outro ESP32 também funciona bem. Só precisas de dois deles e dois módulos SX1276 – um para enviar e outro para receber.

Quanto aos módulos transceptores LoRa SX1276, atenção à versão que compras! Dependendo do país, as frequências permitidas são diferentes (link). Em Europa é 868MHz, na América do Norte 915MHz e na Ásia 433MHz.

A descrição do módulo indica a frequência ou tem um número como 868 ou 915 no nome. Listei um módulo de 868MHz, pois estou na Europa. Mas podes também obter este módulo para a banda de 915MHz.

Para o sensor ambiental, escolhi o BME280, pois tem um formato pequeno, modo de suspensão e pode medir Temperatura, Humidade, Pressão do Ar e Altitude. Nota que existem versões do BME280 para 5V e 3.3V. Certifica-te de comprar a versão 3.3V! Se precisares apenas de Temperatura e Humidade, o Si7021 é uma boa alternativa.

2 x Módulo LoRa 868/915M SX1276

ESP32 lite Lolin32

2 x ESP32 lite

BME280

Sensor BME280

USB data cable

Cabo de Dados USB

Dupont wire set

Conjunto de Fios Dupont

Half_breadboard56a

2x Breadboard

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

O SX1276 é um transceptor LoRa fabricado pela Semtech. A maioria dos módulos (breakout boards) baseados neste CI são configurados para 433 MHz (Ásia), 868 MHz (Europa) ou 915 MHz (América do Norte), dependendo da tua região. No verso do módulo geralmente encontras uma pequena tabela onde a frequência de operação está marcada. A imagem abaixo mostra um módulo que opera na banda de 915 MHz e que pode ser usado na América do Norte:

Back of SX1276 LoRa Module
Verso do Módulo LoRa SX1276

Certifica-te de usar um módulo com a frequência de transmissão correta para o teu país! O módulo pode comunicar a distâncias de 2 a mais de 15 quilómetros, dependendo da qualidade da antena, terreno e configurações. O SX1276 suporta taxas de dados de 0.018 kbps a 37.5 kbps. Para mais detalhes técnicos, vê a folha de dados abaixo:

O módulo tem 16 pinos. Um para a Antena (ANT), um para alimentação (VCC), vários pinos de terra (GND), os pinos para SPI (REST, NSS, SCK, MOSI, MISO) e seis pinos configuráveis Digital IO (DIO).

Nota que o SX1276 usa lógica de 3.3V e por isso não podes ligá-lo diretamente a um Arduino UNO, que opera com níveis lógicos de 5V. Se quiseres usar um Arduino em vez de um ESP32, tens de usar um logic level shifter ou uma placa Arduino que use 3.3V logic.

Sensor BME280

Para medir dados ambientais como temperatura, humidade e pressão do ar, vamos usar o BME280 sensor.

Uma das razões é que tem um modo de baixo consumo, sleep mode, onde consome apenas 0.1µA (3.6 μA em modo normal). Em combinação com um módulo LoRa e o ESP32-lite com corrente de deep-sleep de 5.65mA (a 5V), isto cria um sistema de muito baixo consumo que pode funcionar muito tempo com bateria.

O sensor BME280 é pequeno e normalmente vem numa breakout board com interface I2C; vê a imagem abaixo.

BME280 breakout board
Breakout board BME280

O sensor pode medir pressão de 300 hPa a 1100 hPa, temperatura de -40°C a +85°C, e humidade de 0% a 100%. Para mais informações sobre o BME280, lê o nosso tutorial sobre How To Use BME280 Pressure Sensor With Arduino ou o Weather Station on e-Paper Display tutorial.

Ligação do SX1276 ao ESP32

O SX1276 é controlado via interface SPI. Ao ligá-lo a um ESP32, temos de fazer as seguintes conexões:

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

Certifica-te de ligar o VCC ao pino de saída 3.3V do teu ESP32. Na verdade, não usamos o DIO0, por isso podes deixar essa ligação de fora. O DIO0 é necessário se quiseres adicionar um handler de interrupção que é chamado quando uma medição está completa. A imagem abaixo mostra o diagrama completo de ligações (incluindo DIO0):

Connecting SX1276 to ESP32
Ligação do SX1276 ao ESP32

Se estiveres a usar uma placa ESP32 diferente, os pinos para SPI hardware, que a ligação acima usa, podem ser diferentes. Se não tiveres a certeza de quais pinos usar na tua placa, vê o Find I2C and SPI default pins tutorial.

A foto abaixo mostra a ligação do receptor SX1276 LoRa com o ESP32 numa breadboard:

SX1276 LoRa Receiver with the ESP32 on a breadboard
Receptor SX1276 LoRa com ESP32 numa breadboard

Nota que o módulo SX1276 tem espaçamento de pinos de 2mm e não encaixa diretamente numa breadboard. Vê o Long range communication with LoRa SX1276 and ESP32 tutorial para saber como construir uma versão do módulo compatível com breadboard.

Ligação do BME280 ao ESP32

Queremos medir temperatura ambiente, humidade e pressão do ar usando o sensor BME280. Como os pinos I2C/SPI hardware do ESP32 já estão ligados ao SX1276, usamos I2C por software com os pinos livres 33 para SDA e 25 para SCL, como mostrado abaixo.

Connecting BME280 to ESP32
Ligação do BME280 ao ESP32

Abaixo está a tabela de ligação do BME280 ao ESP32. Podes usar pinos diferentes para I2C, mas se o fizeres, não te esqueças de ajustar o código na próxima secção em conformidade.

BME280 ESP32
SDA33
SCL25
GNDGND
VCC3.3V

Nota que existem versões do BME280 para 5V e 3.3V. Estou a usar a versão 3.3V e por isso ligo o VCC ao pino 3.3V do ESP32.

Como o WEMOS LOLIN32 Lite tem uma porta de bateria e carregador integrados, podes alimentar todo o sistema com uma bateria LiPo. A imagem abaixo mostra a ligação completa com uma bateria LiPo ligada:

SX1276 LoRa Sender with ESP32 and LiPo on breadboard
Transmissor SX1276 LoRa com ESP32 e LiPo numa breadboard

Medi uma corrente de deep sleep de 0.2mA e uma corrente de 140mA ao transmitir dados. Se usares uma 18650 battery, que tem uma capacidade de cerca de 3300mA (dependendo da marca), provavelmente podes fazer o sensor funcionar cerca de um ano se medires e transmitires dados a cada minuto.

Código para Receptor LoRa

Nesta secção escrevemos primeiro o código para o lado do receptor, pois é mais simples. O código seguinte recebe a temperatura, humidade e pressão do sensor BME280 enviados pelo módulo LoRa SX1276 e mostra a medição no 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();
  }
}

Bibliotecas

Para começar, o código inclui as bibliotecas necessárias. A biblioteca SPI.h trata da comunicação entre o ESP32 e o módulo LoRa, enquanto LoRa.h oferece uma forma fácil de interagir com o módulo SX1276.

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

Podes instalar a LoRa library de Sandeep Mistry usando o Library Manager no Arduino IDE. A imagem abaixo mostra a instalação bem-sucedida da biblioteca:

LoRa library in LIBRARY MANAGER
Biblioteca LoRa no LIBRARY MANAGER

Constantes

De seguida, o código define os pinos SPI específicos para o WEMOS LOLIN32 Lite, que é uma placa compacta de desenvolvimento ESP32. Os pinos SS, RST e DIO0 são críticos para a comunicação LoRa e devem ser configurados corretamente para corresponder à tua ligação.

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

initLoRa

Agora vem a função initLoRa(). Esta função inicializa o rádio LoRa e configura-o para a tua região e necessidades de desempenho. Primeiro, define os pinos de controlo usando LoRa.setPins().

LoRa.setPins(SS, RST, DIO0);

Depois, o código tenta iniciar o rádio LoRa na frequência de 868 MHz, usada na Europa. Se a inicialização falhar, tenta novamente a cada 500 milissegundos, imprimindo pontos no Monitor Serial.

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

Quando o módulo LoRa inicia, vários parâmetros são configurados para maximizar o alcance. A palavra de sincronização é definida para 0xF3, o que ajuda a filtrar pacotes que não são da tua rede. O fator de espalhamento é aumentado para 12 para maior alcance, e a largura de banda do sinal é reduzida para 125 kHz para aumentar ainda mais o alcance. A taxa de codificação é definida para 4/8, priorizando alcance em vez de velocidade. Finalmente, a verificação CRC é ativada para garantir a integridade dos dados.

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

readLoRaData

A função readLoRaData() é acionada sempre que chega um pacote. Lê os bytes disponíveis do módulo LoRa para um buffer de caracteres, garantindo que não ultrapassa 128 caracteres. Quando termina, adiciona um caractere nulo para terminar a string.

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

De seguida, usa sscanf() para extrair três valores de ponto flutuante — temperatura, humidade e pressão — usando o símbolo pipe (|) como delimitador. Estes valores são depois impressos no Monitor Serial num formato legível, juntamente com o RSSI (Indicador de Força do Sinal Recebido), que indica a força do sinal recebido.

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

A função setup() é curta e simples. Inicializa a interface Serial a 115200 baud para depuração e chama initLoRa() para preparar o rádio.

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

loop

Finalmente, a função loop() verifica continuamente se há pacotes LoRa a chegar. Quando detecta um pacote usando LoRa.parsePacket(), chama readLoRaData() para processar a informação.

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

Em vez de imprimir no Monitor Serial, poderias mostrar os dados ambientais num ecrã OLED ou e-Paper, gravar num cartão SD ou enviar para a cloud.

Código para Transmissor LoRa

Esta secção contém o código para o transmissor. O código complementa o receptor descrito acima. O transmissor mede dados ambientais como temperatura, humidade e pressão do ar, transmite os dados a cada minuto via LoRa e coloca o ESP32 em modo deep-sleep entre transmissões para poupar bateria:

#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() {
}

Bibliotecas

Primeiro, o código inclui bibliotecas para comunicação LoRa e acesso ao sensor BME280. Embora já tenhamos falado do SPI.h e do LoRa.h, este sketch também usa Wire.h para comunicação I2C e Adafruit_BME280.h, que fornece uma interface de alto nível para o sensor.

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

Podes instalar a biblioteca Adafruit_BME280 via Library Manager e, após instalação bem-sucedida, deverá parecer assim:

Adafruit_BME280 library in Library Manager
Biblioteca Adafruit_BME280 no Library Manager

Objetos

O sensor BME280 é instanciado usando o driver da Adafruit. Este objeto, bme, vai tratar todas as leituras.

Adafruit_BME280 bme;

initBMESensor

A função initBMESensor() inicializa o BME280 usando I2C por software. Em vez dos pinos I2C padrão do ESP32, especifica o GPIO 33 para SDA e o GPIO 25 para SCL, o que pode ser útil quando o bus I2C padrão está a ser usado noutro lugar.

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

Nota que estou a usar o endereço I2C 0x76 ao configurar o sensor via bme.begin(0x76, &Wire). O teu sensor pode ter um endereço I2C diferente. Por exemplo, 0x77 também é comum para o BME280.

O sensor está configurado em modo forçado, o que significa que só mede quando explicitamente mandado. Esta abordagem poupa energia. A amostragem está definida para o mínimo (1x) para temperatura, pressão e humidade, e o filtro interno está desativado.

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

initLora

A função initLoRa() é idêntica ao sketch do receptor e configura o módulo LoRa para comunicação de longo alcance e baixa taxa de bits. Se precisares de relembrar como funciona, vê a explicação anterior.

sendLoRaData

Agora o coração do sketch: sendLoRaData(). Esta função lê os valores ambientais mais recentes do BME280 e envia-os via LoRa. Primeiro, força o sensor a fazer uma nova medição.

bme.takeForcedMeasurement();

Depois lê os valores de temperatura, humidade e pressão. A pressão é dividida por 100 para converter de Pascal para hPa, correspondendo ao formato esperado pelo receptor.

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

Os dados do sensor são então formatados numa única string, usando o caractere | como delimitador. Isto corresponde ao formato esperado no lado do receptor:

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

Uma string típica é assim: 25.59|54.93|1001.23. Em vez de enviar uma string, poderíamos também enviar os dados em bytes. Cada float tem 4 bytes e enviar 3×4 = 12 bytes reduziria a quantidade de dados a enviar. Por outro lado, adicionar mais dados e desempacotar no receptor seria mais complexo.

De seguida, o código inicia um pacote LoRa, escreve os dados e fecha o pacote para transmitir.

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

shutdownPeripherals

Depois de enviar os dados, a função shutdownPeripherals() desliga tudo o que não é necessário. Coloca o módulo LoRa em modo sleep, termina os buses I2C e SPI, e reconfigura todos os pinos GPIO associados como entradas para evitar consumo desnecessário.

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

A função setup() junta tudo. Inicializa a interface Serial, o sensor BME280 e o módulo LoRa. Depois envia os dados e desliga tudo. Finalmente, configura o ESP32 para acordar do deep sleep após 60 segundos usando um timer wakeup, e entra imediatamente em deep sleep.

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

loop

A função loop() fica vazia porque o ESP32 nunca a alcança. Depois de enviar os dados, o microcontrolador dorme até acordar para repetir o ciclo.

void loop() {
}

E é isso. Com o código para transmissor e receptor tens um sistema para medir dados ambientais com um sensor a quilómetros de distância e que pode funcionar muito tempo com bateria.

Conclusões

Neste tutorial aprendeste a medir dados ambientais como temperatura, humidade e pressão do ar com um sensor BME280 e a transmitir esses dados a longas distâncias usando LoRa e o módulo transceptor SX1276.

Ao contrário do WiFi ou Bluetooth, o LoRa permite enviar dados de sensores que estão a quilómetros de distância e precisam de funcionar com bateria durante muito tempo. Não me foquei muito em minimizar o consumo da bateria e o seguinte vídeo BME280 Sleep Mode pode ter informações adicionais úteis.

Se transmitires dados apenas entre alguns sensores, o LoRa puro é suficiente. Mas se tiveres muitos sensores para gerir e quiseres enviar medições para a internet, o LoRaWAN é uma melhor escolha. Vê o tutorial LoRaWAN with Thinknode G1 Gateway para mais informações sobre este tema.

Imprimimos as medições no Monitor Serial, mas podes facilmente estender o código para mostrar os dados num ecrã. Vê os tutoriais Weather Station on e-Paper Display e How to configure TFT_eSPI Library for TFT display.

Se quiseres também mostrar dados meteorológicos da internet ou a hora atual, sugiro os tutoriais Simple ESP32 Internet Weather Station e Automatic Daylight Savings Time Clock.

Se tiveres alguma dúvida, sente-te à vontade para deixar nos comentários.

Boas experiências ; )