Need some insight

Hi,

I have an Arduino-based circuit checking a soul moisture sensor, then if the moisture is too low, turn on a 12v pump via a 5v relay.

When the 5v relay switches on the reading from the soil sensor vary radically.

Soil moisture level: 96.03%
Raw sensor value: 463
Soil moisture level: 79.40%
Raw sensor value: 774
Soil moisture level: 2.23%
Relay ON - Moisture is too low!
Raw sensor value: 740
Soil moisture level: 10.67%
Raw sensor value: 740
Soil moisture level: 10.67%

Could somebody give me insight as to why that would be the case?

What I also noticed about my sensor is that the sensor value never gets to 0 (or at least close to it) or the upper range of 1024. Rubbish sensor?

Specs:
Capacitive Soil Moisture Sensor v1.2 (3 wire) connected to 5V Arduino Uno

Relay is a 2 channel VS SRD-05VDC-SL-C and the 12v side powered by adjustable PSU connected to a 12v caravan type pump.

Thanks
Chris

Code

int sensorPin = A0;  // Pin connected to the sensor's analog output
int ledPin = 13;  
int sensorValue = 0; // Variable to store sensor reading
float moisturePercent = 0.0;
int pumpRelayPin = 8;    
bool relayOn = false; 
float lastMoisturePercent = 0; // Store the last moisture reading

unsigned long lastChangeTime = 0; // Store the time of the last moisture change
unsigned long relayOnTime = 0;    // Store the time when the relay was turned on
unsigned long currentTime = 0;    // Store the current time
const unsigned long timeout = 10000; // 10 seconds for timeout
const unsigned long relayDelay = 15000; // 1 minute for relay delay

// Calibration values (based on observed sensor extremes)
int dryValue = 783;        // Sensor value when completely dry
int wetValue = 380;        // Sensor value when completely wet

void setup() {
  Serial.begin(9600); 
  pinMode(ledPin, OUTPUT); // Set LED pin as an output
  pinMode(pumpRelayPin, OUTPUT);  // Set relay pin as an output
  digitalWrite(pumpRelayPin, HIGH); // Start with relay OFF
  lastMoisturePercent = 100; 
  digitalWrite(ledPin, LOW); 
}

void loop() {
  currentTime = millis();  // Get the current time in milliseconds

  // Read the value from the sensor
  sensorValue = analogRead(sensorPin);
  Serial.print("Raw sensor value: ");
  Serial.println(sensorValue);

  moisturePercent = 100.0 * (float)(dryValue - sensorValue) / (dryValue - wetValue);
  Serial.print("Soil moisture level: ");
  Serial.print(moisturePercent);
  Serial.println("%");

  // Determine the moisture range
  int range;
  if (moisturePercent <= 25.00) {
    range = 1; // 0% - 25%
  } else if (moisturePercent >= 76.00) {
    range = 4; // 76% - 100%
  } else {
    range = 0; // Other ranges, if needed
  }

  // Switch statement for LED flashing pattern
  digitalWrite(ledPin, LOW); 
  switch (range) {
    case 1:
      digitalWrite(ledPin, HIGH); // LED ON continuously
      break;
    case 4:
      flashLED(150, 150); // Flash slowly
      break;
    default:
      digitalWrite(ledPin, LOW); // LED OFF for other ranges
      break;
  }

  if (moisturePercent <= 10.00 && !relayOn) {
    digitalWrite(pumpRelayPin, LOW);   // LOW to turn the relay ON (most relays are active-low)
    relayOn = true; 
    Serial.println("Relay ON - Moisture is too low!");
    lastChangeTime = currentTime;
    relayOnTime = currentTime; // Record the time when the relay was turned on
  } else if (moisturePercent >= 60.00 && relayOn) {
    if (currentTime - relayOnTime >= relayDelay) {
      relayOn = false;    
      digitalWrite(pumpRelayPin, HIGH);  // HIGH to turn the relay OFF
      Serial.println("Relay OFF - Moisture is sufficient.");
      lastChangeTime = currentTime;  
    }
  }

  // Check if the moisture level has changed; if it has, reset the change timer
  if (moisturePercent != lastMoisturePercent) {
    lastChangeTime = currentTime;
    lastMoisturePercent = moisturePercent;
  }

  // If 10 seconds have passed without moisture change, turn the relay off
  if (relayOn && (currentTime - lastChangeTime >= timeout)) {
    digitalWrite(pumpRelayPin, HIGH);  // HIGH to turn the relay OFF
    relayOn = false;               // Track that the relay is OFF
    Serial.println("Relay OFF - No change in moisture level for 10 seconds.");
  }

  delay(1000); // Adjust if needed
}

