ESP32

Wireless Water Level Indicator using LoRa and ESP32

Wireless Water Level Indicator:

 

Wireless Water Level Indicator using LoRa and ESP32- In today’s article, you will learn how to make a long range Wireless Water Level Indicator using a waterproof Ultrasonic Sensor JSN SR04T/AJ-SR04M and a pair of LILYGO TTGO LoRa32 modules.

Wireless Water Level Indicator

TTGO LoRa32 is 868MHz and is based on the LoRa SX1276 chip combined with ESP32 WiFi + Bluetooth and an I2C supported 0.96-inches OLED display module. So many things on this tiny board, its quite impressive. You don’t have to do any soldering, just upload the desired code and start monitoring anything you want.




Wireless Water Level Indicator

When these modules are not in line-of-sight means when there are lots of obstacles in between then the range is 77 meters.

Wireless Water Level Indicator

And when both the modules are in line-of-sight then the wireless communication range is more than 1Km. I practically demonstrated this in my getting started video on the LoRa32. So, if you have got any questions, regarding its technical specifications, how to install the required libraries, and how to write your first program, then I recommend you should read my getting started article on the TTGO LoRa32 Modules.



HC-SR04 Vs Waterproof Ultrasonic Sensor:

Wireless Water Level Indicator

HC-SR04 and the waterproof JSN SR04T are both popular ultrasonic sensors that are commonly used in distance measurement applications. However, there are some differences between them that may make one a better choice depending on your specific needs.

The HC-SR04 is a low-cost ultrasonic sensor that can measure distances from 2cm to 4 meters with an accuracy of about 3mm. It has a working voltage range of 5V DC and consumes very low power. The HC-SR04 is easy to use, it can be easily interfaced with microcontrollers, and can be easily found in the market.

The JSN SR04T, on the other hand, is a more advanced ultrasonic sensor that has a longer sensing range of up to 6 meters and a higher accuracy of up to 1mm. It operates at a wider voltage range of 3-5V DC and has a built-in temperature compensation feature that helps to improve accuracy even in varying temperatures. However, the JSN SR04T is relatively more expensive and may be harder to find in the market.

In short, if you need a low-cost ultrasonic sensor with a shorter range and don’t require high accuracy, then the HC-SR04 may be a good choice. However, if you need a more advanced ultrasonic sensor with a longer range and higher accuracy, and you are willing to spend more, then the JSN SR04T could be a better option. For more technical specifications, features, and applications; read my article on the Waterproof Ultrasonic Sensor.

A few days ago, I created an IoT-based water level monitoring system using the same waterproof ultrasonic sensor. In that system, the water pump would automatically turn on and off. However, this time, I don’t want the water pump to automatically turn on/off. I want the control of the water pump to be in my hands and the hands of my family members, and I also don’t want us to be dependent on the internet.

Wireless Water Level Indicator

The switch for the water pump is installed here and next to it I can install my water level indicator or monitor. Then, all the family members can check the water level in the water tank and turn the water pump on/off accordingly. So, this is exactly, what I am going to do. So, without any further delay, let’s get started!!!



Amazon Links:

TTGO LoRa32

Official product link

Waterproof Ultrasonic Sensor

Other Tools and Components:

Top Arduino Sensors:

Super Starter kit for Beginners

Digital Oscilloscopes

Variable Supply

Digital Multimeter

Soldering iron kits

PCB small portable drill machines

*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!

Wireless Water Level Indicator, Circuit Diagram:

Transmitter side circuit diagram:

Wireless Water Level Indicator

The 5v and GND wires of the Waterproof Ultrasonic Sensor are connected to the 5V and GND pins on the LoRa32 module. Whereas the Trigger and Echo pins are connected to the GPIO pins 12 and 13 respectively.



TTGO LoRa32 Pins Diagram:

Wireless Water Level Indicator

You can see the SSD1306 Oled display module SDA and SCL pins are connected to the Pins 04 and 15. And the SX1276 LoRa transceiver module MOSI, SCLK, CS, DIO, RST, and MISO pins are connected to pins 27, 5, 18, 26, 14, and 19. All the other pins are clearly labeled. You can see the 5V and 3.3V pins are available on both sides. We have got three GND pins. And all these other GPIO pins can be used for connecting input and output devices.

