NRF24L01 Arduino Based Soil Moisture Monitoring System with Feedback
Table of Contents
NRF24L01 Arduino:
NRF24L01 Arduino Based Soil Moisture Monitoring System with Feedback– In this article, you will learn how to make a Wireless Soil Moisture Monitoring System for Plants using NRF24L01 transceiver modules, a pair of Arduino boards, a DC water pump, 1-channel relay module, a switch, SSD1306 Oled display Module, DS18B20 waterproof temperature sensor, and the soil moisture Sensor.
If you don’t have this particular type of the soil moisture sensor, no worries at all, you can use any other type of soil moisture sensor, you are not bound to use only the resistive type soil moisture sensors, you can also use the capacitive type soil moisture sensors. The reason I am using this soil moisture sensor is; because the sensor probes are long, waterproofed, and are made of highly corrosion-resistant material to ensure a long lifetime up to 6 months after being plugged into the soil. Due its long sensor probes, it digs deeper into the Soil.
Ever since I got Long Range LoRa transceiver modules, since then I didn’t use these NRF24L01 transceiver modules. There were times when I was using NRF24L01 radio modules in all of my wireless projects. Links to all the related NRF24L01 based projects are given at the end of this article.
The Soil Moisture Monitoring System which I am going to make, can be built using the Long Range LoRa transceiver modules and can also be built using the NRF24L01 transceiver modules. If the distance between the Transmitter and Receiver is over 800 meters then go far LoRa transceiver modules. And if the distance is between 100 meters and 800 meters then you can use the PA + LNA versions of the NRF24L01, and if the distance is less than 100 meters then you can use these low-cost NRF24L01 modules.
Since in my house, the maximum distance between the transmitter and receiver can be up to 40 meters so that’s why I selected these low-cost short range NRF24L01 Transceiver modules.
This is the transmitter’s side, along which a soil moisture sensor, NRF24L01 Transceiver module, a 1-channel relay module, DS18B20 temperature sensor, 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 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. The DS18B20 temperature sensor is optional, if you do not want to measure the temperature then there is no need to connect the temperature sensor. The reason I am using it, is to explain that we can send multiple sensor values.
And this is the receiver’s side. It has been connected with a button, an Oled display module, and NRF24L01 transceiver module. Now I am going to explain it’s working a bit, and then we will kick off our practical demonstration.
The transmitter will send values related to the soil moisture, temperature, and motor status, which will show on the Oled display module fitted on the receiver’s side. The Button on the receiver side is used to control the water pump. When the control signal is sent, the receiver side turns ON or turns OFF the Motor and at the same time, the transmitter also sends a feedback message whether the motor is turned ON or Not. So, it’s a two-way communication system. 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 see soil moisture, temperature, and motor status on the Oled display module. Now, if I press the button, the light will turn ON, which will indicate that the motor has turned ON and at the same time I also receive a feedback that the motor has powered ON. You can see the motor status has changed from 0 to 1 on the display.
Now, I am going to add some water and you will see an increase in the soil moisture value.
Let’s say, this soil moisture value is enough, so now I can press the button and turn OFF the water pump wirelessly.
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)
*Disclosure: These are affiliate links. As an Amazon Associate I earn from qualifying purchases.
Soil moisture Sensor:
This soil moisture sensor provides data in both digital and analog form. The two probes on the sensor act as variable resistors. Use it in a home automated watering system, hook it up to IoT, or just use it to find out when your plant needs water.
The soil moisture sensor consists of two probes which are used to measure the volumetric content of water. The two probes allow the current to pass through the soil and then it gets the resistance value to measure the moisture value.
When there is more water, the soil will conduct more electricity which means that there will be less resistance. Therefore, the moisture level will be higher. Dry soil conducts electricity poorly, So when there will be less water, then the soil will conduct less electricity which means that there will be more resistance. Therefore, the moisture level will be lower.
Wiring Connection
VCC: 3.3V-5V
GND: GND
DO: Digital output interface (0 and 1)
AO: Analog output interface
Features:
- Dual output mode, analog output more accurate
- A fixed bolt hole for easy installation
- With power indicator (red) and digital switching output indicator (green)
- Having LM393 comparator chip, stable.
DS18B20 Temperature sensor:
One-wire temperature sensors like the DS18B20 are devices that can measure temperature with a minimal amount of hardware and wiring. These sensors use a digital protocol to send accurate temperature readings directly to your development board without the need for an analog to digital converter or other extra hardware. You can get one-wire sensors in different form factors like waterproof and high-temperature probes–these are perfect for sensing temperature in many different projects and applications. And since these sensors use the one-wire protocol you can even have multiple of them connected to the same pin and read all their temperature values independently.
The DS18B20 Waterproof Temperature Sensor has three wires
- The red wire is the VCC wire: the operating voltage is 3 to 5 volts.
- Yellow Wire is the Data wire: we usually connect a resistor between the data wire and VCC wire.
- The black wire is the Ground wire.
This temperature sensor is capable of measuring the temperature ranging from -55°C to 125°C
NRF24L01 Arduino Transmitter Circuit:
This is the transmitter circuit diagram. The VCC and GND pins of the NRF24L01 are connected with the 3.3v and GND pins of the Arduino. Don’t forget to add a 10uF capacitor between the VCC and GND pins of the NRF24L01 module. The CE, SCN, SCK, MOSI, and MISO pins of the NRF24 module are connected with the Arduino pins 9, 10, 13, 11, and 12 respectively.
The VCC and GND pins of the DS18B20 are also connected with the 3.3V and GND pins of the Arduino. While the data pin is connected with D2 pin of the Arduino. There is also a 330 ohm resistor connected between the data wire and VCC.
The analog output pin of the Soil Moisture interface board is connected with the A0 pin of the Arduino, while the VCC and GND pins of the interface circuit are connected with the 5V and GND pins of the Arduino.
The one-channel relay module is controlled using the Arduino pin D3.
I am using a separate 12V power supply for the DC water pump. The ground wire is directly connected with the GND wire of the DC water pump. While the 12V wire is connected with the Water pump through the relay. So, by turning ON and turning OFF this relay we can turn ON and turn of the water pump. Now, let’s take a look at the receiver’s side circuit diagram.
NRF24L01 Arduino Receiver Circuit:
The NRF24L01 connection with the Arduino remains exactly the same. A switch is connected to pin D2. The SSD1306 Oled display module SDA and SCL/SCK pins are connected with the A4 and A5 pins while the VDD/VCC and GND pins are connected with the Arduino’s 3.3V and GND pins.
PCBs from JLCPCB:
These are the NRF24L01 development boards which I just received from the JLCPCB. I will be using these boards for testing my NRF24L01 transceiver based projects. On the left and right side you can add different types of sensors and displays. I am really impressed with the PCBs quality. The silk screen is quite clear and the white color solder mask looks pretty amazing.
This is how the PCB boards look after soldering. You can use any of these boards as the transmitter or receiver as the NRF connections on both the development boards are exactly the same. Next, I connected everything as per the circuit diagrams. And now let’s take a look at the programming.
Project Codes:
Before you start the programming, first of all, make sure you download the NRF24 and NRF24Network libraries, DallasTemperature, Adafruit_GFX, and Adafruit_SSD1306 libraries.
NRF24L01 Transmitter 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 |
/*Transmitter code pins connections vcc 3.3 gnd gnd ce pin9 scn pin10 sck pin13 mosi pin11 miso pin12 */ #include <SPI.h> #include "RF24.h" #include <OneWire.h> #include <DallasTemperature.h> RF24 myRadio (9, 10); byte addresses[][6] = {"0"}; #define smoisture A0 #define ONE_WIRE_BUS 2 #define waterpump 3 int relaystatus=0; int relayvalue; int motorstatus; OneWire oneWire(ONE_WIRE_BUS); // Pass our oneWire reference to Dallas Temperature. DallasTemperature sensors(&oneWire); struct package { int id = 1; float temperature = 18.3; int buttonstate; int soilmoisture=0; int relaystatus; }; typedef struct package Package; Package dataRecieve; Package dataTransmit; void setup() { Serial.begin(115200); pinMode(smoisture,INPUT); pinMode(waterpump,OUTPUT); sensors.begin(); delay(1000); myRadio.begin(); myRadio.setChannel(115); myRadio.setPALevel(RF24_PA_MAX); myRadio.setDataRate( RF24_250KBPS ); // for the maximum communication range myRadio.openReadingPipe(1, addresses[0]); myRadio.startListening(); } void loop() { int moisturevalue = map(analogRead(smoisture), 0, 1023, 100,0 ); // sensors.requestTemperatures(); // Send the command to get temperatures float temperaturevalue=sensors.getTempCByIndex(0); if ( myRadio.available()) { while (myRadio.available()){ myRadio.read( &dataRecieve, sizeof(dataRecieve) ); } Serial.println("Recieve: "); Serial.println(dataRecieve.buttonstate); Serial.print("\n"); relayvalue=dataRecieve.buttonstate; } if(relayvalue==LOW) { digitalWrite(waterpump,HIGH); motorstatus=1; } else { digitalWrite(waterpump,LOW); motorstatus=0; } delay(200); myRadio.stopListening(); dataTransmit.id = dataTransmit.id + 1; dataTransmit.temperature = temperaturevalue; dataTransmit.soilmoisture=moisturevalue; dataTransmit.relaystatus=motorstatus; Serial.println("Transmit: "); Serial.print("Package:"); Serial.print(dataTransmit.id); Serial.print("\n"); Serial.println(dataTransmit.temperature); Serial.println(dataTransmit.soilmoisture); Serial.println( dataTransmit.relaystatus); Serial.print("\n"); myRadio.openWritingPipe(addresses[0]); myRadio.write(&dataTransmit, sizeof(dataTransmit)); myRadio.openReadingPipe(1, addresses[0]); myRadio.startListening(); } |
NRF24L01 Receiver 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 |
/*Receiver code pins connections vcc 3.3 gnd gnd ce pin9 scn pin10 sck pin13 mosi pin11 miso pin12 */ #include <SPI.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include "RF24.h" RF24 myRadio (9, 10); byte addresses[][6] = {"0"}; int button=2; struct package { int id = 1; float temperature = 18.3; int buttonstate=0; int soilmoisture; int relaystatus; }; typedef struct package Package; Package dataRecieve; Package dataTransmit; #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void setup() { Serial.begin(115200); pinMode(button,INPUT_PULLUP); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); delay(2000); display.clearDisplay(); display.setTextColor(WHITE); delay(1000); myRadio.begin(); myRadio.setChannel(115); myRadio.setPALevel(RF24_PA_MAX); myRadio.setDataRate( RF24_250KBPS ); myRadio.openReadingPipe(1, addresses[0]); myRadio.startListening(); } void loop() { //Serial.println(digitalRead(button)); int buttonvalue=digitalRead(button); if ( myRadio.available()) { while (myRadio.available()){ myRadio.read( &dataRecieve, sizeof(dataRecieve) ); } Serial.println("Recieve: "); Serial.print("Package:"); Serial.print(dataRecieve.id); Serial.print("\n"); Serial.println(dataRecieve.temperature); Serial.println(dataRecieve.soilmoisture); Serial.println(dataRecieve.relaystatus); Serial.print("\n"); } delay(200); myRadio.stopListening(); dataTransmit.id = dataTransmit.id + 1; Serial.println("Transmit: "); dataTransmit.buttonstate=buttonvalue; Serial.println(dataTransmit.buttonstate); Serial.print("\n"); myRadio.openWritingPipe(addresses[0]); myRadio.write(&dataTransmit, sizeof(dataTransmit)); myRadio.openReadingPipe(1, addresses[0]); myRadio.startListening(); ///////////////// Serial.println(stat); display.clearDisplay(); display.setCursor(10,0); display.setTextSize(2); display.setTextColor(WHITE); display.print("SM:"+String(dataRecieve.soilmoisture )+"%"); display.setCursor(10,20); display.setTextSize(2); //display.print("C: "); display.print(dataRecieve.temperature); display.print((char)247); display.print("C"); display.setCursor(10,45); display.setTextSize(2); display.print("MS:"+String(dataRecieve.relaystatus)); display.display(); delay(1000); } |
Watch Video Tutorial:
NRF24L01 Related Projects:
Wireless Sensor Network using NRF24L01 Transceiver modules
NRF24L01 based project for Deaf People
NRF24L01 Multiple Transmitters and Single Receiver
Arduino, Android, and NRF24L01 based 12V battery monitoring system
Industrial temperature monitoring using NRF24L01 PA + LNA versions
I appreciate your knowledge and skills.
I read an article and a project that controls the pump based on soil moisture.
From a practical point of view, however, the transmitter should only measure and transmit soil moisture and temperature data to the receiver.
The receiver should turn on the pump based on specific soil moisture thresholds.
Could you modify the circuit and code so that the receiver turns on the pump?
Can I count on answer.
Greetings from Poland