IOT

ESP32 LoRa Sensor Data Monitoring on Web Server

In the past few years, a variety of communication technologies for IoT device interaction have been available. Bluetooth Module and Wi-Fi Technology are the most common. However, they have a restricted range, few entry points, and considerable power consumption. Semtech, therefore, introduces LoRa technology to resolve these issues. Using a single battery, the device functions for a whole year.

In this IoT project, we will create an ESP32 LoRa Web Server to remotely monitor sensor readings from several kilometers away. Using a DHT11 Sensor, the sender will read humidity and temperature data. The data is then transmitted over LoRa Radio. The data is received by the module containing the OLED Display. This receiver can connect the device to a WiFi network. The IP Address will be displayed on the OLED. Using the IP address, you can connect in to any web browser that monitors sensor readings. Below is a brief summary of the ESP32 LoRa Web Server.

ESP32 LoRa Sender Receiver

The LoRa receiver operates an asynchronous web server and stores web page files on the ESP32 filesystem (SPIFFS). It also displays the date and time of the most recent readings. The ESP32 utilizes the Network Time Protocol to retrieve data and time.

Bill of Materials

S.N.COMPONENTSDESCRIPTION
1ESP32 BoardESP32 ESP-32S Development Board (ESP-WROOM-32)
2LoRa ModuleSX1278/76 Lora Module
3OLED Display0.96″ I2C OLED Display
4Connecting WiresJumper Wires
5Breadboard
6DHT11 SensorDHT11 Humidity Temperature Sensor

Installing the Required Libraries

Several Libraries are required for the Arduino IDE project to program the ESP32 Board.

1. DHT11 Library

Temperature and humidity readings require a temperature and humidity sensor. DHT11 is the best and cheapest sensor for this purpose. This requires the installation of the DHT11 Library. Download the library from the link below and import it into the Arduino IDE.

Download DHT11 Library

2. LoRa Library

We require an Arduino library for data transmission and reception utilising LoRa radios. The library supports modules such as Semtech SX1276, SX1277, SX1278, and SX1279, as well as HopeRF RFM95W, RFM96, and RFM98W.

Download LoRa Radio Library

3. OLED Libraries

We need two OLED Display Libraries. Adafruit SSD1306 is a library based on SSD1306 drivers for our Monochrome OLEDs. The Adafruit GFX Library is the primary graphics library for all of our displays, offering a standard set of graphics primitives (points, lines, circles, etc.).

Download SSD1306 Library & Download Adafruit GFX Library

4. NTPClient Library

Network Time Protocol (NTP) is a networking protocol for synchronizing clocks across packet-switched, variable-latency data networks. When the LoRa receiver receives a new LoRa message, it will request the date and time from an NTP server in order to determine when the last packet was received.

Download NTPClient Library

5. Asynchronous Web Server Libraries

To create an asynchronous web server, two libraries are required: the ESPAsyncWebServer library and the Async TCP Library.

Download ESPAsyncWebServer Library & Download AsyncTCP Library

Installing ESP32 Filesystem Uploader Plugin

SPIFFS

SPIFFS enables access to the flash memory in a manner similar to that of a standard filesystem on a computer, albeit with fewer features and restrictions. SPIFFS with the ESP32 board is particularly beneficial for:

1.Create settings configuration files

2. Save data permanently

3. Instead of storing modest quantities of data on a microSD card, create files.

4. Save HTML and CSS files in order to create a web server

5. Save pictures, illustrations, and symbols

SPIFFS ESP32

This project involves uploading a webpage and an image to the ESP32 File System.

Installing the Arduino ESP32 Filesystem Uploader

To install the filesystem uploader, please follow these steps:

Step 1: Visit the link: Release for esptool_py. From here download the ESP32FS-1.0.zip

Step 2: Extract the folder ESP32FS-1.0.zip

Step 3: Move the ESP32FS folder to the C:Program Files (x86)Arduinotools directory.

Step 4: Restart the Arduino integrated development environment. Click on tools to access the “Upload Sketch Data” option.

You have now installed the SPIFFS plugin successfully. The remaining items must be completed on the receiver sketch below.

Circuit: ESP32 LoRa Sensor Data Monitoring on Web Server

Let’s now examine the sender and receiver circuits for the ESP32 LoRa Web Server. I constructed both circuits on breadboard. You may use PCB if you so like.