Receiver Side Circuit Diagram:

Wireless Water Level Indicator

On the receiver side, we don’t need to do anything as the LoRa and Oled display module are already wired up. Now, let’s go ahead and take a look at the transmitter and receiver side programming.




Altium Designer + Altium 365 + Octopart:

Arduino LoRa Free SMS

Altium 365 lets you hold the fastest design reviews ever. Share your designs from anywhere and with anyone with a single click. it’s easy, leave a comment tagging your teammate and they’ll instantly receive an email with a link to the design. Anyone you invite can open the design using a web browser. Using the browser interface, you’re able to comment, markup, cross probe, inspect, and more. Comments are attached directly to the project, making them viewable within Altium designer as well as through the browser interface. Design, share, and manufacture, all in the same space with nothing extra to install or configure. Connect to the platform directly from Altium Designer without changing how you already design electronics. Altium 365 requires no additional licenses and comes included with your subscription plan.

Get real-time component insights as you design with Octopart built into Altium 365. Octopart is the fastest search engine for electronic parts and gives you the most up-to-date part data like specs, datasheets, cad models, and how much the part costs at different amounts etc. Right in the design environment so you can focus on your designs. Start with Altium Designer and Activate Altium 365. Search for electronic parts on Octopart.



Wireless Water Level Indicator Programming:

If you already have an ESP32 board installed, then there shouldn’t be any problem, you just need to install these libraries.

LoRa.h

Adafruit_GFX

Adafruit_ssd1306

But if you don’t have an ESP32 board installed, then you need to install it first, and the complete process is explained in the article Getting started with ESP32 WiFi + Bluetooth Module.

Anyway, once you install the ESP32 board then you can go to the Tools menu, Board, ESP32 Arduino, and from the boards list you can select TTGO LoRa32-OLED.

Wireless Water Level Indicator



Transmitter Side LoRa32 Code:

#include <SPI.h>
#include <LoRa.h>

//Libraries for OLED Display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.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

#define trigPin 12
#define echoPin 13
int distance;

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

//packet counter
int counter = 0;
String LoRaData;
String outgoing;              // outgoing message
int relay1Status;
byte msgCount = 0;            // count of outgoing messages
byte localAddress = 0xBB;     // address of this device
byte destination = 0xFF;      // destination to send to
long lastSendTime = 0;        // last send time
int interval = 50;     
String Mymessage;

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

void setup() {
  //initialize Serial Monitor
  Serial.begin(115200);
 pinMode(trigPin, OUTPUT);
 pinMode(echoPin, INPUT);
  //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 ");
  display.display();
  
  Serial.println("LoRa Sender Test");

  //SPI LoRa pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //setup LoRa transceiver module
  LoRa.setPins(SS, RST, DIO0);
  
  if (!LoRa.begin(BAND)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  Serial.println("LoRa Initializing OK!");
  display.setCursor(0,10);
  display.print("LoRa Initializing OK!");
  display.display();
  delay(2000);
}

void loop() {
   if (millis() - lastSendTime > interval) {
    // Clear the trigPin by setting it LOW:
  digitalWrite(trigPin, LOW);
  
  delayMicroseconds(5);

 // Trigger the sensor by setting the trigPin high for 10 microseconds:
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // Read the echoPin. pulseIn() returns the duration (length of the pulse) in microseconds:
int  duration = pulseIn(echoPin, HIGH);
  
  // Calculate the distance:
//  distance = duration*0.034/2;
  
distance = (duration/2) / 29.1;
  // Print the distance on the Serial Monitor (Ctrl+Shift+M):
int waterLevelPer = map(distance,22,51, 100, 0);
if(waterLevelPer<0)
{
  waterLevelPer=0;
  }
if (waterLevelPer>100)
{
  waterLevelPer=100;
  }
  Serial.print("Distance = ");
  Serial.print(distance);
  Serial.println(" cm");
  Serial.println(waterLevelPer);
    Mymessage = Mymessage + distance +","+ waterLevelPer;  
    sendMessage(Mymessage);
     display.clearDisplay();
  display.setCursor(0,0);
  display.println("LORA Transmitter");
  display.setCursor(0,20);
  display.setTextSize(1);
  display.print("LoRa packet Sending");
  display.setCursor(0,30);
  display.print(Mymessage);      
  display.display();
  delay(1000);
    
    delay(100);
    Mymessage = "";
  
 //Serial.println("Sending " + message);
    lastSendTime = millis();            // timestamp the message
    interval = random(50) + 100;  
    
    }
     
}




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
}

