cstram
Published © GPL3+

IOT Cloud with LoRa integration

How can you make LoRa Nodes communicate with Arduino IOT Cloud? Check out my project :-)

IntermediateWork in progress1,709
IOT Cloud with LoRa integration

Things used in this project

Hardware components

Espressif ESP32 Development Board - Developer Edition
Espressif ESP32 Development Board - Developer Edition
×2
Pushbutton Switch, Momentary
Pushbutton Switch, Momentary
×1
Monochrome 0.91”128x32 I2C OLED Display with Chip Pad
DFRobot Monochrome 0.91”128x32 I2C OLED Display with Chip Pad
×1
Buzzer
Buzzer
×1
RFM95W Module
×2
Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1

Software apps and online services

Arduino IoT Cloud
Arduino IoT Cloud
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

ESP32 LoRa Arduino IOT Gateway

ESP32 LoRa Node with Display, Buzzer and push button

Arduino Mini Pro loRa node

Code

LoRaWK_Carlo.ino

Arduino
This is the LoRa Node with Display, Buzzer and Push Button
/*
  LoRa Messaging with ESP32

  This code send messages between Devices.
  
  created 19 06 2022
  by Carlo Stramaglia
*/

#include <SPI.h>              // include libraries
#include <LoRa.h>
#include <U8g2lib.h>
#include "OneButton.h"
#include "messages.h"

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE);

#define PIN_INPUT 15 // PIN for push button
#define PIN_BUZ 4 // PIN for buzzer
// current Buzzer state, staring with LOW (0)
int buzState = LOW;
OneButton button(PIN_INPUT, true);

int exitFlag = 0;
int stringNumber = -1;

const int csPin = 5;          // LoRa radio chip select
const int resetPin = 14;       // LoRa radio reset
const int irqPin = 2;         // change for your board; must be a hardware interrupt pin

String outgoing;              // outgoing message

byte msgCount = 0;            // count of outgoing messages
byte localAddress = 0xBA;     // address of this device
byte destination = 0xFF;      // destination to send to
long lastSendTime = 0;        // last send time
int interval = 2000;          // interval between sends



void setup() {
  Serial.begin(9600);                   // initialize serial
  u8g2.begin();

  // enable the buzzer
  pinMode(PIN_BUZ, OUTPUT); // sets the digital pin as output
  digitalWrite(PIN_BUZ, buzState); // sets the buzzer in mute

  // link the doubleclick function to be called on a doubleclick event.
  button.attachClick(Click);
  button.attachLongPressStart(longPressStart);
  
  // override the default CS, reset, and IRQ pins (optional)
  LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin

  if (!LoRa.begin(868E6)) {             // initialize ratio at 868 MHz
    Serial.println("LoRa init failed. Check your connections.");
    while (true);                       // if failed, do nothing
  }

  Serial.println("LoRa init succeeded.");
}

void loop() {

  delay(10);
  button.tick();
  // parse for a packet, and call onReceive with the result:
  onReceive(LoRa.parsePacket());
}

