Por conta de inúmeras
perguntas que recebi sobre como funciona o envio de dados não só de um LoRa
para outro, mas também para um servidor, hoje vamos tratar de duas coisas que,
na realidade, já mostramos anteriormente, mas em vídeos separados. Ou seja, vou
fazer um LoRa, o EndPoint, enviar para um outro LoRa, o Gateway, dados de um
sensor BME280 sobre temperatura, umidade e pressão. Esse “cara” que recebe as
informações vai enviar tudo isso para o IBM Watson através do protocolo MQTT.
Pessoal, para melhor
compreensão, sugiro que vocês assistam esses dois vídeos que tratam deste
assunto:
Quer comprar suas placas?
ESP32 neste link: https://bit.ly/2JH0dXN
ESP32 LoRa https://bit.ly/2yuZ2Gm
(Não fica de "miserê". Compra logo 2 de cada porque se seu componente queimar, você vai ficar na mão!)
Comunicação
Mostro aqui o esquema do nosso
projeto.
Quero destacar uma coisa que
considero importante: quando o EndPoint envia dados para o Gateway, não é por
TCP-IP e nem LoRaWAN, mas, sim, por LoRa, ou seja, o protocolo radio LoRa.
Gráfico
Esta aqui é a tela gráfica do
IBM Watson. Você acompanha as variações em casas decimais. Isso de forma
prática e rápida de trabalhar, sem necessidade de fazer ou programar qualquer
tipo de gráfico.
BME280
Deixo aqui também o pinout do
BME280. Gostei bastante dele por ele ser i2c.
Montagem
Demonstração
No vídeo você por ver uma
demonstração do circuito funcionando. Posso dizer que esse é um exemplo bem
completo de IOT (Internet of Things).
Biblioteca BME280
Na IDE do Arduino vá em
Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Procure por bme280 e instale
Adafruit BME280 Library
Biblioteca Adafruit Unified Sensor
Na IDE do Arduino vá em
Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Procure por adafruit sensor
based libraries e instale Adafruit Unified Sensor
Biblioteca PubSubClient
Na IDE do Arduino vá em
Sketch->Incluir Biblioteca->Gerenciar Bibliotecas...
Procure por pubsubclient e
instale PubSubClient
Modificar pinagem i2c do WiFi LoRa 32
Abra o arquivo:
C:\Users\<SEU_USUARIO>\Documents\Arduino\hardware\heltec\esp32\variants\wifi_lora_32\pins_arduino.h
e altere os pinos SDA para 4 e o pino SCL para 15
Código fonte
Temos várias partes do código
fonte que já expliquei, ou melhor, dos dois códigos que temos aqui, já que
temos o Master e o Slave. Nosso foco principal deste vídeo, portanto, será no
Master, que, no caso, é o roteador, na parte que tratamos do Master.ino – Receive.
De qualquer forma, vou expor
aqui neste artigo todas as partes do código novamente, para facilitar sua visualização.
LoRaSendReceiveBME280MQTT.ino
De início, vamos incluir as
bibliotecas e definir os destinos dos GPIOs, além da frequência do rádio.
Ainda, vamos criar constantes para informar ao Slave sobre os trabalhos com os
dados, bem como o retorno destes para o Master. Estruturamos ainda os dados do
sensor e apontamos a variável para controlar o display.
#include <SPI.h> #include <LoRa.h> #include <Wire.h> #include <SSD1306.h> //Deixe esta linha descomentada para compilar o Master //Comente ou remova para compilar o Slave #define MASTER #define RST 14 // GPIO14 RESET #define DI00 26 // GPIO26 IRQ(Interrupt Request) #define BAND 433E6 //Frequência do radio - exemplo : 433E6, 868E6, 915E6 //Constante para informar ao Slave que queremos os dados const String GETDATA = "get"; //Constante que o Slave retorna junto com os dados para o Master const String SETDATA = "set"; //Estrutura com os dados do sensor typedef struct { float temperature; float pressure; float humidity; }Data; //Variável para controlar o display SSD1306 display(0x3c, SDA, SCL);
LoRaSendReceiveBME280MQTT.ino – setupDisplay
Neste primeiro Setup, vamos
atuar na configuração do display.
void setupDisplay(){ //O estado do GPIO16 é utilizado para controlar o display OLED pinMode(16, OUTPUT); //Reseta as configurações do display OLED digitalWrite(16, LOW); //Para o OLED permanecer ligado, o GPIO16 deve permanecer HIGH //Deve estar em HIGH antes de chamar o display.init() e fazer as demais configurações, //não inverta a ordem digitalWrite(16, HIGH); //Configurações do display display.init(); display.flipScreenVertically(); display.setFont(ArialMT_Plain_16); display.setTextAlignment(TEXT_ALIGN_LEFT); }
LoRaSendReceiveBME280MQTT.ino – setupLoRa
Aqui, vamos tratar das
configurações iniciais do LoRa.
//Configurações iniciais do LoRa void setupLoRa(){ //Inicializa a comunicação SPI.begin(SCK, MISO, MOSI, SS); LoRa.setPins(SS, RST, DI00); //Inicializa o LoRa if (!LoRa.begin(BAND, true)){ //Se não conseguiu inicializar, mostra uma mensagem no display display.clear(); display.drawString(0, 0, "Erro ao inicializar o LoRa!"); display.display(); while (1); } //Ativa o crc LoRa.enableCrc(); //Ativa o recebimento de pacotes LoRa.receive(); }
Master.ino
Nesta etapa, compila apenas se
MASTER estiver definido no arquivo principal. Incluímos as bibliotecas
PubSubCliente.h e WiFi.h, substituímos pelo SSID da própria rede, assim como
senha e Server MQTT que iremos utilizar. Ainda, damos um nome ao tópico que
devemos enviar os dados para que eles apareçam nos gráficos. Apontamos o ID que
usaremos para conectar. Por fim, o QUICK_STAR deve permanecer como está.
//Compila apenas se MASTER estiver definido no arquivo principal #ifdef MASTER #include <PubSubClient.h> #include <WiFi.h> //Substitua pelo SSID da sua rede #define SSID "TesteESP" //Substitua pela senha da sua rede #define PASSWORD "87654321" //Server MQTT que iremos utlizar #define MQTT_SERVER "quickstart.messaging.internetofthings.ibmcloud.com" //Nome do tópico que devemos enviar os dados //para que eles apareçam nos gráficos #define TOPIC_NAME "iot-2/evt/status/fmt/json" //ID que usaremos para conectar //QUICK_START deve permanecer como está const String QUICK_START = "d:quickstart:arduino:";
No DEVICE_ID mudamos para um
id único. Nesse exemplo utilizamos o MAC Address do dispositivo que estamos
utilizando. Servirá como identificação no site https://quickstart.internetofthings.ibmcloud.com.
//No DEVICE_ID você deve mudar para um id único //Aqui nesse exemplo utilizamos o MAC Address //do dispositivo que estamos utilizando //Servirá como identificação no site //https://quickstart.internetofthings.ibmcloud.com const String DEVICE_ID = "241ab91e0fa0"; //Concatemos o id do quickstart com o id do nosso //dispositivo const String CLIENT_ID = QUICK_START + DEVICE_ID; //Cliente WiFi que o MQTT irá utilizar para se conectar WiFiClient wifiClient; //Cliente MQTT, passamos a url do server, a porta //e o cliente WiFi PubSubClient client(MQTT_SERVER, 1883, wifiClient);
Definimos intervalos entre os
envios, variáveis para guardarmos os valores de temperatura, umidade e pressão,
assim como o tempo do último envio e onde ficam os dados que chegam do outro
dispositivo LoRa.
//Intervalo entre os envios #define INTERVAL 500 //Tempo do último envio long lastSendTime = 0; //Onde ficam os dados que chegam do outro dispositivo LoRa Data data;
Master.ino - setup
Fazemos nesta parte as
configurações que envolvem o Master, chamando as configurações iniciais do
display e do LoRa. Conectamos ainda à rede WiFi.
void setup(){ Serial.begin(115200); //Chama a configuração inicial do display setupDisplay(); //Chama a configuração inicial do LoRa setupLoRa(); display.clear(); display.drawString(0, 0, "Master"); display.display(); //Conectamos à rede WiFi setupWiFi(); connectMQTTServer(); }
Master.ino – connectMQTTServer
Temos aqui a função
responsável por conectar ao server MQTT.
//Função responsável por conectar ao server MQTT void connectMQTTServer() { Serial.println("Connecting to MQTT Server..."); //Se conecta ao id que definimos if (client.connect(CLIENT_ID.c_str())) { //Se a conexão foi bem sucedida Serial.println("connected"); } else { //Se ocorreu algum erro Serial.print("error = "); Serial.println(client.state()); } }
Master.ino – setupWiFi
Já nesta etapa trabalhamos com
a Função responsável por conectar a rede WiFi.
//Função responsável por conectar à rede WiFi void setupWiFi() { Serial.println(); Serial.print("Connecting to "); Serial.print(SSID); //Manda o esp se conectar à rede através //do ssid e senha WiFi.begin(SSID, PASSWORD); //Espera até que a conexão com a rede seja estabelecida while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } //Se chegou aqui é porque conectou Serial.println(""); Serial.println("WiFi connected"); }
Master.ino – loop
Neste Loop, definimos o envio
do pacote para informar ao Slave o desejo de receber dados e ainda verificamos
se existem pacotes a serem recebidos.
void loop(){ //Se passou o tempo definido em INTERVAL desde o último envio if (millis() - lastSendTime > INTERVAL){ //Marcamos o tempo que ocorreu o último envio lastSendTime = millis(); //Envia o pacote para informar ao Slave que queremos receber os dados send(); } //Verificamos se há pacotes para recebermos receive(); }
Master.ino – send
Inicializamos o pacote e
enviamos o que está contido em “GETDATA”, para posteriormente finalizar e
enviar o pacote.
void send(){ //Inicializa o pacote LoRa.beginPacket(); //Envia o que está contido em "GETDATA" LoRa.print(GETDATA); //Finaliza e envia o pacote LoRa.endPacket(); }
Master.ino – receive
Nesta etapa, verificamos se o
pacote tem o tamanho mínimo de caracteres que esperamos e armazenamos uma
string. Ainda analisamos se o cabeçalho é o que esperamos, partimos para a
leitura dos dados e os exibimos no display.
Criamos, então, o json que
enviaremos para o server MQTT. Publicamos no tópico onde o servidor espera para
receber e gerar o gráfico.
void receive(){ //Tentamos ler o pacote int packetSize = LoRa.parsePacket(); //Verificamos se o pacote tem o tamanho mínimo de caracteres que esperamos if (packetSize > SETDATA.length()){ String received = ""; //Armazena os dados do pacote em uma string for(int i=0; i<SETDATA.length(); i++){ received += (char) LoRa.read(); } //Se o cabeçalho é o que esperamos if(received.equals(SETDATA)){ //Fazemos a leitura dos dados LoRa.readBytes((uint8_t*)&data, sizeof(data)); //Mostramos os dados no display showData(); Serial.print("Publish message: "); //Criamos o json que enviaremos para o server mqtt String msg = createJsonString(); Serial.println(msg); //Publicamos no tópico onde o servidor espera para receber //e gerar o gráfico client.publish(TOPIC_NAME, msg.c_str()); } } }
Master.ino – showData
Por fim, mostramos o tempo que
o Master levou para criar o pacote, enviar, o Slave receber, fazer a leitura,
criar um novo pacote e enviá-lo ao Master, que o recebe e faz a leitura. Este é
impresso no display.
void showData(){ //Tempo que demorou para o Master criar o pacote, enviar o pacote, //o Slave receber, fazer a leitura, criar um novo pacote, enviá-lo //e o Master receber e ler String waiting = String(millis() - lastSendTime); //Mostra no display os dados e o tempo que a operação demorou display.clear(); display.drawString(0, 0, String(data.temperature) + " C"); display.drawString(0, 16, String(data.pressure) + " Pa"); display.drawString(0, 32, String(data.humidity) + "%"); display.drawString(0, 48, waiting + " ms"); display.display(); }
Master.ino – createJsonString
Temos aqui a função
responsável por criar um Json com os dados lidos.
//Função responsável por criar //um Json com os dados lidos String createJsonString() { String json = "{"; json+= "\"d\": {"; json+="\"temperature\":"; json+=String(data.temperature); json+=","; json+="\"humidity\":"; json+=String(data.humidity); json+=","; json+="\"pressure\":"; json+=String(data.pressure); json+="}"; json+="}"; return json; } #endif
Slave.ino
Iniciando o código do Slave,
compilamos apenas se o Master não estiver definido no arquivo principal. Incluímos as bibliotecas e apontamos a
responsável pela leitura da temperatura, pressão e umidade.
//Compila apenas se MASTER não estiver definido no arquivo principal #ifndef MASTER #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> //Responsável pela leitura da temperatura, pressão e umidade Adafruit_BME280 bme;
Slave.ino – setup
No Setup do Slave, chamamos as
configurações iniciais do display e do LoRa.
void setup(){ Serial.begin(115200); //Chama a configuração inicial do display setupDisplay(); //Chama a configuração inicial do LoRa setupLoRa(); //0x76 se pino SDO do sensor estiver no GND //0x77 se pino SDO do sensor estiver no 3.3v if (!bme.begin(0x76)) { display.clear(); display.drawString(0, 0, "Sensor não encontrado"); display.display(); while(1); } display.clear(); display.drawString(0, 0, "Slave esperando..."); display.display(); }
Slave.ino – loop
Assim como no Master, nesta
parte do Loop do Slave, verificamos se o mesmo tem a quantidade de caracteres
esperada e armazenamos os dados em uma String. Ainda, simulamos a leitura dos
dados. Criamos o pacote, finalizamos e enviamos. Por fim, exibimos as
informações no display.
void loop(){ //Tenta ler o pacote int packetSize = LoRa.parsePacket(); //Verifica se o pacote possui a quantidade de caracteres que esperamos if (packetSize == GETDATA.length()) { String received = ""; //Armazena os dados do pacote em uma string while(LoRa.available()) { received += (char) LoRa.read(); } if(received.equals(GETDATA)) { //Faz a leitura dos dados Data data = readData(); Serial.println("Criando pacote para envio"); //Cria o pacote para envio LoRa.beginPacket(); LoRa.print(SETDATA); LoRa.write((uint8_t*)&data, sizeof(data)); //Finaliza e envia o pacote LoRa.endPacket(); showSentData(data); } } }
Slave.ino – showSentData
Enfim, exibidos os dados no
display.
void showSentData(Data data) { //Mostra no display display.clear(); display.drawString(0, 0, "Enviou:"); display.drawString(0, 16, String(data.temperature) + " C"); display.drawString(0, 32, String(data.pressure) + " Pa"); display.drawString(0, 48, String(data.humidity) + "%"); display.display(); } #endif
Gráfico
Para visualizar o gráfico do
sensor vá até https://quickstart.internetofthings.ibmcloud.com
No campo Device id digite o
DEVICE_ID que você definiu no código.
É importante mudar no código
este devido id para um id única, utilizado somente por você,
para não dar conflito com
dados enviados por outra pessoa.
Aceite os termos e clique em
Go.
8 Comentários
Olá Fernando. Excelente trabalho, didática sensacional, parabéns ! Amigo, preciso de uma ajuda: nas minhas aplicações eu não tenho um WiFi disponível, trabalho em locais remotos, porém com cobertura GSM. Estou idealizando um gateway com o chip SIM800L operando em GMS/GPRS. Preciso de conectá-lo ao IMB Watson via GPRS. POde me ajudar indicando algum material de estudo ? Desde já agradeço.
ResponderExcluirFernando
ResponderExcluirDificil traduzir parangolé.
Mas excelente como sempre seu video tutorial. Espero aproveitar o video para um produto que estamos desenvolvendo. Muito obrigado mais uma vez.
Olá Fernando.
ResponderExcluirÉ possível usar o mesmo sistema com um padrão de WI-FI diferente do 802.11?
É que não sei qual é o padrão usado na minha região e receio comprar o material e não funcionar posteriormente.
Boa tarde Fernando!
ResponderExcluirEstou adquirindo dois TTGO T-Beam ESP32 433 com GPS onboard e um TTGO T-Deer ESP32 para usar como Gateway, ueria saber se funcionária e poderia fazer um vídeo sobre o TTGO T-Beam ESP32 com GPS onboard. Parabéns pelo trabalho você explica muito bem!
Att,
Roni A Carvalho
Bom dia Roni.. também tenho muito interesse em aprender mais sobre o TTGO T-Beam. Minha necessidade é desenvolver um sistema de rastreamento GPS móvel, onde um comboio de veículos enviam sua localização via Lora para um veículo "central". Qualquer coisa que descobrir sobre essa placa, mande aqui por favor.
ExcluirAtt,
Guilherme Lopes
Estou procurando um tutorial de envio direto para um Gateway público usando Lorawan.
ResponderExcluirAqui no brasil a cobertura Lora já é bem grande.
A American Tower já espalhou gateways Lora em todas as grandes cidades.
Já tenho um serviço contratado com a Kore Wireless...
Mas para se conectar a rede LoraWan deles, é preciso implementar o protocolo LoraWan no ESP32...
Encontrei umas 3 bibliotecas para implementar LoraWan no ESP32, mas cada uma delas tem algum problema...
https://github.com/HelTecAutomation/ESP32_LoRaWAN
https://github.com/matthijskooijman/arduino-lmic
Seguindo exemplos dessas 2 bibliotecas acima, não tive sucesso...
Nos exemplos, pede para alterar alguns arquivos dentro da pasta SRC da biblioteca, mas os arquivos que manda alterar, nem existem dentro da pasta.
Fernando, já conseguiu implementar LoraWan?
Grato!
Julio Cardoso
Me esqueci de postar este outro link:
ResponderExcluirhttps://github.com/mcci-catena/arduino-lmic
Não tenho nenhum sensor, apenas os dois módulos Heltec, é possível mandar só uma mensagem para a nuvem como exemplo?
ResponderExcluir