Sender Circuit for ESP32 LoRa Web Server

The following is an ESP32 LoRa Module SX1278 Sender Circuit with a DHT11 Sensor. This component serves as a transmitter. DHT11 Humidity Temperature Sensor reads and transmits humidity and temperature data through LoRa Radio.

ESP32 LoRa Web Server Sender Circuit

The connection is fairly simple. Simlarly connect the Lora SX1278 & ESP32 as follows.

ESP32 PINSSX1278 PINS
GNDGND
3.3VVCC
D5NSS
D23MOSI
D19MISO
D18SCK
D14RST
D2DIO0

Connect similarly the DHT11 digital input pin to GPIO4 on the ESP32. Connect its VCC to ESP32’s 3.3V and GND to its GND.

ESP32 SX1278 DHT11

Receiver Circuit for ESP32 LoRa Web Server

The following is an ESP32 LoRa Module SX1278 Receiver Circuit with an OLED Display. This component serves as a Receiver. Using LoRa Radio, humidity and temperature data are received.

ESP32 LoRa Web Server Receiver Circuit

The ESP32 and LoRa SX1278 connection is the same as described previously. However, the OLED must be linked here. Connect the I2C pins of the OLED display, SDA and SCL, to GPIO21 and GPIO22 on the ESP32. Provide 3.3V VCC and connect the GND to GND.

ESP32 SX1278 OLED Display

ESP32 LoRa Sender Code

After assembling the Sender Circuit circuit on breadboard. Copy and upload the following code to the ESP32 Board. Before uploading, be sure to adjust the LoRa frequency to match the region.

#define BAND 433E6 //433E6 for Asia, 866E6 for Europe, 915E6 for North America


//Libraries for LoRa
#include <SPI.h>
#include <LoRa.h>

//Libraries for LoRa
#include “DHT.h”
#define DHTPIN 4 //pin where the dht11 is connected
DHT dht(DHTPIN, DHT11);

//define the pins used by the LoRa transceiver module
#define ss 5
#define rst 14
#define dio0 2

#define BAND 433E6 //433E6 for Asia, 866E6 for Europe, 915E6 for North America

//packet counter
int readingID = 0;

int counter = 0;
String LoRaMessage = “”;

float temperature = 0;
float humidity = 0;



//Initialize LoRa module
void startLoRA()
{
LoRa.setPins(ss, rst, dio0); //setup LoRa transceiver module

while (!LoRa.begin(BAND) && counter < 10) {
Serial.print(“.”);
counter++;
delay(500);
}
if (counter == 10)
{
// Increment readingID on every new reading
readingID++;
Serial.println(“Starting LoRa failed!”);
}
Serial.println(“LoRa Initialization OK!”);
delay(2000);
}

void startDHT()
{
if (isnan(humidity) || isnan(temperature))
{
Serial.println(“Failed to read from DHT sensor!”);
return;
}
}

void getReadings(){
humidity = dht.readHumidity();
temperature = dht.readTemperature();
Serial.print(F(“Humidity: “));
Serial.print(humidity);
Serial.print(F(“% Temperature: “));
Serial.print(temperature);
Serial.println(F(“°C “));
}

void sendReadings() {
LoRaMessage = String(readingID) + “/” + String(temperature) + “&” + String(humidity) ;
//Send LoRa packet to receiver
LoRa.beginPacket();
LoRa.print(LoRaMessage);
LoRa.endPacket();

Serial.print(“Sending packet: “);
Serial.println(readingID);
readingID++;
Serial.println(LoRaMessage);
}

void setup() {
//initialize Serial Monitor
Serial.begin(115200);
dht.begin();
startDHT();
startLoRA();
}
void loop() {
getReadings();
sendReadings();
delay(5000);
}

ESP32 LoRa Web Server Receiver Code

The LoRa Receiver receives incoming LoRa packets and transmits the data to an asynchronous web server. In addition to the sensor readings, the last time these measurements were obtained and the RSSI are also displayed (received signal strength indicator).

Along with the HTML file, we must save the image on the ESP32 filesystem (SPIFFS).

Creating index.html webpage

The following code should be copied and pasted into Notepad. Save the file as index.html in order to publish the final webpage.