void sendMessage(String outgoing) {
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(outgoing.length());        // add payload length
  LoRa.print(outgoing);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

void onReceive(int packetSize) {
  if (packetSize == 0) return;          // if there's no packet, return

  // read packet header bytes:
  int recipient = LoRa.read();          // recipient address
  byte sender = LoRa.read();            // sender address
  byte incomingMsgId = LoRa.read();     // incoming msg ID
  byte incomingLength = LoRa.read();    // incoming msg length

  String incoming = "";

  while (LoRa.available()) {
    incoming += (char)LoRa.read();
  }

  if (incomingLength != incoming.length()) {   // check length for error
    Serial.println("error: message length does not match length");
    return;                             // skip rest of function
  }



  // if the recipient isn't this device or broadcast,
  if (recipient != localAddress && recipient != 0xFF) {
    Serial.println("This message is not for me.");
    return;                             // skip rest of function
  }

  // if message is for this device, or broadcast, print details:
  Serial.println("Received from: 0x" + String(sender, HEX));
  Serial.println("Sent to: 0x" + String(recipient, HEX));
  Serial.println("Message ID: " + String(incomingMsgId));
  Serial.println("Message length: " + String(incomingLength));
  Serial.println("Message: " + incoming);
  Serial.println("RSSI: " + String(LoRa.packetRssi()));
  Serial.println("Snr: " + String(LoRa.packetSnr()));
  Serial.println();
  digitalWrite(PIN_BUZ, !buzState);
  Serial.println("Buzzer ON");
  delay (100);
  digitalWrite(PIN_BUZ, buzState);
  Serial.println("Buzzer OFF");
  sendDisplay(sender, incoming);
}

void sendDisplay(unsigned int nodeID, String messageLoRa) {
  char messageLocal[MAX_STRING_SIZE];
  u8g2.clearBuffer();          
  u8g2.setFont(u8g2_font_ncenB08_tr);
  if (nodeID == 1) 
    sprintf (messageLocal, "Message From: Cloud");
  else
    sprintf (messageLocal, "Message From: %d", nodeID);
  u8g2.drawStr(0,10, messageLocal);
  messageLoRa.toCharArray(messageLocal, MAX_STRING_SIZE);
  u8g2.drawStr(0,30, messageLocal); 
  u8g2.sendBuffer();          
  delay(1000);  
}


void Click()
{
  Serial.println("x1");
  stringNumber++;
  if (stringNumber > (NUMBER_OF_STRING-1))
    stringNumber = 0;
  displayString (stringNumber+1, arr[stringNumber]);
  
  exitFlag = 0;

} // Click

void longPressStart()
{
  Serial.println("Long");
  displaySend (stringNumber+1, arr[stringNumber]);
  // Send data to the node
  //LoRaNow.clear();
  sendMessage(arr[stringNumber]);
  exitFlag = 1;
 
} // LongPressStart


void displayString (unsigned int stringNo, char* stringToDisplay) {
  char messageLocal[MAX_STRING_SIZE];
  u8g2.clearBuffer();          
  u8g2.setFont(u8g2_font_ncenB08_tr); 
  sprintf (messageLocal, "Destination: %d", destination);   
  u8g2.drawStr(0,10, messageLocal);
  sprintf (messageLocal, "Text selection: %d of %d", stringNo, NUMBER_OF_STRING); 
  u8g2.drawStr(0,20, messageLocal);
  u8g2.drawStr(0,30, stringToDisplay); 
  u8g2.sendBuffer();          
  delay(100);  
}

void displaySend (unsigned int stringNo, char* stringToDisplay) {
  char messageLocal[MAX_STRING_SIZE];
  u8g2.clearBuffer();          
  u8g2.setFont(u8g2_font_ncenB08_tr); 
  sprintf (messageLocal, "Destination: %d", destination);
  u8g2.drawStr(0,10, messageLocal);
  sprintf (messageLocal, "Sending Message: %d of %d", stringNo, NUMBER_OF_STRING);  
  u8g2.drawStr(0,20, messageLocal);
  u8g2.drawStr(0,30, stringToDisplay); 
  u8g2.sendBuffer();          
  delay(100);  
}

messages.h

Arduino
Pre-Defined messages inlude file.
#define NUMBER_OF_STRING 4
#define MAX_STRING_SIZE 40

char arr[NUMBER_OF_STRING][MAX_STRING_SIZE] =
{ "Get on the phone!",
  "The music is too loud",
  "I'm ready to move",
  "See you at 10:00 AM"
};

LoRa_ESP32_Cloud.ino

Arduino
ESP32 LoRa Arduino IOT Cloud gateway
/* 
  LoRa Cloud Arduino IOT Gateway with ESP32

  Created by Carlo Stramaglia 26 07 2022
  
*/

#include "thingProperties.h"
#include <SPI.h>              // include libraries
#include <LoRa.h>

const int csPin = 5;          // LoRa radio chip select
const int resetPin = 14;       // LoRa radio reset
const int irqPin = 2;         // change for your board; must be a hardware interrupt pin

byte msgCount = 0;            // count of outgoing messages
byte localAddress = 0x01;     // address of this device
byte destination = 0xFF;      // destination to send to
long lastSendTime = 0;        // last send time
int interval = 2000;          // interval between sends

String incoming;
String outgoing;              // outgoing message

void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500);
   
  // override the default CS, reset, and IRQ pins (optional)
  LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin

  if (!LoRa.begin(868E6)) {             // initialize ratio at 915 MHz
    Serial.println("LoRa init failed. Check your connections.");
    while (true);                       // if failed, do nothing
  }
  
  Serial.println("LoRa init succeeded.");

  delay(1500);

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
}

void loop() {
  ArduinoCloud.update();
  // Your code here
  // parse for a packet, and call onReceive with the result:
  onReceive(LoRa.parsePacket()); 
  //ciaoString = incoming;
  
  
}



