Soil Moisture Sensor for Arduino, ESP8266 and ESP32

Soil Moisture Sensor Tutorial for Arduino, ESP8266 and ESP32

In this tutorial we want to measure if the soil is wet or dry with a soil moisture sensor.

First we describe the basics how the measurement is done.

In the second part of this tutorial I show you how you can expand the lifetime of the soil moisture sensor because due to oxidization, the sensor will be damaged.

Soil Moisture Sensor

The last part of this tutorial I show you a practical example with Arduino, ESP8266 or ESP32 microcontrollers. In this example I measured the soil moisture of an orchid plant over 2 weeks.

Table of Contents

How the sensor measures the soil moisture?

The soil moisture sensor consists of 2 probes with are put in the soil. Depending on the current direction one probe will function as the cathode and the other one as anode. Generally which probe is the anode or cathode is irrelevant for the functionality of the sensor, because the sensor only measures the resistance and is therefore independent of the direction of the current flow.

The electrical circuit is closed over the soil which functions as resistance for the current flow. This resistance is measured and depends on the amount of water in the soil because water is a natural conductor for electricity. The lower the measured resistance, the higher is the amount of water in the soil.

The following pictures show the wiring between the soil moisture sensor and an the most used microcontroller from Arduino, ESP32 and ESP8266. If you are missing your favorite microcontroller, let me know in the comment section and I will add the wiring also your this microcontroller board.

Soil Moisture Sensor Arduino Pro Mini

Soil Moisture Sensor Arduino Uno

Soil Moisture Sensor Arduino Mega

Soil Moisture Sensor ESP32 NodeMCU

Soil Moisture Sensor ESP8266 NodeMCU

Soil Moisture Sensor ESP8266 WeMos D1 Mini

Expand the Lifetime of Soil Moisture Sensors

The current flow through the anode which have contact with water is a perfect environment for electrolysis which damages the sensor and makes the sensor inaccurate. How strong the electrolysis will be depends on three different factors:

  1. the quality of your sensor,
  2. the amount of water in the soil,
  3. how often and how much current is passed through electrodes

If the electrolysis taken place, the sensor is damaged and the sensor values can not be used to measure the moisture in the soil. But there is a simple solution because you can reduce the current by connecting the sensor to 3.3V and not 5V. Moreover you can measure the resistance from the soil only every hour to extend the lifespan of the soil moisture sensor.

To ensure that the current flow through the sensor is zero when you do not want to measure the sensor value, you have different options depending on the microcontroller you use

ESP8266 based Microcontroller

If you are using an ESP8266 based microcontroller you can use the deep-sleep function and waken up the sensor when you want to read the sensor value. If you do not know how to set the ESP8266 in the deep-sleep mode, I wrote a tutorial in this article. The main drawback in this solution is that the maximum sleep time is 71 minutes.

The following picture shows you the fritzing sketch for the NodeMCU using deep-sleep.

NodeMCU Moisture Sensor

The following script reads the analog value of the soil moisture sensor on pin A0 and prints the value to the serial monitor. After the value is displayed the NodeMCU will sleep for 1 minute and waken up again. Therefore the whole script has to be inside the setup function.

#define SensorPin A0 

void setup() { 
  Serial.begin(9600);
  float sensorValue = analogRead(SensorPin);
  Serial.println(sensorValue);

  ESP.deepSleep(1*60e6); // Deep Sleep for 1 minute
}

void loop() {
} 

Arduino based Microcontroller

If you want to read the sensor value only once or twice a day using a large delay inside the script you can use the following NPN based circuit to ensure that the sensor is voltage-free if you do not read the sensor value. You can not use the deep-sleep function with an Arduino. Therefor you have to use this solution with the NPN MOSFET.

Arduino Moisture Sensor MOSFET

The program script does not differ much compared to that for the NodeMCU. The only two differences is that we use the loop function to read the moisture sensor value every 30 seconds. Also we use die digital pin4 as output to enable and disable the MOSFET. Therefore we enable the MOSFET (enable the current flow on the gate) before we want to read the sensor value and disable the MOSFET (disable the current flow on the gate) after the value is read.

#define SensorPin A0 

void setup() { 
  Serial.begin(9600);
  pinMode(4, OUTPUT);
}

void loop() {
  digitalWrite(4, HIGH);
  delay(1000);

  float sensorValue = analogRead(SensorPin);
  Serial.println(sensorValue);
  delay(1000);
  digitalWrite(4, LOW);
  delay(30000);
} 

ESP32 based Microcontroller

