Code Review: ESP32 Automated Watering System for Pothos Plant

Hi everyone,

Is there any hidden bug or hardware conflict in this code for my little degree project? I also want to use this project myself to automate the watering of my Pothos plant at home.

As a side note, I took a 2-year gap year during my final semesters, so I am a bit rusty with my coding skills. Please forgive me if I am a bit slow to understand, but I am eager to learn and fix this project, thank you!

Hardware details:

  • MCU: Arduino WEMOS D1 R32
  • Temperature/Humidity: DHT11
  • Water Level Sensor (Digital input)
  • Soil Moisture Sensor (Analog input on Pin 34)
  • Pump and LED
  • LCD I2C

Here is my current code:

#define SOIL_PIN 34
#define DHTPIN 4
#define LED_PIN 27
#define PUMP_PIN 18
#define WATER_PIN 14
#define BUZZER_PIN 23

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>

#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  pinMode(PUMP_PIN, OUTPUT);
  pinMode(WATER_PIN, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  
  dht.begin();
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("SYSTEM START");
  delay(2000);
  lcd.clear();
}

void loop() {
  int soilValue = analogRead(SOIL_PIN);
  int waterValue = digitalRead(WATER_PIN);
  float humidity = dht.readHumidity();
  float temperature = dht.readTemperature();

  Serial.println("=====");
  Serial.print("Soil: "); Serial.println(soilValue);
  Serial.print("Hum: ");  Serial.println(humidity);

  lcd.setCursor(0,0);
  lcd.print("Soil: ");
  lcd.print(soilValue);
  lcd.print("    ");
  
  lcd.setCursor(0,1);
  lcd.print("Hum: ");
  lcd.print(humidity, 1);
  lcd.print("%   ");

  if (waterValue == LOW) { 
    Serial.println("WATER IS LOW!");
    digitalWrite(PUMP_PIN, LOW);
    digitalWrite(LED_PIN, LOW);
    digitalWrite(BUZZER_PIN, HIGH);
  } 
  else {
    digitalWrite(BUZZER_PIN, LOW); // Turn off alarm if water is fine
    
    if (soilValue > 2500) { 
      digitalWrite(LED_PIN, HIGH);
      digitalWrite(PUMP_PIN, HIGH); // Turn pump on
      
      if (humidity < 50) {
        delay(1500); 
      } else {
        delay(700); 
      }
      digitalWrite(PUMP_PIN, LOW);
    } 
    else {
      digitalWrite(LED_PIN, LOW);
      digitalWrite(PUMP_PIN, LOW);
    }
  }

  delay(2000);
}

.

My specific questions:

  1. Is using delay() inside the main loop safe enough, or will it cause the water level sensor to respond too slowly if the tank runs dry?
  2. Are there any ESP32 pin conflicts? (I am using Pin 34 for analog read, and Pins 4, 27, 18, 23, 35 for others).
  3. Pothos plants prefer drying out between waterings. Is this logic smart enough to prevent overwatering?

Any advice on the code or hardware wiring would be highly appreciated. Thank you!

Generally, I'd make the comment that you are using an WiFi enabled MCU but not making use of its WiFi feature.

If the water value starts low then it appears the plant is never watered although the buzzer sounds.

Thank you soo much for the feedback!

Aahhh, I see, now that i'm thinking, this project have potential, i do think about to use IoT in this project, to notice me via apps when water in low.

I'll try with new code and added IoT into it and some adjustment. I'm really thankfull for notice and telling me, kind sir! hehe :smile:

Instead of starting immediately with code, it is often better to write down a few rules first. These are only examples:

  1. After a full watering, the plant cannot be watered again until the humidity reaches X units.
  2. During a watering session, the pumps stops when either the water value sensor is 1 or the humidity is Y
  3. A buzzer should sound if the sensors indicate that a watering is required and this state has persisted for N minutes.

It is also not clear what you are doing with the soil sensor.

Regarding IOT, you have the choice of posting all measurements (humidity, water value, last watering time etc.) to a web server or you can run a web server on the WEMOS D1 so that people can browse the data there.

What degree is this incidentally?

What is not working in this project?

I would fix the first problem before adding more to a non-working sketch.

Ah, I see, right, it turns out there's still a lot more i don't know yet.
Regarding my degree, my major is computer systems. This project is for automated watering system.. project for my graduation test but I wanted to keep this project a little further as a personal project for home. Since taking a gap year, I've been slow to process knowledge... thank you for your patience, hehe

Personally, when I first started this project, there were a few logic I create:

  • If the moisture is dry and the humidity is low, there's water in the reservoir = pump (++), LED, and buzzer are off.
  • If the moisture is dry and the humidity is normal, around 29°, there's water in the reservoir = pump (+), LED, and buzzer are off.
  • If the moisture is wet and the humidity is low (I will take 23°), there's water in the reservoir = pump is off, LED, and buzzer are off.
  • If the moisture is wet and the humidity is normal, there's water in the reservoir = pump is off, LED, and buzzer are off.
  • If the moisture is wet and the humidity is normal, there's water in the reservoir = pump is off, LED, and buzzer are off.
  • If the moisture is dry and the humidity is low, there's water in the reservoir = pump is off, LED, and buzzer are off.
  • If the moisture is dry and the humidity is low, there's no water in the reservoir = pump is off, LED, and buzzer are on.
    ++ 15 seconds, + 7 seconds, on.

