Today we will be working with some inexpensive soil moisture sensors. We’ll learn how these sensors function, we’ll see how to calibrate them, and then we’ll build two projects – a soil moisture meter with an OLED display, and an automated watering system that works with the Arduino IoT Cloud.

 

Introduction

Soil moisture sensors have many applications in agriculture, research, and everyday gardening.   By keeping tabs on the amount of water in the soil, we can make sure our precious crop stays hydrated, or we can get an idea of the conditions before planting.

Inexpensive moisture sensors are readily available, and they are great to experiment with. Today we’ll see how they work, and how to calibrate them so that we can use them in some interesting projects.

Our first project will be a simple soil moisture meter that uses a Raspberry Pi Pico microcontroller and displayed its output on an OLED display.

Then we will build an automated watering system using an Arduino Nano 33 IoT board. Our system will have an OLED display and will also use the Arduino IoT Cloud to allow you to monitor and control the system over the internet.

I have to confess, however, that gardening is not my forte. In fact, quite the opposite, I have a history as a plant-killer. So don’t expect any botanical advice here!

But while I can’t tell you how to grow prize-winning roses, I can show you how to keep tabs on the moisture in their soil. So let’s start by understanding how we measure that.

Soil Moisture Sensing

When we discuss soil moisture sensors, we are speaking of sensors that estimate volumetric water content.  This is different from another class of moisture sensor called a tensiometer, which measures the potential that soil has to contain water.

There are a number of different methods of measuring the water content in soil:

  • Microwave methods – Frequency Domain Reflectometry and Time Domain Reflectometry are two methods that exploit the effect that water has on high-frequency radio waves. 
  • Neutron Sensors – This advanced method uses the moderator properties of water on a stream of neutrons.
  • Soil Resistivity – Soil resistance changes in proportion to the amount of water inside it.
  • Galvanic Cell – Soil can act as a battery! The tiny current it can produce can be measured to determine its water content.
  • Capacitance – You can use the soil as a dialect for a capacitor, whose value will change as the moisture within the soil changes.

Some of these methods are obviously too esoteric for experimenters and hobbyists. 

The most common types of inexpensive soil moisture sensors are based upon resistance and capacitance, and these are the types we will be using for our experiments and projects.

Resistive Sensors

This is by far the easiest, and most inexpensive, type of soil moisture sensor.  You can literally make one out of a couple of nails inserted into the soil. In fact, if you take a multimeter and insert its probes into the ground you should get a reading, which will change if you add water.

Resistive sensors are not only simple to build, but they also operate pretty well, giving a great variation between dry and wet soil.  They can also operate as a crude “rain sensor”, get them wet, and the resistance will drop.

But they also have a couple of disadvantages:

  • The probes will corrode over time, changing their resistive properties.
  • They induce voltage into the soil, which may be harmful to the growing process.

These sensors are generally wired in a voltage-divider configuration, so they output a voltage that changes in proportion to the level of moisture they detect. This makes them quite easy to use with a microcontroller that has an analog to digital converter (ADC), as most popular microcontrollers do.

Capacitive Sensors

Capacitive sensors, while slightly more expensive than their resistive counterparts, have become very popular. As they don’t need direct electrical contact with the soil, they can be insulated from corrosion, eliminating many of the disadvantages inherent to resistive sensors.

These sensors work with two insulated plates. Soil (or water) between the plates becomes a dielectric material, forming a capacitor. A timer is used with the capacitor determining its discharge period, and this is converted into a voltage that changes in proportion to the moisture level.

As resistive and capacitive sensors both produce a changing voltage, we can use both of them with the same circuits. They are not direct replacements for one another, however, as one produces more voltage when the moisture rises while the other produces less.

I2C Sensors

While most inexpensive soil moisture sensors produce an analog voltage, you can also obtain sensors with other interfaces, such as serial or I2C. These are useful when your device, such as a microcomputer, has no analog inputs.

Most of the I2C sensors available use capacitive sensing, and they have an internal analog to digital converter to convert the signal into a digital one, which is then sent to a small internal controller with an I2C interface.