<!DOCTYPE HTML><html>
<head>
<meta name=”viewport” content=”width=device-width, initial-scale=1″>
<link rel=”icon” href=”data:,”>
<title>ESP32 (LoRa + Server)</title>
<link rel=”stylesheet” href=”https://use.fontawesome.com/releases/v5.7.2/css/all.css” integrity=”sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr” crossorigin=”anonymous”>
<style>
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
header {
margin: 0;
padding-top: 5vh;
padding-bottom: 5vh;
overflow: hidden;
background-image: url(mainimage);
background-size: cover;
color: white;
}
h2 {
font-size: 2.0rem;
}
p { font-size: 1.2rem; }
.units { font-size: 1.2rem; }
.readings { font-size: 2.0rem; }
</style>
</head>
<body>
<header>
<h2>ESP32 (LoRa + Server)</h2>
<p><strong>Last received packet:<br/><span id=”timestamp”>%TIMESTAMP%</span></strong></p>
<p>LoRa RSSI: <span id=”rssi”>%RSSI%</span></p>
</header>
<main>
<p>
<i class=”fas fa-thermometer-half” style=”color:#059e8a;”></i> Temperature: <span id=”temperature” class=”readings”>%TEMPERATURE%</span>
<sup>&deg;C</sup>
</p>
<p>
<i class=”fas fa-tint” style=”color:#00add6;”></i> Humidity: <span id=”humidity” class=”readings”>%HUMIDITY%</span>
<sup>%</sup>
</p>

</main>
<script>
setInterval(updateValues, 10000, “temperature”);
setInterval(updateValues, 10000, “humidity”);
setInterval(updateValues, 10000, “rssi”);
setInterval(updateValues, 10000, “timestamp”);

function updateValues(value) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById(value).innerHTML = this.responseText;
}
};
xhttp.open(“GET”, “/” + value, true);
xhttp.send();
}
</script>
</body>
</html>

Creating data folder & its content

Create the folder data and place the index.html file inside of it. Additionally, download and place this image in the same folder. The image should retain the name mainimage. You may use any image you choose, but the filename must remain the same.

Now copy the data folder and paste it together with the ino file into the sketh folder of the receiver.

Uploading html & image to SPIFFS

After completing the preceding procedures, you can now upload the file system:

LoRa Receiver Code

After uploading the HTML page and image to SPIFFS, you can copy the code below and upload the ESP32 Board’s receiver code. Change WiFi SSID and password in the code provided below.

const char* ssid = “Alexahome”;
const char* password = “12345678”;
// Import Wi-Fi library
#include <WiFi.h>
#include “ESPAsyncWebServer.h”

#include <SPIFFS.h>

//Libraries for LoRa
#include <SPI.h>
#include <LoRa.h>

//Libraries for OLED Display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Libraries to get time from NTP Server
#include <NTPClient.h>
#include <WiFiUdp.h>

//define the pins used by the LoRa transceiver module
#define ss 5
#define rst 14
#define dio0 2

#define BAND 433E6 //433E6 for Asia, 866E6 for Europe, 915E6 for North America

//OLED pins
#define OLED_SDA 21
#define OLED_SCL 22
#define OLED_RST -1
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Replace with your network credentials
const char* ssid = “Alexahome”;
const char* password = “12345678”;

// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String day;
String hour;
String timestamp;


// Initialize variables to get and save LoRa data
int rssi;
String loRaMessage;
String temperature;
String humidity;
String readingID;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);

// Replaces placeholder with DHT values
String processor(const String& var){
//Serial.println(var);
if(var == “TEMPERATURE”){
return temperature;
}
else if(var == “HUMIDITY”){
return humidity;
}
else if(var == “TIMESTAMP”){
return timestamp;
}
else if (var == “RRSI”){
return String(rssi);
}
return String();
}

//Initialize OLED display
void startOLED(){
//reset OLED display via software
pinMode(OLED_RST, OUTPUT);
digitalWrite(OLED_RST, LOW);
delay(20);
digitalWrite(OLED_RST, HIGH);

//initialize OLED
Wire.begin(OLED_SDA, OLED_SCL);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) { // Address 0x3C for 128×32
Serial.println(F(“SSD1306 allocation failed”));
for(;;); // Don’t proceed, loop forever
}
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.print(“LORA SENDER”);
}

