Introduction: You Have to Know This: GATEWAY!

About: Do you like technology? Follow my channel on Youtube and my Blog. In them I put videos every week of microcontrollers, arduinos, networks, among other subjects.

Because of the many questions I received about how data sending works not only from one LoRa to another, but also to a server, today we are going to discuss two things that we have already shown previously, but in separate videos. I will make a LoRa (the EndPoint) send to another LoRa (the Gateway) data of a BME280 sensor of temperature, humidity, and pressure. Who receives the information will send all this to IBM Watson through the MQTT protocol.

To understand clearer, I suggest you watch these two videos concerning this subject:

SUPER WIFI LONG DISTANCE WITH ESP32 LORA WITH ARDUINO

IBM WATSON WITH ESP32 AS AN ENDPOINT

Step 1: Communication

Here is the outline of our project.

I want to highlight one thing that I consider important: when EndPoint sends data to the Gateway, it is not TCP-IP and LoRaWAN, but rather by LoRa, which is the LoRa radio protocol.

Step 2: Graphic

This is the IBM Watson graphical screen. You track variations in decimal places. This is practical and fast to work with, without the need to make or program any type of chart.

Step 3: Heltec WiFi LoRa 32 Pinout

Step 4: BME280

I leave here also the pinout of the BME280. I really liked this because it is i2c.

Step 5: Assembly

Step 6: Demonstration

This video shows a demonstration of the circuit running. I can say that this is a very complete example of IoT (Internet of Things).

Step 7: Library BME280

In the Arduino IDE, go to Sketch-> Include Library-> Manage Libraries ...

Look for bme280 and install the Adafruit BME280 Library

Step 8: Adafruit Unified Sensor Library

In the Arduino IDE, go to Sketch-> Include Library-> Manage Libraries ...

Search for Adafruit sensor based libraries and install the Adafruit Unified Sensor

Step 9: PubSubClient Library

In the Arduino IDE, go to Sketch-> Include Library-> Manage Libraries ...

Search for pubsubclient and install PubSubClient

Step 10: Modify LoRa 32 WiFi I2c Pinout

Open the file:

C: \ Users \ ALU \ Documents and Settings \ Arduino \ hardware \ hel32 \ var32 \ variants \ wifi_lora_32 \ pins_arduino.h and change the SDA pins to 4 and the SCL pin to 15

Step 11: Source Code

I have explained several parts of the source code. This includes the two codes we have here, as we have the Master and the Slave. Our main focus of this video, therefore, will be on the Master, which in this case is the router in the part concerning Master.ino - Receive.

I will show in this article all parts of the code again, so you can view it with ease.

Step 12: LoRaSendReceiveBME280MQTT.ino

At the beginning, we will include the libraries and define the destinations of the GPIOs, in addition to the frequency of the radio. Also, we will create constants to inform the Slave about the data work, as well as the return of these to the Master. We also structure the sensor data and point the variable to control the 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

In this first Setup, we will act on the configuration of the 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

Here, we’ll look at the initial LoRa settings.

//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(); }

Step 13: Master.ino

In this step, only compile if MASTER is set in the main file. We include the PubSubCliente.h and WiFi.h libraries; we replace the SSID of the network itself, as well as the password and Server MQTT that we will use. Also, we give a name to the topic from where we should send the data, so that they appear in the charts. We point out the ID we will use to connect. Finally, QUICK_STAR should remain as is.

//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:";

In DEVICE_ID, we have changed it to a unique id. In this example, we use the MAC Address of the device we are using. It will serve as identification on the 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);

We define intervals between the sends, and the variables to store the values of temperature, humidity, and pressure, as well as the time of the last sending and the location of the data that arrives from the other LoRa device.

//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

We make in this part the configurations that involve the Master, calling the initial configurations of the display and the LoRa. We also connect to the WiFi network.

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

Here, we have the function responsible for connecting to the MQTT server.

//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

Already in this step, we work with the function responsible for connecting the WiFi network.

//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

In this Loop, we define the sending of the package to inform the Slave the desire to receive data and also check if there are packages to be received.

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

We initialize the package and send what is contained in "GETDATA", to finalize and send the package later.

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

In this step, we check if the packet has the minimum length of characters that we expect and store a string. We still analyze if the header is what we expect, and we start to read the data and show it on the display.

We then create the json that we will send to the MQTT server. We publish on the topic where the server waits to receive and generate the graph.

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

Master.ino - showData

Finally, we show the time the Master took to create the package, send it, receive the Slave, read it, create a new package, and send it to the Master, who receives it and reads it. This is printed on the 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

Here we have the function responsible for creating a Json with the data read.

//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

Step 14: Slave.ino

Starting the Slave code, we only compiled if the Master is not defined in the main file. We have included the libraries and pointed out the individual responsible for reading the temperature, pressure, and humidity.

//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

In Slave Setup, we call up the initial display and LoRa settings.

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

As in the Master, in this part of the Slave Loop, we check if it has the expected number of characters and store the data in a String. We also simulated the reading of the data. We create the package, finalize it, and send it. Finally, we show the information on the 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

Finally, the data is displayed.

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

Step 15: Graphic

To view the sensor graph, go to https://quickstart.internetofthings.ibmcloud.com

In the Device id field, enter the DEVICE_ID that you defined in the code.

It is important to change in the code this due id for a unique id, which is used only by you.

This avoids conflict with data sent by another person.

Accept the terms and click Go.

Step 16: Download the Files: