1

I'm using a water level sensor with two outputs, a 5V fan and a water pump. Here's the wiring project.

What I want to do with this project is:

  1. When the water reaches 2 cm, then the water pump is on while the fan is off.
  2. When the water is less than 2 cm, then the water pump is off and fan is on.

The problem is, when the water is below the threshold, the sensor reading is quite unstable, thus making the relay switching too fast (see the video).

How to eliminate this problem?

#include <Wire.h>
#include <Blynk.h>
#include <LiquidCrystal_I2C.h>
#define BLYNK_PRINT Serial
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>

//Initialize the LCD display
LiquidCrystal_I2C lcd(0x27, 16, 2);
char auth[] = "xxx";//Enter your Auth token
char ssid[] = "xxx";//Enter your WIFI name
char pass[] = "xxx";//Enter your WIFI password
BlynkTimer timer;

#define pinSensor A0 
#define PIN_RELAY_1  D3
#define PIN_RELAY_2  D4
const int startThreshold = 2; // pumping shall start at startThreshold [cm] and above
const int stopThreshold  = 1; // pumping shall start at stopThreshold  [cm] and below

int sensorValue = 0; // sensor reading integer
float tinggiAir = 0; // height of water
float sensorVoltage = 0; // voltage sensor
int nilaiMax = 686; // max value of sensor after fully drowning it in water
float panjangSensor = 4.0 ; // sensor's height in cm

enum StatusType {CHECKLEVEL, LEVELHIGH, PUMPING};
StatusType status = LEVELHIGH;   // Let's start with LEVELHIGH to check level and set the display correctly

void setup() {
  Serial.begin(115200);
  lcd.begin(); 
  Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
  lcd.backlight();
  pinMode(PIN_RELAY_1, OUTPUT);
  pinMode(PIN_RELAY_2, OUTPUT);
  digitalWrite(PIN_RELAY_1, LOW);
  digitalWrite(PIN_RELAY_2, LOW);
  lcd.setCursor(0, 0);
  lcd.print("Water level");
  lcd.setCursor(4, 1);
  lcd.print("Monitoring");
  delay(1000);
  lcd.clear();
  timer.setInterval(100L, wLevel);
}
void wLevel() {
   sensorValue = analogRead(pinSensor);
  tinggiAir = sensorValue*panjangSensor/nilaiMax;
  sensorVoltage = sensorValue*5.0/686;
}

void loop() {
      Blynk.run();
     timer.run();
     handleStatus();
}

void handleStatus() {
  switch (status) {
    case CHECKLEVEL:
      // Wait for level above threshold
      if (tinggiAir >= startThreshold) { // if wlevel more than startThreshold [cm], then
        status = LEVELHIGH;
        Serial.println("LEVELHIGH");
      }
      break;
    case LEVELHIGH:
      levelIsHigh();
      break;
    case PUMPING:
      if (tinggiAir < stopThreshold) { // if wlevel less than stopThreshold [cm] (use hysteresis!) then
        levelIsLowAgain();
        status = CHECKLEVEL;
        Serial.println("CHECKLEVEL");
      }
      break;
  }
  displayValues();
}

void displayValues() {
  static int oldSensorValue = -1; // variable untuk menampung nilai baca dari sensor dalam bentuk integer
  float oldTinggiAir = -1; // variabel untuk menampung ketinggian air
  float oldSensorVoltage = -1; // untuk menampung nilai ketinggian air

  if (oldSensorValue != sensorValue ||
      oldTinggiAir   != tinggiAir   ||
      oldSensorVoltage != sensorVoltage) {
    Serial.print("Sensor Value = ");
    Serial.println(sensorValue);
    Serial.print("Sensor Voltage = ");
    Serial.println(sensorVoltage);
    Serial.print("Tinggi Air = ");
    Serial.println(tinggiAir);
    Serial.println();
    // Display ke LCD
    lcd.setCursor(0, 0);
    lcd.print("WLevel:");
    lcd.print(tinggiAir);
    lcd.setCursor(12, 0);
    lcd.print("cm");
    oldSensorValue = sensorValue;
    oldTinggiAir   = tinggiAir;
    oldSensorVoltage = sensorVoltage;
    Blynk.virtualWrite(V0, tinggiAir);
  }
}
void levelIsHigh() {
  digitalWrite(PIN_RELAY_1, HIGH);
  digitalWrite(PIN_RELAY_2, LOW);
  lcd.setCursor(0, 1);
  lcd.print("F:OFF P:ON");
  Blynk.logEvent("wlevel", "wLevel more than 2cm");
  status = PUMPING;
  Serial.println("PUMPING");
}

void levelIsLowAgain() {
  digitalWrite(PIN_RELAY_1, LOW);
  digitalWrite(PIN_RELAY_2, HIGH);
  lcd.setCursor(0, 1);
  lcd.print("F:ON P:OFF");
  status = CHECKLEVEL;
  Serial.println("CHECKLEVEL");
}
4
  • You could read multiple values from the level sensor and average them to get a more stable reading.
    – StarCat
    Nov 8, 2023 at 7:17
  • 3
    And add some hysteresis
    – KIIV
    Nov 8, 2023 at 8:03
  • Try de-bouncing the sensor for several seconds unless you need a faster response.
    – Gil
    Nov 8, 2023 at 8:18
  • Do you prefer a software solution - or hardware solution? If hardware, you need to find a comparator (or op-amp) - perhaps on your ESP8266, or a stand-alone hardware item. REF1, REF2.
    – Seamus
    Nov 8, 2023 at 18:56

1 Answer 1

5

The problem lies in the specification itself. According to your rules, what should the pump do if the water level is exactly 2 cm? Granted, there is no such thing as an exact physical measurement. But you have to realize that every measurement system is noisy. Noise is a fact of life you have to cope with when playing with hardware. You may try to make it smaller (by filtering), but it will always be there. Now the question is: what should the pump do if the difference between the water level and the required 2 cm is less than the noise amplitude?

The standard solution, as suggested by KIIV, is to implement some hysteresis. You define two thresholds: one for turning the pump on and another, lower threshold for turning it off. When the readings are in between these two thresholds, you live the pump alone, in whatever state it was before.

In pseudo-code:

const float low_threshold = 1.9;
const float high_threshold = 2.1;

void loop() {
    float water_level = get_level_reading();
    if (water_level < low_threshold)
        turn_pump_off();
    if (water_level > high_threshold)
        turn_pump_on();
}

If the difference between these thresholds is larger than the noise amplitude, noise will not disturb your system.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.