//Initialize LoRa module
void startLoRA(){
int counter;

//setup LoRa transceiver module
LoRa.setPins(ss, rst, dio0); //setup LoRa transceiver module

while (!LoRa.begin(BAND) && counter < 10) {
Serial.print(“.”);
counter++;
delay(500);
}
if (counter == 10) {
// Increment readingID on every new reading
Serial.println(“Starting LoRa failed!”);
}
Serial.println(“LoRa Initialization OK!”);
display.setCursor(0,10);
display.clearDisplay();
display.print(“LoRa Initializing OK!”);
display.display();
delay(2000);
}

void connectWiFi(){
// Connect to Wi-Fi network with SSID and password
Serial.print(“Connecting to “);
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(“.”);
}
// Print local IP address and start web server
Serial.println(“”);
Serial.println(“WiFi connected.”);
Serial.println(“IP address: “);
Serial.println(WiFi.localIP());
display.setCursor(0,20);
display.print(“Access web server at: “);
display.setCursor(0,30);
display.print(WiFi.localIP());
display.display();
}

// Read LoRa packet and get the sensor readings
void getLoRaData() {
Serial.print(“Lora packet received: “);
// Read packet
while (LoRa.available()) {
String LoRaData = LoRa.readString();
// LoRaData format: readingID/temperature&soilMoisture#batterylevel
// String example: 1/27.43&654#95.34
Serial.print(LoRaData);

// Get readingID, temperature and moisture
int pos1 = LoRaData.indexOf(‘/’);
int pos2 = LoRaData.indexOf(‘&’);
readingID = LoRaData.substring(0, pos1);
temperature = LoRaData.substring(pos1 +1, pos2);
humidity = LoRaData.substring(pos2+1, LoRaData.length());
}
// Get RSSI
rssi = LoRa.packetRssi();
Serial.print(” with RSSI “);
Serial.println(rssi);
}

// Function to get date and time from NTPClient
void getTimeStamp() {
while(!timeClient.update()) {
timeClient.forceUpdate();
}
// The formattedDate comes with the following format:
// 2018-05-28T16:00:13Z
// We need to extract date and time
formattedDate = timeClient.getFormattedDate();
Serial.println(formattedDate);

// Extract date
int splitT = formattedDate.indexOf(“T”);
day = formattedDate.substring(0, splitT);
Serial.println(day);
// Extract time
hour = formattedDate.substring(splitT+1, formattedDate.length()-1);
Serial.println(hour);
timestamp = day + ” ” + hour;
}

void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
startOLED();
startLoRA();
connectWiFi();

if(!SPIFFS.begin()){
Serial.println(“An Error has occurred while mounting SPIFFS”);
return;
}
// Route for root / web page
server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, “/index.html”, String(), false, processor);
});
server.on(“/temperature”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, “text/plain”, temperature.c_str());
});
server.on(“/humidity”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, “text/plain”, humidity.c_str());
});
server.on(“/timestamp”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, “text/plain”, timestamp.c_str());
});
server.on(“/rssi”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, “text/plain”, String(rssi).c_str());
});
server.on(“/mainimage”, HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, “/mainimage.jpg”, “image/jpg”);
});
// Start server
server.begin();

// Initialize a NTPClient to get time
timeClient.begin();
// Set offset time in seconds to adjust for your timezone, for example:
// GMT +1 = 3600
// GMT +8 = 28800
// GMT -1 = -3600
// GMT 0 = 0
timeClient.setTimeOffset(0);
}

void loop() {
// Check if there are LoRa packets available
int packetSize = LoRa.parsePacket();
if (packetSize) {
getLoRaData();
getTimeStamp();
}
}

Testing the Sender & Receiver with ESP32 LoRa Web Server

Once the code has been uploaded, the Serial Monitor can be accessed to observe the following results for transmitting and receiving.

On the receiving end, the OLED display is visible. The IP Address is displayed on the OLED display.

ESP32 LoRa SX1278 Web Server

Access any web browser on a computer or mobile device. The IP address displayed on the OLED display should be visited. You can watch a beautiful representation of LoRa Received messages on the Webserver. In addition to the RSSI value, temperature and humidity data are displayed. Additionally, the homepage displays the time of the last received packet.

ESP32 LoRa Web Server

Conclusion
Hope this blog helps you to understand how to design an ESP32 LoRa Sensor Data Monitoring on a Web Server. We, MATHA ELECTRONICS will come back with more informative blogs.

Leave a Reply

Your email address will not be published.