/*
  Since CiaoString is READ_WRITE variable, onCiaoStringChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onCiaoStringChange()  {
  // Add your code here to act upon CiaoString change
  sendMessage(ciaoString);
}

void sendMessage(String outgoing) {
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(outgoing.length());        // add payload length
  LoRa.print(outgoing);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

void onReceive(int packetSize) {
  if (packetSize == 0) return;          // if there's no packet, return

  // read packet header bytes:
  int recipient = LoRa.read();          // recipient address
  byte sender = LoRa.read();            // sender address
  byte incomingMsgId = LoRa.read();     // incoming msg ID
  byte incomingLength = LoRa.read();    // incoming msg length

  Serial.println("Received from: 0x" + String(sender, HEX));
  Serial.println("Sent to: 0x" + String(recipient, HEX));
  Serial.println("Message ID: " + String(incomingMsgId));
  Serial.println("Message length: " + String(incomingLength));
  Serial.println();

  incoming = "";
  byte incomingData[20];
  int i=0;
  
  if (sender != 0x2) {
    while (LoRa.available()) {
      incoming += (char)LoRa.read();
    }
/*    if (incomingLength != incoming.length()) {   // check length for error
    Serial.println("error: message length does not match length");
    return;                             // skip rest of function
    } */
  }
  else {
    Serial.println("We are in the case of data rceiving");    
    while (LoRa.available()) {
    incomingData[i] = LoRa.read();
    i++;
    }    
  }
  
  

  // if the recipient isn't this device or broadcast,
  if (recipient != localAddress && recipient != 0xFF) {
    Serial.println("This message is not for me.");
    return;                             // skip rest of function
  }

  if (sender != 0x2) {
    // if message is for this device, or broadcast, print details:
    Serial.println("Received from: 0x" + String(sender, HEX));
    Serial.println("Sent to: 0x" + String(recipient, HEX));
    Serial.println("Message ID: " + String(incomingMsgId));
    Serial.println("Message length: " + String(incomingLength));
    Serial.println("Message: " + incoming);
    Serial.println("RSSI: " + String(LoRa.packetRssi()));
    Serial.println("Snr: " + String(LoRa.packetSnr()));
    Serial.println(); 
    ciaoString = "Device " + String (sender,DEC) + ": " + incoming;
    }
  else {
    humidity = ((incomingData[2] << 8) | incomingData[3]);
    humidity = humidity/10;
    temperature = ((incomingData[0] << 8) | incomingData[1]);
    temperature = temperature/10;
    rzero = ((incomingData[4] << 16) | incomingData[5] << 8 | incomingData[6]);
    rzero = rzero/100;
    crzero = ((incomingData[7] << 16) | incomingData[8] << 8 | incomingData[9]);
    crzero = crzero/100;
    resistance = ((incomingData[10] << 16) | incomingData[11] << 8 | incomingData[12]);
    resistance = resistance/100;
    ppm = ((incomingData[13] << 16) | incomingData[14] << 8 | incomingData[15]);
    ppm = ppm/100;
    cppm = ((incomingData[16] << 16) | incomingData[17] << 8 | incomingData[18]);
    cppm = cppm/100;
    Serial.println("Final Temperature: " + String (temperature));
    Serial.println("Final Humidity: " + String (humidity));
    Serial.println("Final rzero: " + String (rzero));
    Serial.println("Final crzero: " + String (crzero));
    Serial.println("Final resistance: " + String (resistance));
    Serial.println("Final ppm: " + String (ppm));
    Serial.println("Final cppm: " + String (cppm));
  }
}

thingProperties.h

Arduino
// Code generated by Arduino IoT Cloud, DO NOT EDIT.

#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

const char DEVICE_LOGIN_NAME[]  = "TO BE REPLACED WITH YOUR DATA";

const char SSID[]               = "TO BE REPLACED WITH YOUR DATA";    // Network SSID (name)
const char PASS[]               = "TO BE REPLACED WITH YOUR DATA";    // Network password (use for WPA, or use as key for WEP)
const char DEVICE_KEY[]  = "TO BE REPLACED WITH YOUR DATA";    // Secret device password

void onCiaoStringChange();

String ciaoString;
float cppm;
float crzero;
float ppm;
float resistance;
float rzero;
CloudTemperatureSensor temperature;
CloudRelativeHumidity humidity;

void initProperties(){

  ArduinoCloud.setBoardId(DEVICE_LOGIN_NAME);
  ArduinoCloud.setSecretDeviceKey(DEVICE_KEY);
  ArduinoCloud.addProperty(ciaoString, READWRITE, ON_CHANGE, onCiaoStringChange);
  ArduinoCloud.addProperty(cppm, READ, ON_CHANGE, NULL);
  ArduinoCloud.addProperty(crzero, READ, ON_CHANGE, NULL);
  ArduinoCloud.addProperty(ppm, READ, ON_CHANGE, NULL);
  ArduinoCloud.addProperty(resistance, READ, ON_CHANGE, NULL);
  ArduinoCloud.addProperty(rzero, READ, ON_CHANGE, NULL);
  ArduinoCloud.addProperty(temperature, READ, ON_CHANGE, NULL);
  ArduinoCloud.addProperty(humidity, READ, ON_CHANGE, NULL);

}

WiFiConnectionHandler ArduinoIoTPreferredConnection(SSID, PASS);

LoRa_Temperature_and_Air_Node.ino

