Soil moisture sensing ESP32

Posted by

Today we will do a soil moisture sensing project with ESP32-S2 Franzininho. We will use a capacitive soil moisture sensor, found here. The information will be sent to Thingspeak, go create a free account there.

Capacitive soil moisture sensor
Capacitive soil moisture sensor

This sensor consumes less than 5mA. The Franzininho board has a 3.3V LDO regulator called Ap2112 that provides up to 600mA. So we will be sure not to use an external source, just Franzininho’s own regulator. Franzininho’s pinout diagram is here.

We will use the analog pin for the humidity sensor: GPIO 1 (ADC1_0). We will also have a temperature and atmospheric pressure sensor, the BMP280. It will be connected to SCL-GPIO 9, SDA-GPIO 8 pins in addition to 3.3V and GND. All code used will be here.

Schematic diagram

The schematic diagram of our plant moisture meter is below. Now notice the simplicity, just two external components.

Soil moisture sensor with ESP32-S2 schematic diagram
Soil moisture sensor with ESP32-S2 schematic diagram

The assembly of the prototype in the plant pot is seen below. So notice that I packed a plastic box to contain the ESP32-S2 Franzininho and the BMP280 atmospheric pressure sensor. Outside are the USB power cable and the capacitive humidity sensor.

Circuit assembled and reading to be used
Circuit assembled and reading to be used
soil moisture sensing
Sensor mounted in the plant

Arduino code and Thingspeak connection

The code I developed was based on this link, containing everything necessary to send information to the Thingspeak service. So see that the part you have to configure is four lines below. Enter your WiFi credentials, channel number and API key (which you can find on your thingspeak dashboard).

const char* ssid = "yourwifiname";   // your network SSID (name) 
const char* password = "yourwifipassword";   // your network password

WiFiClient  client;

unsigned long myChannelNumber = yourchannelnumber;
const char * myWriteAPIKey = "yourchannelapikey";

The code below writes to Thingspeak every 30 seconds, this is important so that we don’t exceed the free account’s writing limit. And also because we are talking about a slow quantity (humidity), update speed is not necessary.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_BMP280.h>

#include <WiFi.h>
#include "ThingSpeak.h"

#define BMP_SCK  (13)
#define BMP_MISO (12)
#define BMP_MOSI (11)
#define BMP_CS   (10)

const char* ssid = "";   // your network SSID (name) 
const char* password = "";   // your network password

WiFiClient  client;

unsigned long myChannelNumber = ;
const char * myWriteAPIKey = "";

Adafruit_BMP280 bmp; // I2C
//Adafruit_BMP280 bmp(BMP_CS); // hardware SPI
//Adafruit_BMP280 bmp(BMP_CS, BMP_MOSI, BMP_MISO,  BMP_SCK);

int stabilization= 0;
int iterationcounter= 0;
int oldtime= 0;
int humidityreadings;
int writetothingspeak= 0;
int temperature;
int pressure;

void setup() {
  // put your setup code here, to run once:
 
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);   
  ThingSpeak.begin(client);  // Initialize ThingSpeak

  /* Default settings from datasheet. */
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500); /* Standby time. */

  unsigned status;
  status = bmp.begin(BMP280_ADDRESS_ALT, BMP280_CHIPID);
  //status = bmp.begin();
  if (!status) {
    Serial.println(F("Could not find a valid BMP280 sensor, check wiring or "
                      "try a different address!"));
    Serial.print("SensorID was: 0x"); Serial.println(bmp.sensorID(),16);
    Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
    Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
    Serial.print("        ID of 0x60 represents a BME 280.\n");
    Serial.print("        ID of 0x61 represents a BME 680.\n");
    while (1) delay(10);
  }
                  
}

void loop() {
  // put your main code here, to run repeatedly:
  if(micros()- oldtime > 10000){ //repeat every 10mS
    oldtime= micros();

    iterationcounter++;
    if(iterationcounter > 97){ //wait 970mS to do anything (in between readings)
      stabilization++;
      
      if(stabilization = 3){
        
        stabilization= 0;
        iterationcounter= 0;
        humidityreadings= analogRead(A0); //read analog value
        Serial.println(humidityreadings);

        Serial.print(F("Temperature = "));
        temperature= bmp.readTemperature();
        Serial.print(temperature);
        Serial.println(" *C");

        Serial.print(F("Pressure = "));
        pressure= bmp.readPressure();
        Serial.print(pressure);
        Serial.println(" Pa");
      }
    }
    writetothingspeak++;
    if(writetothingspeak == 3000){ //write to thingspeak iot server every 30 seconds
      writetothingspeak= 0;

      // Connect or reconnect to WiFi
      if(WiFi.status() != WL_CONNECTED){
        Serial.print("Attempting to connect");
        while(WiFi.status() != WL_CONNECTED){
          WiFi.begin(ssid, password); 
          delay(5000);     
        } 
      Serial.println("\nConnected.");
      }

      // Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different
      // pieces of information in a channel.  Here, we write to field 1.
      
      ThingSpeak.setField(1, humidityreadings);
      ThingSpeak.setField(2, temperature);
      ThingSpeak.setField(3, pressure);
      

      int x = ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);

      /*for(int j= 1; j < 9; j++){
        int x = ThingSpeak.writeField(myChannelNumber, j, humidityreadings[j], myWriteAPIKey);
    
        if(x == 200){
          Serial.println("Channel update successful.");
        }
        else{
          Serial.println("Problem updating channel. HTTP error code " + String(x));
      }
      delay(1000);
    }*/
    

    }
    
  }
  
}

Below you see an example of soil moisture, temperature and ambient atmospheric pressure readings. Note that the humidity value is raw, I haven’t treated it or converted it to a percentage yet.

Thingspeak measurements
Thingspeak measurements

Final words

The ESP32-S2 Franzininho WiFi board is very versatile, we can connect a soil moisture sensor, an atmospheric pressure sensor (BMP280). And after all this send information to the Thingspeak service. By doing a perceptual conversion, which I didn’t do, you can determine how wet or dry the soil is.

I cannot guarantee that the service will be connected, but here is the link to the example dashboard that I used in this article. Want to get to know another sensor? check this article about a NTC thermistor.

Leave a Reply

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