Like the ESP8266 also the ESP32 has a deep sleep power mode. But you do not have to change the wiring to wake up the ESP32. Therefore we can take the wiring from the first section of this tutorial and only have to change the program code.

#define SensorPin A0 

void setup() { 
  Serial.begin(9600);
  float sensorValue = analogRead(SensorPin);
  Serial.println(sensorValue);

  esp_sleep_enable_timer_wakeup(1*60e6); // Deep Sleep for 1 minute
  esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // all RTC Peripherals are powered
  esp_deep_sleep_start();
}

void loop() {
} 

MQTT Example for Long Term Monitoring

The following table gives you an overview of all components and parts that I used for this tutorial. If you want to support my work, you can buy something from the following links and I will earn a small commission. This does not affect the price you pay for the products.

If you are interested in components and parts that I used in other tutorials, visit the components and parts page.

 Arduino UnoAmazonBanggoodAliExpress
ORESP8266 NodeMCUAmazonBanggoodAliExpress
ORESP32 NodeMCUAmazonBanggoodAliExpress
ANDSoil Moisture SensorAmazonBanggoodAliExpress
ANDRaspberry Pi 4 Model B KitAmazon AliExpress
ORRaspberry Pi 4 Model BAmazonBanggoodAliExpress
ORRaspberry Pi 3 B+ KitAmazon AliExpress
ORRaspberry Pi 3+AmazonBanggoodAliExpress

In the following example I want to observe the soil moisture for a plant for a long time and see the course of the moisture as a line-chart. Therefore I build a MQTT system including the following components:

  • NodeMCU to read the analog soil moisture sensor values and send them every hour via MQTT to a MQTT broker
  • Raspberry Pi as MQTT broker which saves the moisture values to an Influx database and visualize the soil moisture of the plan via Grafana.
Moisture Sensor Foto

This example relates strongly on two articles I wrote the last month. Therefor I will speed up this example because you find a step by step tutorial in the following two articles:

At the end of this article you can download all scripts, compressed as zip file, for this example with the blue download button.

First we build the part of the NodeMCU to send the sensor values to the MQTT broker. The following picture shows the wiring for the NodeMCU.

NodeMCU Moisture Sensor

The program code is nearly exactly the same as I used to send the temperature and humidity to the MQTT broker. I only changed the MQTT topic, MQTT clientID and read the moisture values to send them via MQTT to the same existing broker. The main code is inside the setup function, because I use the deep-sleep function of the NodeMCU to reduce the electrolysis on the soil moisture sensor.

Make sure you add the delay before entering the deep-sleep. I had some problems, that the NodeMCU shutting down while the MQTT message was not completely send.

#include "ESP8266WiFi.h" // Enables the ESP8266 to connect to the local network (via WiFi)
#include "PubSubClient.h" // Allows us to connect to, and publish to the MQTT broker

#define SensorPin A0 

// WiFi
const char* ssid = "KabelBox-0174";
const char* wifi_password = "943476385562******";

// MQTT
// Make sure to update this for your own MQTT Broker!
const char* mqtt_server = "192.168.0.8";
const char* plant_topic = "plant";
const char* mqtt_username = "cdavid";
const char* mqtt_password = "cdavid";
// The client id identifies the ESP8266 device. Think of it a bit like a hostname (Or just a name, like Greg).
const char* clientID = "client_plant_test";


// Initialise the WiFi and MQTT Client objects
WiFiClient wifiClient;
PubSubClient client(mqtt_server, 1883, wifiClient); // 1883 is the listener port for the Broker


void connect_MQTT(){
  Serial.print("Connecting to ");
  Serial.println(ssid);

  // Connect to the WiFi
  WiFi.begin(ssid, wifi_password);

  // Wait until the connection has been confirmed before continuing
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // Debugging - Output the IP Address of the ESP8266
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Connect to MQTT Broker
  // client.connect returns a boolean value to let us know if the connection was successful.
  // If the connection is failing, make sure you are using the correct MQTT Username and Password (Setup Earlier in the Instructable)
  if (client.connect(clientID, mqtt_username, mqtt_password)) {
    Serial.println("Connected to MQTT Broker!");
  }
  else {
    Serial.println("Connection to MQTT Broker failed...");
  }
}