If you watch my previous videos or read my articles on the LoRa32 and IoT based Water Level monitoring system, then you would know how I combined the two codes.

You can clearly see, I am using the same pins definitions. I am using the same local address and destination address. If you want to monitor multiple water tanks then you will have to use those addresses. You can read my article on how to use multiple LoRa transmitters with a single LoRa receiver.

if (millis() - lastSendTime > interval) {
    // Clear the trigPin by setting it LOW:
  digitalWrite(trigPin, LOW);
  
  delayMicroseconds(5);

 // Trigger the sensor by setting the trigPin high for 10 microseconds:
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // Read the echoPin. pulseIn() returns the duration (length of the pulse) in microseconds:
int  duration = pulseIn(echoPin, HIGH);
  
  // Calculate the distance:
//  distance = duration*0.034/2;
  
distance = (duration/2) / 29.1;
  // Print the distance on the Serial Monitor (Ctrl+Shift+M):
int waterLevelPer = map(distance,22,51, 100, 0);

This set of instructions is used to calculate the water level distance and then using the map function we convert the distance information into water level percentage.

Mymessage = Mymessage + distance +","+ waterLevelPer;  
    sendMessage(Mymessage);
   
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("LORA Transmitter");
  display.setCursor(0,20);
  display.setTextSize(1);
  display.print("LoRa packet Sending");
  display.setCursor(0,30);
  display.print(Mymessage);      
  display.display();
  delay(1000);
    
    delay(100);
    Mymessage = "";
  
 //Serial.println("Sending " + message);
    lastSendTime = millis();            // timestamp the message
    interval = random(50) + 100;  

Then we make a message consisting of the distance and water level percentage. You can see I am using comma as the delimiter. It will help me in splitting the message to retrieve the distance and water level percentage values. Anyways, finally I send the message, and at the same time I also print this message on the Oled display module. Now, let’s go ahead and take a look at the receiver side programming.



Receiver Side LoRa32 Code:

#include <SPI.h>
#include <LoRa.h>

//Libraries for OLED Display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.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
#define led 12
//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


Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RST);
byte msgCount = 0;            // count of outgoing messages

String outgoing;              // outgoing message
String LoRaData;
byte localAddress = 0xFF;     // address of this device
byte destination = 0xBB;      // destination to send to
long lastSendTime = 0;        // last send time
int interval = 50;          // interval between sends
String statusmessage = "";

void setup() { 
  //initialize Serial Monitor
  Serial.begin(115200);
  
  //reset OLED display via software
  pinMode(OLED_RST, OUTPUT);
  pinMode(led,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(2);
  display.setCursor(0,0);
  display.print("LORA RECEIVER ");
  display.display();

  Serial.println("LoRa Receiver Test");
  
  //SPI LoRa pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //setup LoRa transceiver module
  LoRa.setPins(SS, RST, DIO0);

  if (!LoRa.begin(BAND)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  Serial.println("LoRa Initializing OK!");
  display.setCursor(0,10);
  display.println("LoRa Initializing OK!");
  display.display();  
}

void loop() {
  // parse for a packet, and call onReceive with the result:
  onReceive(LoRa.parsePacket());
}
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()) {
      LoRaData = LoRa.readString();
      //Serial.print(LoRaData);
    }

String q = getValue(LoRaData, ',', 0); 
String r = getValue(LoRaData, ',', 1); 
int distance = q.toInt();
int waterLevelPer = r.toInt();
Serial.print("distance = ");
Serial.println(distance);
Serial.print("waterLevelPer = ");
Serial.println(waterLevelPer);


    String rssi = "RSSI " + String(LoRa.packetRssi(), DEC) ;
    //Serial.println(rssi);

  
  delay(10);

  
   // Dsiplay information
   display.clearDisplay();
   display.setCursor(5,0);
    display.setTextSize(2);
   display.print("W-L-Meter");

   display.setCursor(0,25);
    display.setTextSize(2);
//   display.print("Dist:");
//   display.setCursor(70,15);
//    display.setTextSize(2);
//   display.print(distance);
       display.print("Dist:"+String(distance)+"cm");    

   display.setCursor(0,50);
    display.setTextSize(2);
   //display.print("W-L:");
    display.print("W-L:"+String(waterLevelPer)+"%");    

//   display.setCursor(92,40);
//    display.setTextSize(2);
//   display.print(waterLevelPer);
   display.display();   
  
}
String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;
 
    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

