ESP32 LoRa Sensor Monitoring with Web Server (Long Range Communication)

In this project, you’ll build a sensor monitoring system using a TTGO LoRa32 SX1276 OLED board that sends temperature, humidity and pressure readings via LoRa radio to an ESP32 LoRa receiver. The receiver displays the latest sensor readings on a web server.

ESP32 LoRa Sensor Monitoring with Web Server Long Range Communication

With this project you’ll learn how to:

  • Send sensor readings via LoRa radio between two ESP32 boards;
  • Add LoRa and Wi-Fi capabilities simultaneously to your projects (LoRa + Web Server on the same ESP32 board);
  • Use the TTGO LoRa32 SX1276 OLED board or similar development boards for IoT projects.

Recommended reading: TTGO LoRa32 SX1276 OLED Board: Getting Started with Arduino IDE

Watch the Video Demonstration

Watch the video demonstration to see what you’re going to build throughout this tutorial.

Project Overview

The following image shows a high-level overview of the project we’ll build throughout this tutorial.

Project Overview ESP32 LoRa Sender and ESP32 LoRa32 Receiver board
  • The LoRa sender sends BME280 sensor readings via LoRa radio every 10 seconds;
  • The LoRa receiver gets the readings and displays them on a web server;
  • You can monitor the sensor readings by accessing the web server;
  • The LoRa sender and the Lora receiver can be several hundred meters apart depending on their location. So, you can use this project to monitor sensor readings from your fields or greenhouses if they are a bit apart from your house;
  • The LoRa receiver is running an asynchronous web server and the web page files are saved on the ESP32 filesystem (SPIFFS);
  • The LoRa receiver also shows the date and time the last readings were received. To get date and time, we use the Network Time Protocol with the ESP32.

For an introduction to LoRa communication: what’s LoRa, LoRa frequencies, LoRa applications and more, read our Getting Started ESP32 with LoRa using Arduino IDE.

Parts Required

TTGO LoRa32 SX1276 OLED board with antenna

For this project, we’ll use the following components:

You’ll also need some jumper wires and a breadboard.

You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!

Preparing the Arduino IDE

To program the TTGO LoRa32 SX1276 OLED boards we’ll use Arduino IDE. To upload files to the ESP32 filesystem, we’ll use the ESP32 filesystem uploader plugin.

So, before proceeding, you need to install the ESP32 package and the ESP32 filesystem uploader plugin in your Arduino IDE.

Installing libraries

For this project you need to install several libraries.

LoRa, BME280 and OLED Libraries

The following libraries can be installed through the Arduino Library Manager. Go to Sketch > Include Library> Manage Libraries and search for the library name.

Asynchronous Web Server Libraries

To build the asynchronous web server, you also need to install the following libraries:

These libraries are not available to install through the Library Manager. So, you need to unzip the libraries and move them to the Arduino IDE installation libraries folder.

Alternatively, you can go to Sketch > Include Library > Add .ZIP library… and select the libraries you’ve just downloaded.

NTPClient Library

Everytime the LoRa receiver picks up a new a LoRa message, it will request the date and time from an NTP server so that we know when the last packet was received.

For that we’ll be using the NTPClient library forked by Taranais. Follow the next steps to install this library in your Arduino IDE:

  1. Click here to download the NTPClient library. You should have a .zip folder in your Downloads
  2. Unzip the .zip folder and you should get NTPClient-master folder
  3. Rename your folder from NTPClient-master to NTPClient
  4. Move the NTPClientfolder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Alternatively, you can go to Sketch > Include Library> Add .ZIP library… and select the library you’ve just downloaded.

LoRa Sender

The LoRa Sender is connected to a BME280 sensor and sends temperature, humidity, and pressure readings every 10 seconds. You can change this period of time later in the code.

Recommended reading: ESP32 with BME280 Sensor using Arduino IDE (Pressure, Temperature, Humidity)

LoRa Sender Circuit

The BME280 we’re using communicates with the ESP32 using I2C communication protocol. Wire the sensor as shown in the next schematic diagram:

TTGO LoRa32 SX1276 OLED board ESP32 Sender
BME280ESP32
VIN3.3 V
GNDGND
SCLGPIO 13
SDAGPIO 21

LoRa Sender Code

The following code reads temperature, humidity and pressure from the BME280 sensor and sends the readings via LoRa radio.

Copy the following code to your Arduino IDE.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-lora-sensor-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

//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 for BME280
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

//define the pins used by the LoRa transceiver module
#define SCK 5
#define MISO 19
#define MOSI 27
#define SS 18
#define RST 14
#define DIO0 26

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

//OLED pins
#define OLED_SDA 4
#define OLED_SCL 15 
#define OLED_RST 16
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