Arduino
/*
  LoRa Temperature and Air Node.

  This code send Temperature and MQ135 data to a Gateway for Arduino IOT data collection.
  
  created 25 07 2022
  by Carlo Stramaglia
*/

#include <SPI.h>              // include libraries
#include <LoRa.h>
#include <MQ135.h>
#include <DHT.h>

const int csPin = 10;          // LoRa radio chip select
const int resetPin = 7;       // LoRa radio reset
const int irqPin = 4;         // change for your board; must be a hardware interrupt pin

String outgoing;              // outgoing message

byte msgCount = 0;            // count of outgoing messages
byte localAddress = 0x02;     // address of this device
byte destination = 0x01;      // destination to send to

int Temperature = 0;        // Temperature Integer
int Humidity = 0;           // Humidity Integer
int Rzero = 0;
int cRzero = 0;
int Resistance = 0;
int Ppm = 0;
int Cppm = 0;

int payloadLenght = 19;
byte Data[19];

#define PIN_MQ135 A0 // MQ135 Analog Input Pin
#define DHTPIN 3 // DHT Digital Input Pin
#define DHTTYPE DHT22 // DHT11 or DHT22, depends on your sensor

MQ135 mq135_sensor(PIN_MQ135);
DHT dht(DHTPIN, DHTTYPE);

float temperature, humidity; // Temp and Humid floats, will be measured by the DHT

void setup() {
  Serial.begin(9600);                   // initialize serial
  dht.begin();
  delay (1000);  
  
  // override the default CS, reset, and IRQ pins (optional)
  LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin

  if (!LoRa.begin(868E6)) {             // initialize ratio at 868 MHz
    Serial.println("LoRa init failed. Check your connections.");
    while (true);                       // if failed, do nothing
  }

  Serial.println("LoRa init succeeded.");
}

void loop() {

  delay(4000);
  
  humidity = dht.readHumidity();
  temperature = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(humidity) || isnan(temperature)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }

  float rzero = mq135_sensor.getRZero();
  float correctedRZero = mq135_sensor.getCorrectedRZero(temperature, humidity);
  float resistance = mq135_sensor.getResistance();
  float ppm = mq135_sensor.getPPM();
  float correctedPPM = mq135_sensor.getCorrectedPPM(temperature, humidity);

  Serial.print("MQ135 RZero: ");
  Serial.print(rzero);
  Serial.print("\t Corrected RZero: ");
  Serial.print(correctedRZero);
  Serial.print("\t Resistance: ");
  Serial.print(resistance);
  Serial.print("\t PPM: ");
  Serial.print(ppm);
  Serial.print("ppm");
  Serial.print("\t Corrected PPM: ");
  Serial.print(correctedPPM);
  Serial.println("ppm");
  Serial.println("Temperature and Humidity before the calculation");
  Serial.print(temperature);
  Serial.print("  ");
  Serial.println(humidity);

  Temperature = int (temperature*10);
  Humidity = int (humidity*10);
  Rzero = int (rzero*100);
  cRzero = int (correctedRZero*100);
  Resistance = int (resistance*100);
  Ppm = int(ppm*100);
  Cppm = int (correctedPPM*100);
  // prepare and schedule data for transmission 
    Data[0] = Temperature >> 8; 
    Data[1] = Temperature;
    Data[2] = Humidity >> 8;
    Data[3] = Humidity;
    Data[4] = Rzero >> 16;
    Data[5] = Rzero >> 8;
    Data[6] = Rzero;
    Data[7] = cRzero >> 16;
    Data[8] = cRzero >> 8;
    Data[9] = cRzero;
    Data[10] = Resistance >> 16;
    Data[11] = Resistance >> 8;
    Data[12] = Resistance;
    Data[13] = Ppm >> 16;
    Data[14] = Ppm >> 8; 
    Data[15] = Ppm;
    Data[16] = Cppm >> 16;
    Data[17] = Cppm >> 8; 
    Data[18] = Cppm;
    //Data[0] = 0xa; 
    //Data[1] = 0xb;
    //Data[2] = 0xc;
    //Data[3] = 0xd;
    Serial.println("Data of Temp and Hum after manipulation");
    Serial.println(Temperature);
    Serial.println(Humidity);
    //Serial.println(Data[2],DEC);
    //Serial.println(Data[3],DEC);
     
  sendMessage(Data);
}

void sendMessage(byte* outgoing) {
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  //LoRa.write(outgoing.length());        // add payload length
  LoRa.write(payloadLenght);        // add payload length
  LoRa.write(outgoing, payloadLenght);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

Credits

cstram

cstram

16 projects • 21 followers
Passionate about IT, Electronics and DIY. Strong believer in Raspberry and Arduino devices. Experience in digital television and security.

Comments