As with most I2C devices, they do have limitations in the distance they can be placed from the host processor.

Digital Sensors

Not to be confused with I2C sensors, digital sensors have a simple, one-bit, output that changes state when the moisture crosses a preset threshold.

Many moisture sensors can be operated in digital mode as well as analog mode. We will be using our sensors in analog mode for our experiments.

Sensor Calibration

Before we can put our moisture sensors, resistive or capacitive, to work, we will need to calibrate them. These devices all seem to have their own “personalities”, and even two sensors from the same batch will exhibit different characteristics.

Calibration is a simple procedure that involves determining the analog output of the sensor under two conditions:

  • Completely Dry – In the air, with minimal humidity.
  • Completely Wet – Immersed completely in water.

So all you really need to test out your sensors is a glass of water, and a cloth to dry off your sensor!

Calibration with a Raspberry Pi Pico

Our calibration arrangement will make use of a Raspberry Pi Pico microcontroller, as it is inexpensive and also has a very accurate 12-bit ADC.

Here is how we will connect our sensor to the Pico. Note that while I’m illustrating the use of a capacitive sensor, you can also use the exact same hookup for a resistive sensor.

In this experiment, we will be using the Arduino IDE 1.8 to program the Pico in C++, but you could use another IDE such as PlatformIO or the Arduino IDE 2.0.  In any case, you’ll need to install a boards manager for the Pico if you haven’t already done that.

If you want more details about using the Pico with the Arduino IDE 1.8 please see my article about building a Pico Simon game.

Once you have your IDE set up, you can use the following sketch to test out your Pico:

As you can see, this is a very simple sketch. After defining a variable to hold the sensor value and the pin of the ADC connection, we move into the Setup. Here we initialize the serial monitor and also use an analogReadResoloution command to set our ADC to use 12-bits.

In the Loop, we just read the value from the ADC and print it to the serial monitor. After a short delay, we do it all over again.

Run the sketch ad observe the serial monitor. Make note of the readings in both wet and dry conditions.

 

You’ll notice that the resistive sensor has a much larger range and that the lower numbers (less voltage) are obtained when the soil is dry.

The capacitive sensors, on the other hand, have a limited range and produce higher numbers when they are dry.

Make sure you record your results, as you’ll need them for the two projects we are going to build.

Calibration with an Arduino Uno

You may also perform the calibration using an Arduino Uno. As the Uno has a 10-bit ADC you will get lower numbers, but they are still quite useful.

Here is the Uno hookup:

Note that we are powering our sensor (again, this can be capacitive or resistive) with the Uno’s 3.3-volt output and not the 5-volts. While most soil moisture sensors can work at either 3.3 or 5-volts they are usually using 3.3-volts internally.  This is especially true for capacitive sensors, simple resistive ones can work on any voltage.

Also, note how we feed 3.3-volts to the Arduino AREF pin. This is the Analog Reference, and it determines the reference voltage used by the internal ADC. Our moisture sensors (again, capacitive ones specifically) only output up to 3-volts, regardless of power supply voltage, so this avoids “wasting” the upper bits.

We can use the same code we used for the Raspberry Pi Pico, with two modifications:

  • Remove the analogReadReference command, as it won’t compile with the Arduino Uno.
  • Replace it with an analogReference(EXTERNAL) command, to tell the Uno to use the AREF pin for its analog reference voltage.

The resulting code is printed here:

The testing procedure with the Uno is the same, but the results will be different, as a 10-bit ADC only reads from 0 to 1023, while a 12-bit ADC reads from 0 to 4095.

If you want to use these readings in the projects we are building today, you can multiply them by four.

Build a Soil Moisture Meter

Our first project is a soil moisture meter. It reads the moisture content on a scale of 0 to 100 percent and useless an OLED display as its output. 

While I will be assembling my meter on a solderless breadboard, this is also a good project to build into a permanent enclosure. The wiring is so simple that a PCB would be overkill, so you could just hand-wire it on a piece of perfboard.

