Wireless Soil Moisture Monitor using Arduino & LoRa SX1278
Table of Contents
Wireless Soil Moisture Monitor:
Wireless Soil Moisture Monitor using Arduino & Lora- In this article, you will learn how to make a Wireless Soil Moisture Monitor using Arduino, a pair of Long Range SX1278 LoRa transceiver modules, a Soil Moisture Sensor, DHT11 Sensor, a one-channel relay module,and SSD1306 I2C supported Oled display module.
In my previous article, I have described the specifications of this new type of soil moisture sensor, pinout, and its application along with an Arduino quite in detail. I wrote that article, especially for the beginners and I touched upon everything at a fairly elementary level and explained how will you print the value of soil moisture on the Oled display module. Besides this, I also attempted to elaborate on how will you set the threshold value of soil moisture, so that whenever the soil moisture declines below the threshold level, the buzzer turns ON and starts to beep automatically. I also went on to explain howwill you use that beginner’s level project as a meter for measuring the soil moisture of different plants.
I am talking much about the previous article because the project that I am going to design today, is just a modified version of my previous project. So, in my article, I am not going to explain all those things, which have been extensively discussed and explained during my previous project. Hence, if you are on a beginner’s level, I will highly recommend you to go and read my previous article. So, let us move on to today’s project, which is a wireless soil moisture monitor.
There is an empty plot beside our home, which is actually owned by my uncle. It has not been in any type of use and lying futile since long. So, an idea to grow some plants and vegetables on it clicked in my mind. As I explained in my previous article, plants may be damaged as a result of low or high soil moisture levels. An ordinary soil moisture monitoring system won’t work here properly, because the plot is spaced apart from my home at quite a distance. I cannot use Bluetooth technology either, because Bluetooth’s communication range is short, and it is not actually possible to do real-time monitoring through GSM technology. If you want to use GSM technology just for Alert message purposes, then it is ok. But the real-time monitoring is merely impossible through the application of just GSM technology.
I already have a few LoRa transceiver modules available with me, using which I am going to design a long-range wireless soil moisture monitor. By means of using LoRa transceiver modules, we can monitor different sensors and control different loads within a range of even kilometers.
Anyway, here is a prototype model of my wireless soil moisture monitor. On the left side is the transmitter, along which a soil moisture sensor, DHT11 sensor, SX1278 LoRa transceiver module, one channel relay module, and a 220 Vac light bulb, are connected. I am using a bulb instead of a water pump so that at the time of carrying out a practical demonstration, you can clearly see for yourself how do I turn this bulb ON or OFF. You can also use a 220 Vac water pump and you can also use a DC water pump; it all depends on your choice. If you have to control a large water pump, then you better use a power relay. I have written a separate article on IoT Power Relay, in which I have practically demonstrated how to control a large water pump.
And on the right side is the receiver. It has been connected with a button, an Oled display module, and an SX1278 LoRa transceiver module. Now I am going to explain it’s working a bit, and then we will kick off our practical demonstration.
I will install the receiver side inside my room. I am not using a buzzer this time around, because I can read the soil moisture values on the Oled display at any time and then I can control the water pump accordingly. So, let me tell you how it works; the transmitter will send values related to the soil moisture, temperature, humidity, and motor status, which will show on the Oled display module fitted on the receiver’s side. Now, let us go ahead and start our practical demonstration.
I have powered up the transmitter as well as the receiver’s side. You can view soil moisture, temperature, humidity, and motor status on the Oled display. At this very moment, soil moisture is 16%. Now, if I press the button, the light will turn ON, which will indicate that the motor has turned ON and I also simultaneously receive feedback that the motor has powered ON.
Now, I am going to add some water and you will see an increase in the soil moisture value.
Let’s say that this soil moisture value is enough, so now I can press the button and turn the pump OFF wirelessly.
You can see that the motor status has changed from 0 to 1 on the display.
It is so simple, isn’t it? All we need is to read the soil moisture value on the display and then accordingly control the water pump on the remote side.
It can also be used as a wireless soil moisture meter; this way you can move around and measure the soil moisture of different plants while the supervisor in the control room keeps an eye on the soil moisture values.I am sure by now, you might have got an idea of how does this system work. So, without any further delay let’s get started!!!
Amazon Links:
Arduino Nano USB-C Type (Recommended)
DHT11 Temperature and Humidity Sensor
*Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.
About the Soil Moisture Sensor:
Here is my Soil moisture detection kit. Let’s first take a look at the interface circuit. The board is provided with this blue color potentiometer which is used to adjust the sensitivity. This board is also provided with the most stable LM393 comparator chip and with On-board power and signal indicator LEDs to display the module working status. The working voltage of this module is from 3.3V to 12 volts. Because of this wide input voltage range, this Soil moisture sensor can be used with 3.3V and 5V compatible controller boards.
All the 4 headers are clearly labeled. The first pin is the Analog output pin and it should be connected with the Analog pin on the Arduino. The 2nd pin is the digital output put pin which can be connected with any digital pin on the Arduino or any other controller board. 3rd pin is the ground pin and the last pin is the VCC pin which you can connect with 3.3V or 5V.
The sensor probes are waterproofed and are made of highly corrosion-resistant material to ensure long lifetime up to 6 months after being plugged into the soil. The Sensor Cable length is 1.3 meters.
Specifications:
Working Voltage: DC 3.3-12V
Working Current: <20m(Output Current:<30mA)
Module Size: 36X15X6mm
Sensor Pin Pitch:5mm
Sensor Cable Length:1.3meter/3.94ft
Sensor Connecting Port: XH2.54 2P
SX1278 LoRa Module:
In this project we are using the same SX-1278 LoRa modules for the transmitter and receiver. In order to use it as a receiver and transmitter we will make change in the code. Lora SX-1278 long distance wireless transceiver module integrates Semtech RF transceiver chip SX1278, which adopts LoRa TM Spread Spectrum modulation frequency hopping technique. The features of long distance and high sensitivity (-139dBm) make this module perform better than FSK and GFSK module. Multi-signal won’t affect each other even in crowd frequency environment; it comes with strong anti-interference performance. This module is 100mW and ultra small size, widely used in AMR , remote industrial control filed.
Features:
- Frequency Range: 868 / 915 MHz
- Sensitivity up to -139dBm @Lora
- Maximum output power: 20 dBm
- 13mA@receiver mode
- Sleep current <200 nA
- Data transfer rate: @FSK,1.2-300 Kbps
- @Lora TM, 0.018-37.5 Kbps
- Lora TM, FSK, GFSK & OOK Modulation mode
- Built-in ESD Protection
- 127 dB Dynamic Range RSSI
- Packet engine up to 256 bytes with FIFO and CRC
- Hopping frequency
- Built-in temperature sensor and low battery indicator
- Excellent blocking immunity
- Operating Temperature Range:-40 ~ + 85 °C4
Applications:
- Remote control
- Remote meter reading
- Home security alarm and remote keyless entry
- Industrial control
- Home automation remote sensing
- Individual data records
- Toys control
- Sensor network
- Tire pressure monitoring
- Health monitoring
- Wireless PC peripherals
- Tag reading and writing
DHT11 Sensor:
DHT11 is a Humidity and Temperature Sensor, which generates calibrated digital output. DHT11 can be interface with any microcontroller like Arduino, Raspberry Pi, etc. and get instantaneous results. DHT11 is a low cost humidity and temperature sensor which provides high reliability and long term stability.
It uses a capacitive humidity sensor and a thermistor to measure the surrounding air, and outputs a digital signal on the data pin (no analog input pins needed). It is very simple to use, and libraries and sample codes are available for Arduino and Raspberry Pi.
This module makes is easy to connect the DHT11 sensor to an Arduino or microcontroller as includes the pull up resistor required to use the sensor. Only three connections are required to be made to use the sensor – Vcc, Gnd and Output.
It has high reliability and excellent long-term stability, thanks to the exclusive digital signal acquisition technique and temperature & humidity sensing technology.
DHT11 Specifications:-
- Power Supply: 3.3~5.5V DC
- Output: 4 pin single row
- Measurement Range: Humidity 20-90%RH, Temperature 0~50℃
- Accuracy: Humidity +-5%RH, Temperature +-2℃
- Resolution: Humidity 1%RH, Temperature 1℃
- Interchangeability: Fully Interchangeable
- Long-Term Stability:<±1%RH/year
DHT11 Pin Description:-
- Pin 1: Power +Ve (3.3VDC to 5.5VDC Max wrt. GND)
- Pin 2: Serial Data Output
- Pin 3: Power Ground or Power –Ve
Wireless Soil moisture Monitor Tx Side:
This is the circuit diagram of the Wireless Soil Moisture Monitor transmitter side. The VCC of the LoRa module is connected with 3.3V of the Arduino. The MISO Pin of the LoRa module is connected with the Arduino pin 12. The MOSI pin is connected with pin 11. The SCK pin of the LoRa module is connected with pin 13. The NSS pin is connected with the Arduino’s pin 10 and the ground pin of the LoRa module is connected with the Arduino’s GND.
On the left side, you can see a 5V regulated power supply based on the LM7805 voltage regulator. We use this regulated 5 volts to power up the Arduino and all the other electronics.
The signal or data pin of the DHT11 temperature and humidity sensor is connected with the Analog pin A5 while the VCC and GND pins are connected with the Arduino’s 3.3V and GND pins.
The analog output of the soil moisture sensor board is connected with the Analog pin A1. While the VCC and GND pins are connected with the Arduino’s 5V and GND.
The 5V relay module input wire is connected with the Arduino digital pin 6. We use this relay to controla 12V DC water pump. But if you want you can also control a 110/220Vac water pump.
Wireless Soil moisture Monitor Rx Side:
The Lora module connections with the Arduino and the 5V regulated power supply wiring remains exactly the same.
The SDA and SCL or SCK pins of the SSD1306 Oled display module are connected with the A4 and A5 pins of the Arduino. While the VCC and GND pins of the Oled display module are connected with the Arduino’s 5 volts and GND pins.
A button is connected with the Digital pin 5 of the Arduino which is used to control the Water pump on the transmitter side.
Required Libraries:
I have written two programs, one for the transmitter side and the other one for the receiver side. Anyway, before you start the programming, first of all, make sure you download all the necessary libraries.
Wireless Soil Moisture Monitor Tx side Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
/* Transmitter, nano */ #include <SPI.h> // include libraries #include <Wire.h> #include <LoRa.h> #include "dht.h" #define dht_apin A5 // Connect DHT data pin with Analog Pin 5 int Relay=6; //Relay will be connected with digital pin 6 dht DHT; int msensor = A1; // moisture sensor is connected with the analog pin A1 of the Arduino int msvalue = 0; // moisture sensor value int mspercent; // moisture value in percentage String stat=""; // Motor status int buttonstat; float temp; // to save the temperature value int hum; //humidity value 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 = 200; // interval between sends String Mymessage = ""; void setup() { Serial.begin(9600); // initialize serial pinMode(msensor, INPUT); pinMode(Relay, OUTPUT); Serial.println("LoRa Duplex"); if (!LoRa.begin(433E6)) { // initialize ratio at 915 MHz Serial.println("LoRa init failed. Check your connections."); while (true); // if failed, do nothing } Serial.println("LoRa init succeeded."); } void loop() { if (millis() - lastSendTime > interval) { msvalue = analogRead(msensor); mspercent = map(msvalue,0,1023,100,0); // To display the soil moisture value in percentage DHT.read11(dht_apin); hum=DHT.humidity; temp=DHT.temperature; relay1Status = digitalRead(Relay); Mymessage = Mymessage + mspercent +"," + temp + "," + hum +","+relay1Status; sendMessage(Mymessage); delay(100); Mymessage = ""; //Serial.println("Sending " + message); lastSendTime = millis(); // timestamp the message interval = random(50) + 100; } // parse for a packet, and call onReceive with the result: onReceive(LoRa.parsePacket()); } void sendMessage(String outgoing) { LoRa.beginPacket(); // start packet LoRa.write(destination); // add destination address LoRa.write(localAddress); // add sender address LoRa.write(msgCount); // add message ID LoRa.write(outgoing.length()); // add payload length LoRa.print(outgoing); // add payload LoRa.endPacket(); // finish packet and send it msgCount++; // increment message ID } void onReceive(int packetSize) { if (packetSize == 0) return; // if there's no packet, return // read packet header bytes: int recipient = LoRa.read(); // recipient address byte sender = LoRa.read(); // sender address byte incomingMsgId = LoRa.read(); // incoming msg ID byte incomingLength = LoRa.read(); // incoming msg length String incoming = ""; while (LoRa.available()) { incoming += (char)LoRa.read(); } if (incomingLength != incoming.length()) { // check length for error //Serial.println("error: message length does not match length"); ; return; // skip rest of function } // if the recipient isn't this device or broadcast, if (recipient != localAddress && recipient != 0xFF) { // Serial.println("This message is not for me."); ; return; // skip rest of function } String q = getValue(incoming, ',', 0); buttonstat = q.toInt();// button status if(buttonstat==HIGH) { digitalWrite(Relay,HIGH); } else { digitalWrite(Relay,LOW); } incoming = ""; } 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]) : ""; } |
This code is the modified version of my previous two projects. LoRa two way communication project and Soil moisture monitoring system. So, for a complete explanationread my previous articles. Anyway, the main purpose of this program is to read the Soil moisture sensor, Dht11 sensor, and relay status. And send these values to the receiver side. And at the same time, it also monitors if any command is received to control the water pump. Now, let’s take a look at the receiver side programming.
Wireless Soil Moisture Monitor Rx side Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
/* Receiver, Nano */ #include <SPI.h> // include libraries #include <LoRa.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> int mstat; int button=5; int relay1Status; float temp; int hum; String stat=""; // Motor status String outgoing; // outgoing message int smoisture; byte msgCount = 0; // count of outgoing messages byte localAddress = 0xFF; // address of this device byte destination = 0xBB; // destination to send to long lastSendTime = 0; // last send time int interval = 200; // interval between sends String statusmessage = ""; #define SCREEN_WIDTH 128 // ORelay display width, in pixels #define SCREEN_HEIGHT 64 // ORelay display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define ORelay_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, ORelay_RESET); void setup() { Serial.begin(9600); // initialize serial; pinMode(button,INPUT_PULLUP); Serial.println("LoRa Duplex"); if (!LoRa.begin(433E6)) { // initialize ratio at 915 MHz Serial.println("LoRa init failed. Check your connections."); while (true); // if failed, do nothing } Serial.println("LoRa init succeeded."); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); delay(2000); display.clearDisplay(); display.setTextColor(WHITE); } void loop() { if (millis() - lastSendTime > interval) { relay1Status = digitalRead(button); statusmessage = statusmessage + relay1Status; sendMessage(statusmessage); delay(100); statusmessage = ""; lastSendTime = millis(); // timestamp the message interval = random(50) + 100; // 2-3 seconds } // parse for a packet, and call onReceive with the result: onReceive(LoRa.parsePacket()); } void sendMessage(String outgoing) { LoRa.beginPacket(); // start packet LoRa.write(destination); // add destination address LoRa.write(localAddress); // add sender address LoRa.write(msgCount); // add message ID LoRa.write(outgoing.length()); // add payload length LoRa.print(outgoing); // add payload LoRa.endPacket(); // finish packet and send it msgCount++; // increment message ID } void onReceive(int packetSize) { if (packetSize == 0) return; // if there's no packet, return // read packet header bytes: int recipient = LoRa.read(); // recipient address byte sender = LoRa.read(); // sender address byte incomingMsgId = LoRa.read(); // incoming msg ID byte incomingLength = LoRa.read(); // incoming msg length String incoming = ""; while (LoRa.available()) { incoming += (char)LoRa.read(); } if (incomingLength != incoming.length()) { // check length for error // Serial.println("error: message length does not match length"); ; return; // skip rest of function } // if the recipient isn't this device or broadcast, if (recipient != localAddress && recipient != 0xFF) { //Serial.println("This message is not for me."); ; return; // skip rest of function } String q = getValue(incoming, ',', 0); String r = getValue(incoming, ',', 1); String s = getValue(incoming, ',', 2); String t = getValue(incoming, ',', 3); smoisture = q.toInt();// soil moisture value temp = r.toFloat();// tempreature value hum = s.toInt();// humidity value Serial.println(hum); mstat = t.toInt(); Serial.println(mstat); //if(mstat==1) //{ // stat="ON"; // } // else // { // stat="OFF"; // // } display.clearDisplay(); display.setCursor(10,0); display.setTextSize(2); display.setTextColor(WHITE); display.print("SM:"+String(smoisture)+"%"); display.setCursor(10,20); display.setTextSize(2); //display.print("C: "); display.print(temp); display.print((char)247); display.print("C"); display.setCursor(10,45); display.setTextSize(2); display.print("H:"+String(hum)+"%"+" M:"+String(mstat)); display.display(); delay(1000); incoming = ""; } 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 read the button ON or OFF status and send it to the transmitter side to control the water pump. And at the same time, it also monitors if any message is received from the transmitter side. The message simply consists of comma-separated values. The message is first split and the corresponding sensor values are printed on the Oled display module. So, that’s all about the programming.