On the receiver side we have got the same libraries and the same pins definitions. So, let’s go to the loop() function.

We simply read the packet, and then using the getValue() function we simply split the message using comma as the delimiter and the retrieved values are stored in variables distance and waterLevelPer. And then finally, these values are printed on the Oled display module.

getValue() function is a user-defined function and its job is to split the message. You can select any character as the delimiter. So, that’s all about the programming. I have already uploaded these programs and now let’s start our practical demonstration.




Wireless Water Level Indicator Practical Demonstration:

Wireless Water Level Indicator

I have powered up the transmitter side using my designed 5V and 3A power supply and my created 4S lithium Ion Battery. This setup makes the transmitter side completely portable. You can use any 5V regulated power supply. And additionally, you can also connect a lithium-ion battery, LoRa32 has an onboard charging circuit. So, if in case there is any power failure the communication won’t be interrupted.

You can also use a solar panel. All you need is to connect your solar panel to this 5V and 3A power supply, it can take input voltages up to 28 volts, and at the output, it gives regulated 5V and 3A. So, during the day time, its going to use Solar energy and during the night time or cloudy weather, it’s going to use a lithium-ion battery as the power source.

Wireless Water Level Indicator

Since nothing is connected on the receiver side except the LoRa and Oled display module, so we can use a single cell lithium ion battery or even a cell phone charger or any other 5V power supply.



Anyways, you can already see the distance and water level percentage on the receiver side. Let me move my hand in front of the Ultrasonic Sensor and you will see a change in the distance and water level percentage.

Wireless Water Level Indicator

When I started to move my hand in front of the Ultrasonic Sensor the distance and Water Level Percentage values started to change, as you can see in the image above.

Wireless Water Level Indicator



Let’s think of this Bucket as the Water Tank and we have to measure the water level inside this Tank. The Ultrasonic Sensor is in its place. So, let’s go ahead and fill this Water Tank.

Wireless Water Level Indicator

So, that’s all about the Wireless Water Level Indicator.

Watch Video Tutorial:

 

 

Engr Fahad

My name is Shahzada Fahad and I am an Electrical Engineer. I have been doing Job in UAE as a site engineer in an Electrical Construction Company. Currently, I am running my own YouTube channel "Electronic Clinic", and managing this Website. My Hobbies are * Watching Movies * Music * Martial Arts * Photography * Travelling * Make Sketches and so on...

4 Comments

  1. Hello Shahzada, I’m experimenting with a similar item but wondering if there is a way to have these working only on a battery and take the reading and transmit it every 5-15 minutes to save on power. I realize in your example you use an Arduino but can these stand alone with a timer set to do this on just a battery.

    My aim is to have many of these transmitting to a central Raspberry Pi that will transmit via cell internet.

    Great project you have especially for those of us who have basements and like to check on their sump occasionally via remote location

  2. Hi Shahzada, excellent and very useful project indeed. One question, How can I send the level signal to the Blynk platform from the receiver module? Appreciate if you can modify the receiver code and include this feature also.

  3. Hi great project. Have you considered making this send to multiple sender/receivers so they can send over longer distances. This might be especially good for where the line of sight is shorter like in cities. I’m currently working on a similar project so would be interested in sharing thoughts. I got the battery this working

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button