So, i could say my rule is...

  • The buzzer and LED will only turn on when there's no water, to alert the user that the tank is empty.
  • The pump's activation is determined by the moisture and humidity levels. If the soil is dry and humid, the pump will run longer. At normal temperatures but dry soil, the pump will run for 7 seconds.
  • So, as long as there's still water in the reservoir, the automatic system will continue silently...
  • Regarding the buzzer, yes, it will continue to sound until the water is filled (I'll add time here, as I consider your suggestion).

I initially thought so, and honestly, after reading your suggestion before, I've gone into IoT and changed some major code (they're running now hehe). I use Blynk

I attached the updated code here:

#define BLYNK_TEMPLATE_ID "TMPL6FHoVUGef"
#define BLYNK_TEMPLATE_NAME "Pothos System"
#define BLYNK_AUTH_TOKEN "_ucpHa4wbYl0RRAWRe_upo5g-kXz5pqr"

#define SOIL_PIN 34
#define DHTPIN 4
#define LED_PIN 27
#define PUMP_PIN 18
#define WATER_PIN 17
#define BUZZER_PIN 23

const int POTHOS_DRY_THRESHOLD = 40; 
const int SENSOR_DRY_RAW = 4095;     
const int SENSOR_WET_RAW = 0;        

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <BlynkSimpleEsp32.h>

char ssid[] = "Suryani";
char pass[] = "tanggallahir";

#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);

int remoteOverride = 0;
float lastGoodHumidity = 50.0;
float lastGoodTemperature = 27.0;

BLYNK_WRITE(V3) {
  remoteOverride = param.asInt();
}

void setup() {
  Serial.begin(115200); 
  pinMode(LED_PIN, OUTPUT);
  pinMode(PUMP_PIN, OUTPUT);
  pinMode(WATER_PIN, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);
  
  dht.begin();
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("CONNECTING WIFI");
  
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
  
  lcd.clear();
  lcd.print("SYSTEM ONLINE");
  delay(2000);
  lcd.clear();
}

void loop() {
  Blynk.run(); 

  int soilValue = analogRead(SOIL_PIN);
  int waterValue = digitalRead(WATER_PIN); 
  float rawHumidity = dht.readHumidity();
  float rawTemperature = dht.readTemperature();
  
  if (!isnan(rawHumidity) && !isnan(rawTemperature)) {
    lastGoodHumidity = rawHumidity;
    lastGoodTemperature = rawTemperature;
  }

  int moisturePercent = map(soilValue, SENSOR_DRY_RAW, SENSOR_WET_RAW, 0, 100);
  moisturePercent = constrain(moisturePercent, 0, 100);

  Blynk.virtualWrite(V1, moisturePercent);

  lcd.setCursor(0,0);
  lcd.print("Moist: ");
  lcd.print(moisturePercent);
  lcd.print("%    ");

  if (waterValue == LOW) { 
    Serial.println("ALARM STATE: AIR HABIS!");
    
    digitalWrite(PUMP_PIN, LOW);
    
    digitalWrite(LED_PIN, HIGH);   
    digitalWrite(BUZZER_PIN, HIGH);  
    
    lcd.setCursor(0,1);
    lcd.print("!! AIR HABIS !! "); 
    Blynk.virtualWrite(V2, 255); 
  } 

  if (waterValue == HIGH) { 
    digitalWrite(BUZZER_PIN, LOW); 
    digitalWrite(LED_PIN, LOW);     
    Blynk.virtualWrite(V2, 0);     
    
    lcd.setCursor(0,1);
    lcd.print(lastGoodTemperature, 1);
    lcd.print((char)223);
    lcd.print("C ");
    lcd.print("H: ");
    lcd.print(lastGoodHumidity, 0);
    lcd.print("%   ");

    if ((moisturePercent < POTHOS_DRY_THRESHOLD) || (remoteOverride == 1)) {
      digitalWrite(PUMP_PIN, HIGH); 
      
      if (remoteOverride == 1) {
        delay(1000); // 
      } else {
        if (lastGoodHumidity < 50) { 
          delay(15000); 
        } else { 
          delay(7000); 
        }
      }
      digitalWrite(PUMP_PIN, LOW); 
    } 
    else {
      digitalWrite(PUMP_PIN, LOW); 
    }
  }

  delay(3000); 
}

What I will added future is.. It'll be powered by house's electricity to make the device will always on and automaticly usable... and battery also cause i need it wireless when i presentation it later....

And honestly, after reviewing your suggestions, if the device is already reading successfully and smooth, I'll move this IoT to a more colorful web design and sending notification also... So like, I can monitoring my pothos and can be watering it in remote, it'll sending me notification when the water is low so i can fill it... i'm so happy hearing your suggestion.. but i dunno if this plan will make too much and overcomplicated the system in other opinion.. but yeah kjdfnvkjvdfkjnvf ending my degree with something memorable.. :"))