void setup() { 
  Serial.begin(9600);
  connect_MQTT();
  Serial.setTimeout(2000);

  float sensorValue = analogRead(SensorPin);
  Serial.println(sensorValue);

  // PUBLISH to the MQTT Broker
  if (client.publish(plant_topic, String(sensorValue).c_str())) {
    Serial.println("Moisture sent!");
    Serial.println(plant_topic);
  }
  // Again, client.publish will return a boolean value depending on whether it succeded or not.
  // If the message failed to send, we will try again, as the connection may have broken.
  else {
    Serial.println("Moisture failed to send. Reconnecting to MQTT Broker and trying again");
    client.connect(clientID, mqtt_username, mqtt_password);
    delay(10); // This delay ensures that client.publish doesn't clash with the client.connect call
    client.publish(plant_topic, String(sensorValue).c_str());
  }
  
  delay(1000);
  ESP.deepSleep(0.2*60e6);
}

void loop() {
} 

Now the NodeMCU sends the current soil moisture every hour to the MQTT broker. The next task is to write a bridge script which reads the MQTT payload and writes it to the Influx Database.

I created a new Influx database called “soil_moisture” and a new Influx user “mqtt_moisture” with the password “mqtt_moisture”. Also I granted all rights for this new database to the new user.

Again the script is nearly the same I used for the indoor weather stations. You find the corresponding article here. You can copy the following python script for your MQTT bridge script or download the python file with the following download button.

import re
from typing import NamedTuple

import paho.mqtt.client as mqtt
from influxdb import InfluxDBClient

INFLUXDB_ADDRESS = '192.168.0.8'
INFLUXDB_USER = 'mqtt_moisture'
INFLUXDB_PASSWORD = 'mqtt_moisture'
INFLUXDB_DATABASE = 'soil_moisture'

MQTT_ADDRESS = '192.168.0.8'
MQTT_USER = 'cdavid'
MQTT_PASSWORD = 'cdavid'
MQTT_TOPIC = 'plant'
MQTT_CLIENT_ID = 'MQTTInfluxDBBridge_moisture'
influxdb_client = InfluxDBClient(INFLUXDB_ADDRESS, 8086, INFLUXDB_USER, INFLUXDB_PASSWORD, None)


def on_connect(client, userdata, flags, rc):
    """ The callback for when the client receives a CONNACK response from the server."""
    print('Connected with result code ' + str(rc))
    client.subscribe(MQTT_TOPIC)


def on_message(client, userdata, msg):
    """The callback for when a PUBLISH message is received from the server."""
    print(msg.topic + ' ' + str(msg.payload))
    sensor_data = float(msg.payload)
    if sensor_data is not None:
        _send_sensor_data_to_influxdb(sensor_data)


def _send_sensor_data_to_influxdb(sensor_data):
    json_body = [
        {
            'measurement': 'soil_moisture',
            'tags': {
                'location': 'livingroom'
            },
            'fields': {
                'value': sensor_data
            }
        }
    ]
    influxdb_client.write_points(json_body)


def _init_influxdb_database():
    databases = influxdb_client.get_list_database()
    if len(list(filter(lambda x: x['name'] == INFLUXDB_DATABASE, databases))) == 0:
        influxdb_client.create_database(INFLUXDB_DATABASE)
    influxdb_client.switch_database(INFLUXDB_DATABASE)


def main():
    _init_influxdb_database()

    mqtt_client = mqtt.Client(MQTT_CLIENT_ID)
    mqtt_client.username_pw_set(MQTT_USER, MQTT_PASSWORD)
    mqtt_client.on_connect = on_connect
    mqtt_client.on_message = on_message

    mqtt_client.connect(MQTT_ADDRESS, 1883)
    mqtt_client.loop_forever()


if __name__ == '__main__':
    print('MQTT to InfluxDB bridge Moisture')
    main()

Now the sensor values are stored in the Influx database so that we can create a dashboard in Grafana. In Grafana you can create a new data source. Use the Influx database and the username and password you set before.

Grafana Configuration Data Sources

Now you can create a new dashboard and visualize the soil moisture of your plants at home. The following picture is the one I collected the data over several days. You see clearly how the sensor values are rising to 980 over the days. Between the 12.01. and 13.01 the plant got some water and the sensor values drop to around 750. In the following days the sensor values rise again and now I know exactly when my plant needs water.

Grafana Soil Moisture Dashboard

Conclusion

I hope with this tutorial you now have a good understanding of the soil moisture sensor. I tried to keep the theory of the sensor as short as possible because the functionality of the sensor is not very complex to understand. Therefor I tried to concentrate on a good practical example using MQTT, InfluxDB and Grafana to build a pretty monitoring system for your plants at home.

I hope you like this article. If you have any questions regarding the moisture sensor or the MQTT example please use the comment section below to ask your questions. I answer them as soon as possible.

10 Responses

Leave A Comment