//BME280 definition
#define SDA 21
#define SCL 13

TwoWire I2Cone = TwoWire(1);
Adafruit_BME280 bme;

//packet counter
int readingID = 0;

int counter = 0;
String LoRaMessage = "";

float temperature = 0;
float humidity = 0;
float pressure = 0;

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

//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 128x32
    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(){
  //SPI LoRa pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //setup LoRa transceiver module
  LoRa.setPins(SS, RST, DIO0);

  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!");
  display.setCursor(0,10);
  display.clearDisplay();
  display.print("LoRa Initializing OK!");
  display.display();
  delay(2000);
}

void startBME(){
  I2Cone.begin(SDA, SCL, 100000); 
  bool status1 = bme.begin(0x76, &I2Cone);  
  if (!status1) {
    Serial.println("Could not find a valid BME280_1 sensor, check wiring!");
    while (1);
  }
}

void getReadings(){
  temperature = bme.readTemperature();
  humidity = bme.readHumidity();
  pressure = bme.readPressure() / 100.0F;
}

void sendReadings() {
  LoRaMessage = String(readingID) + "/" + String(temperature) + "&" + String(humidity) + "#" + String(pressure);
  //Send LoRa packet to receiver
  LoRa.beginPacket();
  LoRa.print(LoRaMessage);
  LoRa.endPacket();
  
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(1);
  display.print("LoRa packet sent!");
  display.setCursor(0,20);
  display.print("Temperature:");
  display.setCursor(72,20);
  display.print(temperature);
  display.setCursor(0,30);
  display.print("Humidity:");
  display.setCursor(54,30);
  display.print(humidity);
  display.setCursor(0,40);
  display.print("Pressure:");
  display.setCursor(54,40);
  display.print(pressure);
  display.setCursor(0,50);
  display.print("Reading ID:");
  display.setCursor(66,50);
  display.print(readingID);
  display.display();
  Serial.print("Sending packet: ");
  Serial.println(readingID);
  readingID++;
}

void setup() {
  //initialize Serial Monitor
  Serial.begin(115200);
  startOLED();
  startBME();
  startLoRA();
}
void loop() {
  getReadings();
  sendReadings();
  delay(10000);
}

View raw code

How the Code Works

Start by including the necessary libraries for LoRa, OLED display and BME280 sensor.

//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 for BME280
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

Define the pins used by the LoRa transceiver module. We’re using the TTGO LoRa32 SX1276 OLED board V1.0 and these are the pins used by the LoRa chip:

//define the pins used by the LoRa transceiver module
#define SCK 5
#define MISO 19
#define MOSI 27
#define SS 18
#define RST 14
#define DIO0 26

Note: if you’re using another LoRa board, check the pins used by the LoRa transceiver chip.

Select the LoRa frequency:

#define BAND 866E6

Define the OLED pins.

#define OLED_SDA 4
#define OLED_SCL 15 
#define OLED_RST 16

Define the OLED size.

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Define the pins used by the BME280 sensor.

//BME280 definition
#define SDA 21
#define SCL 13

Create an I2C instance for the BME280 sensor and a bme object.

TwoWire I2Cone = TwoWire(1);
Adafruit_BME280 bme;

Create some variables to hold the LoRa message, temperature, humidity, pressure and reading ID.

int readingID = 0;

int counter = 0;
String LoRaMessage = "";

float temperature = 0;
float humidity = 0;
float pressure = 0;

Create a display object for the OLED display.

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

setup()

In the setup(), we call several functions that were created previously in the code to initialize the OLED display, the BME280 and the LoRa transceiver module.

void setup() {
  Serial.begin(115200);
  startOLED();
  startBME();
  startLoRA();
}

loop()

In the loop(), we call the getReadings() and sendReadings() functions that were also previously created. These functions are responsible for getting readings from the BME280 sensor, and to send those readings via LoRa, respectively.

void loop() {
  getReadings();
  sendReadings();
  delay(10000);
}

getReadings()

Getting sensor readings is as simple as using the readTemperature(), readHumidity(), and readPressure() methods on the bme object:

void getReadings(){
  temperature = bme.readTemperature();
  humidity = bme.readHumidity();
  pressure = bme.readPressure() / 100.0F;
}

sendReadings()

To send the readings via LoRa, we concatenate all the readings on a single variable, LoRaMessage:

void sendReadings() {
  LoRaMessage = String(readingID) + "/" + String(temperature) + "&" + String(humidity) + "#" + String(pressure);

Note that each reading is separated with a special character, so the receiver can easily identify each value.

Then, send the packet using the following:

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

Each time we send a LoRa packet, we increase the readingID variable so that we have an idea on how many packets were sent. You can delete this variable if you want.

readingID++;

The loop() is repeated every 10000 milliseconds (10 seconds). So, new sensor readings are sent every 10 seconds. You can change this delay time if you want.

delay(10000);

Testing the LoRa Sender

Upload the code to your ESP32 LoRa Sender Board.

Go to Tools > Port and select the COM port it is connected to. Then, go to Tools > Board and select the board you’re using. In our case, it’s the TTGO LoRa32-OLED V1.

Arduino IDE selecting TTGO LoRa32-OLED-V1 board

Finally, press the upload button.

Arduino IDE Upload button

Open the Serial Monitor at a baud rate of 115200. You should get something as shown below.

Arduino IDE: ESP32 LoRa Sender Circuit Demonstration

The OLED of your board should be displaying the latest sensor readings.

TTGO LoRa32 SX1276 OLED board ESP32 Sender Circuit Schematic

Your LoRa Sender is ready. Now, let’s move on to the LoRa Receiver.

LoRa Receiver

The LoRa Receiver gets incoming LoRa packets and displays the received readings on an asynchronous web server. Besides the sensor readings, we also display the last time those readings were received and the RSSI (received signal strength indicator).

The following figure shows the web server we’ll build.

TTGO LoRa32 board ESP32 Receiver Web Server Example

As you can see, it contains a background image and styles to make the web page more appealing. There are several ways to display images on an ESP32 web server. We’ll store the image on the ESP32 filesystem (SPIFFS). We’ll also store the HTML file on SPIFFS.

Organizing your Files

To build the web server you need three different files: the Arduino sketch, the HTML file and the image. The HTML file and the image should be saved inside a folder called data inside the Arduino sketch folder, as shown below.

ESP32 Filesystem plugin files structure organized data folder HTML jpg

Creating the HTML File

Create an index.html file with the following content or download all the project files here:

<!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(winter);
      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>&#37;</sup>
  </p>
  <p>
    <i class="fas fa-angle-double-down" style="color:#e8c14d;"></i> Pressure: <span id="pressure" class="readings">%PRESSURE%</span>
    <sup>hpa</sup>
  </p>
</main>
<script>
setInterval(updateValues, 10000, "temperature");
setInterval(updateValues, 10000, "humidity");
setInterval(updateValues, 10000, "pressure");
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>

View raw code

We’ve also included the CSS styles on the HTML file as well as some JavaScript that is responsible for updating the sensor readings automatically.

Something important to notice are the placeholders. The placeholders go between % signs: %TIMESTAMP%, %TEMPERATURE%, %HUMIDITY%, %PRESSURE% and %RSSI%.

These placeholders will then be replaced with the actual values by the Arduino code.

The styles are added between the <style> and </style> tags.

<style>
  body {
    margin: 0;
    font-family: Arial, Helvetica, sans-serif;
    text-align: center;
  }
  header {
    margin: 0;
    padding-top: 10vh;
    padding-bottom: 5vh;
    overflow: hidden;
    width: 100%;
    background-image: url(winter.jpg);
    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>

If you want a different image for your background, you just need to modify the following line to include your image’s name. In our case, it is called winter.jpg.

background-image: url(winter.jpg);

The JavaScript goes between the <scritpt> and </script> tags.

<script>
setInterval(updateValues("temperature"), 5000);
setInterval(updateValues("humidity"), 5000);
setInterval(updateValues("pressure"), 5000);
setInterval(updateValues("rssi"), 5000);
setInterval(updateValues("timeAndDate"), 5000);

function updateValues(value) {
  console.log(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>

We won’t explain in detail how the HTML and CSS works, but a good place to learn is the W3Schools website.

LoRa Receiver Arduino Sketch

Copy the following code to your Arduino IDE or download all the project files here. Then, you need to type your network credentials (SSID and password) to make it work.

/*********
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-lora-sensor-web-server/

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// 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 SCK 5
#define MISO 19
#define MOSI 27
#define SS 18
#define RST 14
#define DIO0 26

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

//OLED pins
#define OLED_SDA 4
#define OLED_SCL 15 
#define OLED_RST 16
#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     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// 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 pressure;
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 == "PRESSURE"){
    return pressure;
  }
  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 128x32
    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;
  //SPI LoRa pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //setup LoRa transceiver module
  LoRa.setPins(SS, RST, DIO0);

  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 soil moisture
    int pos1 = LoRaData.indexOf('/');
    int pos2 = LoRaData.indexOf('&');
    int pos3 = LoRaData.indexOf('#');
    readingID = LoRaData.substring(0, pos1);
    temperature = LoRaData.substring(pos1 +1, pos2);
    humidity = LoRaData.substring(pos2+1, pos3);
    pressure = LoRaData.substring(pos3+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("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", pressure.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("/winter", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/winter.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();
  }
}

View raw code

How the Code Works

You start by including the necessary libraries. You need libraries to:

  • build the asynchronous web server;
  • access the ESP32 filesystem (SPIFFS);
  • communicate with the LoRa chip;
  • control the OLED display;
  • get date and time from an NTP server.
// 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 SCK 5
#define MISO 19
#define MOSI 27
#define SS 18
#define RST 14
#define DIO0 26

Note: if you’re using another LoRa board, check the pins used by the LoRa transceiver chip.

Define the LoRa frequency:

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

Set up the OLED pins:

#define OLED_SDA 4
#define OLED_SCL 15 
#define OLED_RST 16
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Enter your network credentials in the following variables so that the ESP32 can connect to your local network.

const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Define an NTP Client to get date and time:

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

Create variables to save date and time:

String formattedDate;
String day;
String hour;
String timestamp;

More variables to store the sensor readings received via LoRa radio.

int rssi;
String loRaMessage;
String temperature;
String humidity;
String pressure;
String readingID;

Create an AsyncWebServer object called server on port 80.

AsyncWebServer server(80);

Create an object called display for the OLED display:

AsyncWebServer server(80);

processor()

The processor() function is what will attribute values to the placeholders we’ve created on the HTML file.

It accepts as argument the placeholder and should return a String that will replace that placeholder.

For example, if it finds the TEMPERATURE placeholder, it will return the temperature String variable.

// 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 == "PRESSURE"){
    return pressure;
  }
  else if(var == "TIMESTAMP"){
    return timestamp;
  }
  else if (var == "RRSI"){
    return String(rssi);
  }
  return String();
}

setup()

In the setup(), you initialize the OLED display, the LoRa communication, and connect to Wi-Fi.

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

You also initialize SPIFFS:

if(!SPIFFS.begin()){
  Serial.println("An Error has occurred while mounting SPIFFS");
  return;
}

Async Web Server

The ESPAsyncWebServer library allows us to configure the routes where the server will be listening for incoming HTTP requests.

For example, when a request is received on the route URL, we send the index.html file that is saved in the ESP32 SPIFFS:

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/index.html", String(), false, processor);
});

As mentioned previously, we added a bit of Javascript to the HTML file that is responsible for updating the web page every 10 seconds. When that happens, it makes a request on the /temperature, /humidity, /pressure, /timestamp, /rssi URLs.

So, we need to handle what happens when we receive those requests. We simply need to send the temperature, humidity, pressure, timestamp and rssi variables. The variables should be sent in char format, that’s why we use the .c_str() method.

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("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", pressure.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());
});

Because we included an image in the web page, we’ll get a request “asking” for the image. So, we need to send the image that is saved on the ESP32 SPIFFS.

server.on("/winter", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, "/winter.jpg", "image/jpg");
});

Finally, start the web server.

server.begin();

NTPClient

Still in the setup(), create an NTP client to get the time from the internet.

timeClient.begin();

The time is returned in GMT format, so if you need to adjust for your timezone, you can use the following:

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

loop()

In the loop(), we listen for incoming LoRa packets:

int packetSize = LoRa.parsePacket();

If a new LoRa packet is available, we call the getLoRaData() and getTimeStamp() functions.

if (packetSize) {
  getLoRaData();
  getTimeStamp();
}

The getLoRaData() function receives the LoRa message and splits it to get the different readings.

The getTimeStamp() function gets the time and date from the internet at the moment we receive the packet.

Uploading Code and Files

After inserting your network credentials, save your sketch. Then, in your Arduino IDE go to Sketch > Show Sketch Folder, and create a folder called data. Inside that folder, you should have the HTML file and the image file.

After making sure you have all the needed files in the right directories, go to Tools and select ESP32 Data Sketch Upload.

ESP32 Sketch Data Upload Arduino IDE SPIFFS FS Filesystem

After a few seconds, the files should be successfully uploaded to SPIFFS.

Note: if you don’t see the “ESP32 Sketch Data Upload” option that means you don’t have the ESP32 filesystem uploader plugin installed (how to install the ESP32 filesystem uploader plugin).

Now, upload the sketch to your board.

Arduino IDE Upload button

Open the Serial Monitor at a baud rate of 115200.

You should get the ESP32 IP address, and you should start receiving LoRa packets from the sender.

ESP32 Arduino IDE Serial Monitor window

You should also get the IP address displayed on the OLED.

TTGO LoRa32 SX1276 OLED board ESP32 Receiver Circuit Schematic web server

Demonstration

Open a browser and type your ESP32 IP address. You should see the web server with the latest sensor readings.

ESP32 LoRa + Web Server + Sensor readings

With these boards we were able to get a stable LoRa communication up to 180 meters (590 ft) in open field. These means that we can have the sender and receiver 180 meters apart and we’re still able to get and check the readings on the web server.

LoRa32 SX1276 OLED Board Communication Range Experiment

Getting a stable communication at a distance of 180 meters with such low cost boards and without any further customization is really impressive.

However, in a previous project using an RFM95 SX1276 LoRa transceiver chip with an home made antenna, we got better results: more than 250 meters with many obstacles in between.

RFM95 LoRa SX1276 transceiver chip connected to an ESP32

The communication range will really depend on your environment, the LoRa board you’re using and many other variables.

Wrapping Up

You can take this project further and build an off-the-grid monitoring system by adding solar panels and deep sleep to your LoRa sender. The following articles might help you do that:

You may also want to access your sensor readings from anywhere or plot them on a chart:

We hope you’ve found this project interesting. If you’d like to see more projects using LoRa radio, let us know in the comments’ section.

Thanks for reading.



Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »

Learn how to program and build projects with the ESP32 and ESP8266 using MicroPython firmware DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our weekly newsletter!

70 thoughts on “ESP32 LoRa Sensor Monitoring with Web Server (Long Range Communication)”

  1. Could this be powered by one or more 6 volt solar panels, similar to the article “Power ESP32/ESP8266 with Solar Panels” but using the on-board battery regulator ?

    Reply
  2. Rui,
    Nice Tutorial, but I think you should emphasize some LoRa facts:

    Choice of Frequency has to be relevant to both the country/region you are in and the board you purchase. The TTGO seems to ship with a the same antenna, whatever band you choose (is it 433Mhz?). I have been unable to do a frequency test on the ones I have but they are not very good at 915Mhz.

    Since the frequency bands are a shared resource, most countries have limitations on how often you can transmit your information. Every 10 seconds is good for testing, but Temperature/Humidity/Air Pressure readings would be just as relevant if sent every 30 or 60 seconds.

    If someone is considering other data they should consider how often they need to update the information.

    Dave

    Reply
    • Hi Dave.
      Thank you for your comment.
      Yes, you are right, you need to choose a LoRa board with a frequency suitable for your country.
      And I agree with you that 10 seconds is a very short interval. In a real world application, I suggest using deep sleep and send readings every hour or every 30 minutes.
      Regards,
      Sara

      Reply
  3. Great project!
    I need to send dust sensor data (sensor=SDS 011) via LoRa. I will try out your tutorial and hope it will word with this I2C sensor, too.

    Could you do a tutorial about sending LoRa packets to a TTN gateway?

    Reply
  4. This is really useful. The Sender code worked without any difficulties.
    The received code produced –
    ‘class NTPClient’ has no member named ‘getformattedDate’
    Any ideas how to fix?

    Reply
  5. Very nice tutorial and much appreciated.I have already put into practice your many tutorials and all work perfectly. wonderful tutorials

    do you know a way to monitor battery voltage on this model board

    Yves

    Reply
  6. Excuse me I forgot to ask you if it is possible to have several transmitters (for example 10,20 or 30) and a single receiver?
    thanks again for your tutorials and for your answer

    Yves

    Reply
      • thank you very much for your answer, i experiment that and tell you if it’s working (iI already tried with three sender and it’s work fine)
        regards
        Yves

        Reply
        • Do you have a copy of your code that you could share? I am looking at transmitting both analog values and digital (on/off) values from a few nodes to a master.

          Reply
  7. hello,
    I have this error with the receiver.
    Advice ?
    Thanks,

    C:\2.DATA\DEV\libraries\AsyncTCP\src\AsyncTCP.cpp: In function ‘bool _start_async_task()’:

    C:\2.DATA\DEV\libraries\AsyncTCP\src\AsyncTCP.cpp:221:141: error: ‘xTaskCreateUniversal’ was not declared in this scope

    xTaskCreateUniversal(_async_service_task, “async_tcp”, 8192 * 2, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
    ^

    exit status 1
    Error compiling for board TTGO LoRa32-OLED V1.

    Reply
  8. I bought 2 of these from LilyGO and neither one is recognized by my computer. Do I need a 10uF cap between EN and GND? But, that would have anything to do with the USB port recognized. Any guesses?

    Reply
    • Hi.
      You don’t need to do that.
      Have you ever gotten an ESP32 recognized by your computer?
      I just needed to connect via USB cable and it worked straight away.
      Regards,
      Sara

      Reply
      • Hi Sara,
        Yes I use the ESP32-CAM (although I wish I could figure out how to make it auto refresh on my phone and computer) and the ESP32 for controlling relays.

        Reply
  9. Hi Sara
    The project is running fine, thank you.
    But I’ve some questions about the receiver part.
    From time to time (1h to 6-7h but never longer), the sketch stops.
    And this with Edge or Chrome.
    I suppose it stops reading datas but the serial monitor does not show activities neither the www.
    I’ve already exchanged the ESP w/o success.
    Searching on internet shows “delay” topics.
    You mention in “https://rntlab.com/question/solved-esp32-web-server-drops-connection-crashes/” some identical prblm adding also “delay” but only with Chrome.
    I can’t find any solution.
    What do you suggest?
    Thanks

    Reply
    • Hi Jean-Luc,
      I have the same problem, need to further investigate with serial debugging where it hangs. Were you able to find a solution already?

      Reply
  10. I don’t understand the choice of board for this project.

    If the sensor is a distance away, in a field or somewhere inaccessible, why have an OLED on it? It just wastes power…

    Similarly, if the ‘receiver=base station’ has a web server that shows data on a phone, why bother with an OLED?

    Reply
  11. Hi Sara and Rui,
    First of all, thank you for your so interesting tutorials, they are always very well done and very instructive. Thank you so much!
    I was just surprised of the range of 180 m according to your tests. This is much smaller than the range of 250 m you tested with the simple WIFI connection of ESP-NOW in another of your tutorials, and significantly lower than the long range normally expected for LoRa devices (a few 100 m or even a few km). Do you have any idea why? Is it due to the buidling quality of that specific TTGO board? Or does it need further tweaking to increase the range? If so, I would be grateful if you could make some tutorial about this aspect, or point to relevant information sources.
    Thank you very much in advance for your help!
    Best regards,
    Sam

    Reply
    • Hi Sam.
      Thanks for your comment.
      Yes, you are right. We were also disappointed about the communication range with LoRa. I think it may be due to the environment where we were testing and to the LoRa settings set by default.
      One of our readers reported a communication range of a few km with some changes on the LoRa settings.
      Here are the links for the changes:
      https://pastebin.com/5feAAf4A
      https://pastebin.com/qVqJbQYt
      It may be worth taking a look at this article: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5038744/
      I hope this helps.
      Regards,
      Sara

      Reply
        • I’m getting up to about 500 meters through trees and a few houses in my neighborhood and across the highway. Better than I expected for sure without any optimization of parameters. 915Mhz band. This is just snd and receive sketch so nothing fancy on data transfer. Am going to add GPS to do distance calculations when I get a chance.

          Reply
  12. Hello,

    Very interesting project.
    Do you have any experience using it (this TTGO ESP32 and LoRA) for Radio Control of a model sailboat?
    Do you how many times per second I can be able to send the Potentiometer value (from a Joystick) to the receiver?

    I have a different board (also from TTGO but the one with Oled, GPS, Bluetooth and WiFi and LoRA, of course. Do you know if the same sketch could work? Or is your sketch speficic for the TTGO used in the project?

    One thing I have tried but it did not compile was to use this project with a Heltec Esp32 with LoRA. It can not find the LoRa class…

    Reply
    • Hi.
      This code should be compatible with any board that has an OLED and a LoRa module. To make it compatible you need to make sure that you have the right pin definition for your LoRa board.
      What I mean by this, is that you need to know which pins your board uses to connect to the LoRa module and to the OLED display. Then, insert the right pins on the following lines:

      For the LoRa module:
      #define SCK 5
      #define MISO 19
      #define MOSI 27
      #define SS 18
      #define RST 14
      #define DIO0 26

      And for the OLED
      #define OLED_SDA 4
      #define OLED_SCL 15
      #define OLED_RST 16

      If you have an error that it can’t find the LoRa class, that probably means that you don’t have the library installed.
      I hope this helps.
      Regards,
      Sara

      Reply
  13. Hi,
    Your tutorials are very explicit and easy to learn.
    After trying this project and other from your tutorials, i´m having problems to connect to my wifi. It can´t connect to wifi. It hold in while() cycle printing dots and doesn´t end cycle.
    Using other examples from other examples, i draw a code to try wifi connection and can connect after few seconds. Even when i use same lines from my code in this project and it still can´t connected.
    Per example, i defined Wifi.mode(WiFi_STA) and WiFi.disconnect() before WiFi.Begin() like i did in my program and still can´t connect.

    Can you help me with some solution?

    Regards,
    Hugo Morais

    Reply
    • Hi Hugo.
      Are you using the exact code we’re using in this tutorial?
      Were you able to connect the same board but using a different code?
      REgards,
      sara

      Reply
      • Hi,
        My boards are TTGO T1.
        I tried the exact code. With your code the while() loop doesn´t finish and isn’t connected.
        Using other code i made it to connect my wifi.

        Regards, Hugo

        Reply
        • What code do you use to connect to Wi-Fi?
          Isn’t that the same we use in the code?
          Have you inserted the right network credentials?
          Regards,
          Sara

          Reply
          • #include “WiFi.h”

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

            // Set WiFi to station mode and disconnect from an AP if it was previously connected
            WiFi.mode(WIFI_STA);
            WiFi.disconnect();
            delay(100);

            Serial.println(“Setup done”);
            }

            void prtStatus(){
            Serial.print (“Wifi.status: “);
            Serial.print(WiFi.status());
            Serial.print(” -> “);

            switch (WiFi.status()) {
            case WL_CONNECTED: Serial.println(“Connected”); break;
            case WL_NO_SHIELD: Serial.println(“Wifi not available”); break;
            case WL_IDLE_STATUS: Serial.println(“Waiting for .begin to end”); break;
            case WL_NO_SSID_AVAIL: Serial.println(“No networks available”); break;
            case WL_SCAN_COMPLETED: Serial.println(“Scanning is complete”); break;
            case WL_CONNECT_FAILED: Serial.println(“Connection failed”); break;
            case WL_CONNECTION_LOST: Serial.println(“Connection lost”); break;
            case WL_DISCONNECTED: Serial.println(“Disconnected”); break;
            }
            }

            // new SSID and new Pass
            bool chkWifi(char *nSSID, char *nPass){
            bool isConn=WiFi.status() == WL_CONNECTED; //if connected then goes true
            int i=0;

            prtStatus();

            if (!isConn) {
            Serial.print(“Scanning for “);
            Serial.println(nSSID);

            // WiFi.scanNetworks will return the number of networks found
            int n = WiFi.scanNetworks();
            prtStatus();

            if (n == 0) {
            Serial.println("No networks found");
            } else {
            Serial.print(n);
            Serial.println(" networks found");

            while (i<n && !isConn ) {
            // Print SSID and RSSI for each network found
            Serial.print(i + 1);
            Serial.print(": ");
            Serial.print(WiFi.SSID(i));

            prtStatus();

            if (WiFi.SSID(i)==nSSID) {
            WiFi.begin(nSSID,nPass);
            isConn=WiFi.status() == WL_CONNECTED;

            prtStatus();
            }
            Serial.print(" (");
            Serial.print(WiFi.RSSI(i));
            Serial.print(")");
            Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
            delay(10);
            i++;
            }
            }
            Serial.println("");

            }
            if (isConn && String(nSSID)!=WiFi.SSID(i)) {
            Serial.print(“Already connected to “);
            Serial.println(nSSID);
            Serial.print(“Signal: “);
            Serial.println(WiFi.RSSI());
            Serial.print(“IP: “);
            Serial.println(WiFi.localIP());
            }
            else {isConn=false;}

            return isConn;
            }

            void loop()
            {
            if (!chkWifi(“WIFI1″,”pass1”)) {
            Serial.println(“–> Not connect <–“);
            if (!chkWifi(“Wifi2″,”pass2”)) {
            Serial.println(“–> Not connect <–“);
            }
            }
            delay(2000);
            }

          • It’s some config of wifi or something. Went to my office with different router and it connects well and now i can see webserver and all.
            My router at home is HS8247W from Vodafone.
            But my sketch works at home also….

  14. I have the same problem.
    My router is also the HS8247W from Vodafone.
    Have you found a solution?
    I was asking for support from Vodafone, they were responding to me in 48 hours…

    Reply
    • Hi, Viorino.

      It takes longer but it will connect. Probably Vodafone don´t have a answer for you If they do can you share it?

      Regards,

      Reply
  15. Hi, Rui and Sara.
    Just a simple question, may i work with the BMP180 with this code?
    If can work, and how i can change the code?
    Thanks for good work!
    Regards.

    Reply
  16. Hi, Sara.
    I was already did it, but i got the problem with the I2C, because library of the BMP085 not included
    “TwoWire I2Cone = TwoWire(1);”

    so at the sensor funtion didn´t work theses 2 lines:
    #I2Cone.begin(SDA, SCL, 100000);
    #bool status1 = bme.begin(0x76, &I2Cone);
    some solution for this problem?
    Thanks so much for help!

    Reply
  17. Hallo,
    ich hab dieses schöne Projekt nachgebaut. Mein Sender ist ein arduino mini pro mit RF95 Modul und den BME280E als Sensor.
    Der Empfänger ist ein Heltec WIFI LoRa 32 V2 SX1276 868MHz mit OLED und einer selbstgebauten Antenne (Doppelquad Antenne) für 868MHz.
    Für WLAN hab ich eine kleine externe Antenne angeschlossen.
    In der Software hab ich nur meine Pins zum LoRa Modul geändert.

    Der Sender ist im Garten 1km entfernt. Der Empfänger ist auf den Dachboden installiert.
    Mein Computer und der Router ist in der Wohnung 25m vom Empfänger entfernt.
    Es geht alles wunderbar. Der Empänger verbindet sich mit den Router und aller 5 Minuten kommen neue Daten vom Sender.
    Das geht aber nur solange wie ich die Webseite in kurzen Abständen aufrufe.
    Bei langen Pausen verliert sich die WLAN Verbindung zum Router. Ich kann dann die Seite nicht mehr abrufen.
    Wenn ich den Empänger kurz abschalte oder beim Reset des Empfängers geht wieder alles normal.

    Wer kann mir helfen? Ist es möglich das der Empfänger den Router regelmäßig kontaktiert um die Verbindung nicht zu verlieren.
    Welche Möglichkeit gibt es um die WLAN Verbindung ständig zu halten ?

    Für Hilfe wäre ich sehr dankbar.

    Eric

    Hi,
    I have recreated this beautiful project. My transmitter is an arduino mini pro with an RF95 module and the BME280E as a sensor.
    The receiver is a Heltec WIFI LoRa 32 V2 SX1276 868MHz with OLED and a self-made antenna (double quad antenna) for 868MHz.
    For WLAN I have connected a small external antenna.
    In the software I only changed my pins for the LoRa module.

    The transmitter is 1km away in the garden. The receiver is installed in the attic.
    My computer and the router are 25m away from the receiver in the apartment.
    Everything is going well. The receiver connects to the router and new data comes from the transmitter every 5 minutes.
    But this only works as long as I visit the website at short intervals.
    With long breaks, the WLAN connection to the router is lost. I can then no longer access the page.
    If I switch off the receiver briefly or when the receiver is reset, everything will go back to normal.

    Who can help me? Is it possible for the receiver to contact the router regularly in order not to lose the connection.
    What possibility is there to keep the WiFi connection constantly?

    I would be very thankful for help.
    Eric

    Reply
  18. Hi Sara
    Thank you for the quick information.
    At the weekend I only come to test. I will contact you again.
    many Greetings
    Eric

    Reply
  19. Hi Sara,
    I think everything seems to be ok now. An attempt is made to re-establish the WLAN connection every 30 seconds if the connection was not ok.
    Many thanks for your help.
    https://www.reddit.com/r/esp32/comments/7trl0f/reconnect_to_wifi/
    I ran into a similar problem this weekend where running my code overnight would result in a loss of wifi connection that never reconnected automatically. I solved it within my main loop (in my case I’m re-checking every 30 seconds). Below is the code but I think the main function you are looking for is WiFi.disconnect();
    unsigned long check_wifi = 30000;

    void loop() {
    // if wifi is down, try reconnecting every 30 seconds
    if ((WiFi.status() != WL_CONNECTED) && (millis() > check_wifi)) {
    Serial.println(“Reconnecting to WiFi…”);
    WiFi.disconnect();
    WiFi.begin(SSID, PASS);
    check_wifi = millis() + 30000;
    }
    }

    I added that in the loop
    greeting
    Eric

    Reply
  20. hi sara
    i wanna ask u something. btw im a newbie. the question is how about using esp32 + chip lora sx1278 without oled? could this work base on ur project?

    Reply
    • Hi.
      Yes, it will also work.
      You will need to remove the lines of code that use OLED.
      Additionally, you probably need to change the pins assigned to your LoRa module.
      Usually, different LoRa boards use different pin assignments.
      Regards,
      Sara

      Reply
  21. Sara and Rui,
    I have the Lora working when both sender and receiver are in the same room about 20 feet apart and even in the next building 50 feet away. I now have them set up about 200 feet away and I get gibberish like this
    AF��,�٩ȳ���[�;��\;ȍ�s̘ᵸ����_�xٲi��|�iQ����'(�G{8Lz�’z�f��W��B�,-��G�zRѶ�1������;3 – – RSSI -120
    RSSI when close is around -92.
    The receiver uploads data to Adafruit IO, works fine when closer.
    is this weak signal, if so can I increase it? I have antennas on both units, correct length.
    Also I have the sender in Deep Sleep on solar and battery. the most I have been able to sleep the sender is 30 minutes like your code, but it stops working if more that 1800 sec. Is that the max? I would like to sleep for 2 hours between transmissions. Using ESP32 as your tutorial.
    Thanks

    Reply
  22. Great project!
    Please add also a soil moisture sensor to this project.
    It will be great for iot in agricultural fields.
    Regards

    Reply

Leave a Comment

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.