void flashLED(int onDuration, int offDuration) {
  digitalWrite(ledPin, HIGH); // Turn LED ON
  delay(onDuration);          // Wait for ON duration
  digitalWrite(ledPin, LOW);  // Turn LED OFF
  delay(offDuration);         // Wait for OFF duration
}

You started a topic in the Uncategorised category of the forum when its description explicitly tells you not to

Your topic has been moved to a relevant category. Please be careful in future when deciding where to start new topics

Hi, @ardaussie

Can you please post your code?

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Can you please post a link to specs/data of your sensor?
What model Arduino are you using?

If you disconnect the pump, does the fault still occur?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

There are countless complaints and articles written about the unreliability of cheap hobbyist moisture sensors. The sensors used by 'real' watering systems are very different and more costly. Research!

First thing to do is remove ALL the delay calls.

Rewrite the whole thing using millis at a global scale, and read up on state machines and arrays and structs.

It’s a learning curve, but they will make the code easier to understand and maintain.

Hi @ardaussie,

regarding this question feel free to read this explanation about the background of the capacitive sensor:

https://how2electronics.com/interface-capacitive-soil-moisture-sensor-arduino/

The post of @sonofcy is absolutely correct, due to their physical principal and components those sensors are cheap and serve simple purposes.

Your calculation

// Calibration values (based on observed sensor extremes)
int dryValue = 783;        // Sensor value when completely dry
int wetValue = 380;        // Sensor value when completely wet

 moisturePercent = 100.0 * (float)(dryValue - sensorValue) / (dryValue - wetValue);

will report values higher than 100% and lower than 0% if the sensor would measure values lower or higher than the calibration values. It might not occur if the measured data are always in the limits of the calibration values. But one would always try to catch possible cases. In the examples of the link above these cases are handled by if-statements. Not the best way but possible ...

P.S.:

I created a version on Wokwi that is structured due to the different tasks and uses millis() for the main loop. I left one small 150 ms delay for flashing the led as it does no harm in this application. Still not "perfect" but probably a better start for changes ,,,

Good luck!

https://wokwi.com/projects/408364396627342337

Sketch
/*
   Wokwi: https://wokwi.com/projects/408364396627342337
   Forum: https://forum.arduino.cc/t/need-some-insight/1299365

   Based on the code from post #1 but modified version

*/

const byte sensorPin = A0;  // Pin connected to the sensor's analog output
const byte ledPin = 13;
int sensorValue = 0; // Variable to store sensor reading
float moisturePercent = 0.0;

const byte pumpRelayPin = 8;
bool relayOn = false;
float lastMoisturePercent = 0; // Store the last moisture reading
byte moistureRange = 0;

unsigned long lastMeasurement = 0; // Store the time of the last moisture measurement
unsigned long lastChangeTime = 0; // Store the time of the last moisture change
unsigned long relayOnTime = 0;    // Store the time when the relay was turned on
unsigned long currentTime = 0;    // Store the current time
const unsigned long timeout = 10000; // 10 seconds for timeout
const unsigned long relayDelay = 15000; // 1 minute for relay delay
const unsigned long INTERVAL  = 1000; // One Measurement every second

// Calibration values (based on observed sensor extremes)
const int dryValue = 783;        // Sensor value when completely dry
const int wetValue = 380;        // Sensor value when completely wet

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT); // Set LED pin as an output
  pinMode(pumpRelayPin, OUTPUT);  // Set relay pin as an output
  digitalWrite(pumpRelayPin, HIGH); // Start with relay OFF
  lastMoisturePercent = 100;
  digitalWrite(ledPin, LOW);
}

void loop() {
  currentTime = millis();  // Get the current time in milliseconds
  if (currentTime - lastMeasurement > INTERVAL) {
    lastMeasurement = currentTime;
    readSensor(); // Read the value from the sensor
    determineMoistureRange(); // Determine the moisture range
    handleLeds(); // Switch statement for LED flashing pattern
    handlePump();
  }
}