Moisture Meter Hookup

Here is how we will be hooking up our soil moisture meter:

The OLED display is a standard SSD1306 64 x 128 OLED. These are available in a variety of colors, and some of them have two colors – one for the top 25% of the display and another one for the remainder.

The unit I used is yellow on the top and blue on the bottom, but any color(s) will work.

Moisture Meter Code

Once again, we will be using the Arduino IDE to program our Raspberry Pi Pico with C++.

We will need to install a few libraries to get this working, these libraries are all for the OLED display, and both originate with Adafruit.

You may already have these libraries installed if you have worked with OLED displays before, but if you don’t then go into your Library Manager and look for the following two libraries:

  • Adafruit GFX
  • Adafruit SSD1306

The third library is the Wire library, which is used to communicate using I2C. It is installed into your IDE by default, so you don’t need to go looking for it.

In our sketch, we call the three libraries, then we set up some parameters for the OLED display.

After that, we create an object called “display” to represent the OLED.

Next, we have two sensor constants, which have values that you’ll need to replace with the ones you wrote down when you ran the calibration procedure:

  • DryValue – The value with the sensor out of the water and wiped dry.
  • WetValue – The value when the sensor is completely immersed in water.

We then define two values for the soil moisture reading. The first one is the threading from the ADC, the second one is the measurement converted to a percentage.

Finally, we define the pin that we have attached our sensor to.

In the setup we start the serial monitor, we can use this to troubleshoot or to refine our sensor constant values.

We initialize the OLED and clear the display, and set the resolution of the ADC to 12-bits.

In the Loop, we read the value from the sensor and send it to the serial monitor. We then map it to convert it to a value between o and 100.

We also use a constrain function to make sure we stay between 0 and 100.  The percentage value is also printed to the serial monitor.

Finally, we position and print text to the OLED. On the top line we print the word “Moisture”, and below it, in a larger font, we print the percentage value.  We hold it all for a quarter of a second and then clear the display and start the loop again.

Load the sketch to your Pico and test it all out. You should get a reading of 100% when the sensor is immersed in water, and 0 when it is dry.

Place it into some soil that has been watered and check out the reading you get.

This would make a nice project to build into a small enclosure.

IoT Plant Care

Our final project is an automated watering system that uses the Arduino IoT Cloud to have a dashboard, where we can control and monitor our soil moisture.

It is meant to be used with a small pump and a water source, but I tested mine using just a light bulb as I didn’t want to flood my workbench! I suggest you do the same to get it working before committing it to a pump.  Most relay modules have an onboard LED to indicate they are active, so you really don’t need to hook up anything when you are testing it.

Project Features

The project has both a soil moisture sensor and a temperature & humidity sensor, so it can display three parameters. It has a relay to drive an external, low-voltage pump – please don’t use a high-voltage pump, especially as you are using it in a damp environment!

You use a slider control on the control panel to establish a setpoint, a moisture percentage reading that the pump will turn on (or off) at.

The control panel also displays the pump status, plus the current moisture, temperature, and humidity readings. The temperature reading is Celsius, but you can easily convert it to Fahrenheit with some simple math:

F = (C x 1.8) + 32

The control panel also has a graph that shows you the moisture over time. It has a live view, as well as views for the past hour, day, week, and 15-days.

IoT Plant Care Hookup

The automatic watering project is based upon an Arduino Nano 33 IoT board. You could probably substitute an Arduino Nano RP2040 Connect board, as they have the same pinouts and are both useable on the Arduino IoT Cloud.

We are also using a common DHT22 temperature and humidity sensor. You might want to substitute a DHT21, as it has similar specs but is enclosed in a weatherproof case, so it would be easier to mount if you are using the system outdoors. You could also save a dollar or two by using a DHT11, which is less accurate but still sufficient for our purposes.

The OLED is the same display used in the Moisture Meter project.

The relay module is a standard 5-volt module. As for the pump, I suggest a low-voltage DC pump with a suitable power supply. The one aI tested with works on 4.5 -12 volts.

Here is the hookup diagram:

I built mine on a solderless breadboard, you could do the same or use some perfboard for a more permanent version.

Setting up the Arduino IoT Cloud

If you have not used the Arduino IoT Cloud before, you’ll need to set up an account first. I’ve created a video and article with all the details on how to do that and on how to use the cloud, you might want to refer to it if this is all new to you.

We will be using the Arduino IoT Cloud to accomplish three tasks:

  • Build a Thing – A Thing is basically an IoT Cloud project.
  • Build a Dashboard – This is the way we will control our Thing.
  • Write Code – This makes our Think work with the cloud and dashboard.

Building a Thing

We will start by building our Thing.

A Thing consists of three elements, which we need to set up:

  • A Device – This is our Arduino Nano 33 IoT board, which we need to set up and register with the cloud.
  • A Network – The WiFi network credentials for the Arduino Nano 33 IoT board.
  • Variables – Cloud variables used in our project, which I will now describe.

We will be using the following five cloud variables, whose names and properties are described in the following table:

 

NAME TYPE PERMISSION SEND VALUES
current_Moisture Integer number Read Only On change
current_Temp Floating point number Read Only On change
currrent_Humid Floating point number Read Only On change
pump_Status Boolean Read Only On change
trigger_Level Integer number Read & Write On change

After you set up these variables, the Arduino IoT Cloud will start populating a sketch for you.

Building a Dashboard

Go to the Dashboard tab and create a new dashboard.

You are, of course, at liberty to use whatever widgets you wish on your dashboard. I suggest that you start with the ones I used, and then add or subtract widgets as you see fit.

You can also position the widgets any way you want to. Remember, that there is both a desktop and mobile view for your dashboard.

The following table illustrates the dashboard parameters for the dashboard I created:

 

WIDGET TYPE NAME LINKED VARIABLE
Percentage Moisture current_Moisture
Chart Moisture Level current_Moisture
Gauge Temperature current_Temp
Percentage Humidity currrent_Humid
LED Pump Status pump_Status
Slider Pump Trigger Level trigger_Level

A few other notes about the specific widgets:

  • The Moisture Level chart was set to Spline.
  • The Temperature gauge was set for a minimum of -30 and a maximum of 50. You can change this range to suit your requirements.
  • The Pump Status LED was set to Red and Green.
  • The Pump Trigger Level slider was set to Horizontal with a range of 0 to 80. You can change these if you wish, obviously don’t go over 100 or below 0.

After you create your dashboard, we can enter the code we need to glue all of this together.

IoT Plant Care Code

When you set up your cloud variables, the Arduino IoT Cloud will have automatically started the framework for your code. You can go back into your Thing and click on the Sketch tab to see it.

From here, you will want to click the tab to open the code in the full editor.

There will actually be four files associated with your sketch:

  • The sketch itself named whatever you named your Thing, plus a date.
  • A ReadMe.adoc file. This is a file where you can add descriptive text for your Thing.
  • A thingProperties.h file. If you open this file, you’ll see the declarations and properties for all your Thing variables. It’s a required file, you should not need to edit it yourself.
  • A Secret file. This contains your WiFi SSID and Password. You should never share this file.

You will need to edit the sketch file to add your code, which includes adding libraries for both the OLED and DHT22.  Use the Library Manager to add these libraries:

  • Adafruit GFX Library
  • Adafruit SSD1306
  • DHT sensor library

We will also manually add the Wire library for I2C, you don’t do this in the Library Manager as it is included with the Cloud IDE.

Now we can add the rest of the code. The following is the complete code, note that you need to MERGE it manually and not copy over your own code, as some variables may be specific to your installation!

Let’s go over the key areas of the code.

After the libraries, we set up both the OLED and DHT22. If you decide to use a different DHT sensor, you can change the DHTTYPE definition accordingly.

We define a couple of floats for temperature and humidity. Note that these are Local variables and not cloud variables. We want these in case the device cannot connect to the Internet.