void readSensor() {
  sensorValue = analogRead(sensorPin);
  Serial.print("Raw sensor value: ");
  Serial.println(sensorValue);
  sensorValue = constrain(sensorValue, wetValue, dryValue);
  moisturePercent = 100.0 * (float)(dryValue - sensorValue) / (dryValue - wetValue);
  Serial.print("Soil moisture level: ");
  Serial.print(moisturePercent);
  Serial.println("%");
}

void determineMoistureRange() {
  moistureRange = 0;  // Everything larger than 25% and lower than 76%
  // 0% - 25%
  if (moisturePercent <= 25.00) {
    moistureRange = 1;
  }
  // 76% - 100%
  if (moisturePercent >= 76.00) {
    moistureRange = 4;
  }
}

void handleLeds() {
  switch (moistureRange) {
    case 1:
      digitalWrite(ledPin, HIGH); // LED ON continuously
      break;
    case 4:
      digitalWrite(ledPin, HIGH); // Turn LED ON
      delay(150);          // Wait 150 ms for ON duration,
      // No problem as long as it is much shorter than INTERVAL and other timings
      digitalWrite(ledPin, LOW);  // Turn LED OFF
      break;
    default:
      digitalWrite(ledPin, LOW); // LED OFF for other ranges
      break;
  }
}

void handlePump() {
  // if moisture is low and pump is OFF
  if (moisturePercent <= 10.00 && !relayOn) {
    digitalWrite(pumpRelayPin, LOW);   // LOW to turn the relay ON (most relays are active-low)
    relayOn = true;
    Serial.println("Relay ON - Moisture is too low!");
    lastChangeTime = currentTime;
    relayOnTime = currentTime; // Record the time when the relay was turned on
  }

  // if moisture is sufficient and pump is ON
  if (moisturePercent >= 60.00 && relayOn) {
    // Check if pump has been ON for a sufficient time
    if (currentTime - relayOnTime >= relayDelay) {
      relayOn = false;
      digitalWrite(pumpRelayPin, HIGH);  // HIGH to turn the relay OFF
      Serial.println("Relay OFF - Moisture is sufficient.");
      lastChangeTime = currentTime;
    }
  }

  // Check if the moisture level has changed; if it has, reset the change timer
  if (moisturePercent != lastMoisturePercent) {
    lastChangeTime = currentTime;
    lastMoisturePercent = moisturePercent;
  }

  // If timeout have expired without moisture change, turn the relay off
  if (relayOn && (currentTime - lastChangeTime >= timeout)) {
    digitalWrite(pumpRelayPin, HIGH);  // HIGH to turn the relay OFF
    relayOn = false;               // Track that the relay is OFF
    Serial.println("Relay OFF - No change in moisture level for 10 seconds.");
  }
}

Lol

Seems steady to me, from what you posted.

Why are you using a 5V relay with a 12V PSU? It will work, but it will put unnecessary load on the Arduino's 5V regulator. The more obvious choice of a 12V relay with your 12V PSU would have avoided that. Better still, use a MOSFET like IRL44Z or similar. Don't forget to connect a flyback diode across the pump terminals.

One problem with your code it that it doesn't wait for the moisture to be taken up by the soil. You need to switch the pump on for a short period only (like 30s) and wait for the moisture level to stabilise (maybe 15 mins) before taking another reading. Also take multiple readings and average them because there will always be fluctuations in the reading.

As far as never 0 or 1024, normal. Use the map function to create the range you want. 1024 is too big a range, map it to 128 or 256. REMEMBER, these hobbyist sensors rarely work well, you have to keep the terminal ends very clean. Don't let splash back make them dirty again. Somebody posted a link to a proper working sensor a few weeks ago, see if you can find that.

Hi, @ardaussie

Sorry but;

PLEASE do not go back to older posts and edit then with info requested from a later post.

It disjoints the thread and makes it hard to follow.

If you need to answer a post and/or add information, PLEASE do it in a NEW post.

Some links to your sensor would be helpfull.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:

I guess you wanted to address @ardaussie , didn't you?

The constrain function is already used in the Wokwi sketch I posted above.

Let's have a look at a schematic of this circuit.

The simulation @ec2021 uses a slide fader, THX.

Because wokwi doesn't have a sensor like that. But you don't really need one at this stage.

@ardaussie, you should get your code working using a potentiometer as a proxy for the moisture sensor in the same manner.

Separately just watch the sensor and experiment to get a feel,for the range of values reported, and if you have more than one how they differ.

a7

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.