The variables for soil moisture are exactly what we saw in the Moisture Meter project.

We define a String to hold the pump status text for the OLED, plus a local variable for the pump trigger. Again, the local variable is in case we can’t grab the value from the cloud.

The IoT cloud will include the thingProperties.h file we talked about earlier.

In the Setup the IoT cloud sets up a serial monitor with a delay and calls a function from the thingProperties.h file, this will already be in your code when you launch the editor.

It will then connect to the IoT Cloud, again this is defined automatically.  It also automatically sets a debug level, which you can change if you experience problems and need to troubleshoot.

We then add our own code to initialize the OLED and DHT22. We then set our ADC resolution to 12-bits, set the relay as an output, turn off the relay, and set the cloud variable pump_Status to false.

Now to the Loop!

The first thing in the Loop has been added by the IoT Cloud, it’s a call to an update function. It should remain as the first item in your Loop.

Next, we get the temperature and humidity values from the DHT sensor. We assign them to our local variables and then pass them on to their associated cloud variables.

The procedure for getting the soil moisture value is identical to that in the Moisture Meter. Once we have the value assigned to the local variable, we pass it on to the cloud variable.

We then look at the threshold and the current moisture value to determine if we need to turn on the pump or turn it off. There are functions for each of these tasks.

Finally, we call another function, printOLED, to print to our OLED and hold the display. I hold it for two seconds, but you can adjust this. The result will be the display cycling between moisture, temperature, humidity, and pump status.

The functions pumpOn and pumpOff are pretty self-explanatory, they either activate or deactivate the relay and set the pump status text and variable accordingly.

The printOLED function is also pretty simple, it prints to the OLED using the same syntax we used in the Moisture Meter.

Finally, the onTriggerLevelChange function, which is activated every time the slider on the dashboard changes value.   All we do here is pass the new trigger value to the local variable.

Once you have the sketch in the editor load everything to your Arduino. 

Now go into the dashboard and observe the action. It is common for it to take a minute or two to get connected at first, and there is some natural latency between events on the moisture sensor and dashboard readouts. But it should work, and will allow you to monitor and control your automated watering system from anywhere you have internet connectivity!

Conclusion

Moisture sensors are pretty simple to work with, and they can be used to build some useful projects.

All the projects I built used capacitive sensors, but they could be modified to use resistive ones. Just change the order of the constants in the Map command to make that happen.

Hope you enjoyed the article and its associated video, and that your plants will never be thirsty or overwatered again!

Resources

Code Used in this article – All of the code from this article in an easy-to-use ZIP file.

Moisture Sensing – Wikipedia article on moisture sensors.

I2C Sensor Library – Arduino Library for Catnip Electronics I2C Moisture Sensor

 

 

Water Your Garden with IoT
Summary
Water Your Garden with IoT
Article Name
Water Your Garden with IoT
Description
Learn to use soil moisture sensors with a microcontroller. After calibrating our sensors, we will build a soil moisture meter and an automated watering system using the Arduino IoT Cloud.
Author
Publisher Name
DroneBot Workshop
Publisher Logo
Tagged on:

If you have a question...

Comments about this article are encouraged and appreciated. However, due to the large volume of comments that I receive, it may not be possible for me to answer you directly here on the website.

You are much more likely to get answers to technical questions by making a post on the DroneBot Workshop Forum. Your post will be seen not only by myself, but by a large group of tech enthusiasts who can quickly answer your question. You may also add code samples, images and videos to your forum posts.

Having said that, please feel free to leave constructive comments here. Your input is always welcome. Please note that all comments may be held for moderation.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Fal
1 month ago

Hello it it possible to use arduino nano only instead of arduino nano 33 iot board?

9 days ago
Reply to  Fal

For the cloud feature you will need it to have internet connectivity capability hence the nano 33 iot or others with similar features. You can use the Arduino Nano too with the display and all the sensors and that works fine too without the use of the cloud software features..

2
0
Would love your thoughts, please comment.x
()
x