Capacitive Soil Moisture Sensor doesn´t read values

Hello,
I´m trying to get a programm to run but it doesn´t reads the capacitive soil moisture sensor properly.
It starts at around 800 and is counting up until 1017.

Nothing changes if I hold it inside water.

I used following websites programm and changed the analog input to A1 (because in my simple programm the sensor is reading correctly on this Input). -Yes I made the change with grounding the resistor on the capacitive sensor board-

Thank you for helping me out!

(Code in txt attachement cause of too many characters)

Code.txt (9.99 KB)

When I was reading your sketch, a few lines caught my attention. They happened to be the lines to read and calculate the soil humidity :o

Can you make a test-sketch to test the soil humidity ?

The analogRead() function returns an integer: analogRead() - Arduino Reference.
The map() function uses integer math: map() - Arduino Reference.

I don't use the map() function, because I don't like it :frowning:

A low-pass filter only works with float. This is how it is used:

const float weight = 0.05;
float filteredValue;  // global variable
float newValue = 180.0;  // often a local variable
filteredValue = (weight * newValue) + ((1.0 - weight) * filteredValue);

There is no 'previousValue' as you can see.

Do you really need to turn the soil humidity sensor on and off ? Can you keep it on all the time ? I prefer to let the loop() run as often as possible, but now you have that delay of 250 ms.
You can check the soil humidity hundreds times per second or once per hour (when watering with a fixed amount of water). So you can choose what is the easiest for you.

The two most simple filters are:

  • Using the average of multiple samples. Sometimes without a delay between them, sometimes with a delay.
  • A low-pass filter with 'float' as you have now.
    Both filters can be combined if you want.

Do you want to continue with the low-pass filter ? Then I suggest to take more samples to make the variations more smooth and to do everything with float. You could calculate the resulting moisture in percentage (I prefer using a float for the percentage as well).
I would use the filter on the percentage and not on the return value of analogRead(), but I can't tell you why, it seems to be the right thing ::slight_smile:

Can you show the small test-sketch ? Check every variable if it should be 'int' or 'float' and use a cast to 'float' when converting to float.

Is A0 not working ? Is it damaged ? It is possible to damage the whole analog section of the chip while the rest of the chip seems to work normally. If A0 is really not working, then you should get a new Arduino board.

Koepel:
A low-pass filter only works with float.

Not necessarily, you can easily implement the same filter using integer math, it'll usually be much faster, especially on 8-bit AVRs. Here's a discussion of such an implementation, with an Arduino example at the bottom of the page: Exponential moving average filter - C++ implementation

Floduino:
(Code in txt attachement cause of too many characters)

Like Koepel already suggested, write a small test sketch to isolate the problem. The code you posted is too long to debug efficiently, get rid of everything that's not related to measuring the humidity. Even get rid of the filter at first, and simply print the raw measurements to make sure they're sound.

Pieter

Hey Guys, thanks for your help.

The thing is I basically understand this long posted code but I don´t understand sooo good.
(sorry for bad description)
I´m not this good at coding so I thought I´ll use a finished code -that should work.

The short code just reads the analog input of the sensor. It works just fine but its without check of the water level and stuff like in the big code.
(I´ll post my short code later)

I also dont have any clue about the low-pass filters and how to code them...

But I´ll try the changes you suggested and let you know if anything changes.

The Arduino isn´t damaged - I tried it with 3 different arduinos.

I think the delay is just for the sensor because there is a problem I seen before where the sensor is reading wrong measurements if this delay is missing.
The on/off function isnt neccessary but its fine if its there.

So here is my simple sketch, it´s just for watering if the soil goes dry. I commented out the pump section cause of just testing if the sensor is working fine. And its working fine... I get values from 570 if not touched to 320 if I hold it in my hands... so the fault must be somewhere in the main sketch (the long one)

If you are bored I´d appreciate you to look over the sketch :slight_smile: and maybe test it out.

Thank you!

/*
*Für stabile Messung mindestens 7V Vin (Step up)
*Luft Wert: 653
*Wasser Wert: 294
*Erde trocken Wert: 580
*Erde nass Wert:350
*Erde gießen Wert: 500
*Messung über A0  
*Sensor ON / OFF über D2
*Pumpe ON / OFF über D3
*LCD ON / OFF über D4
*/

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

LiquidCrystal_I2C lcd(0x27, 20, 4);

int soil = 0;


void setup() {
  delay(100); //normal 1000
  Serial.begin(9600);
  Wire.begin();
  lcd.init(); // initialize the lcd
  lcd.backlight();
  pinMode(4, OUTPUT); //Sensor 1 ON OFF continuously, D4 = GPIO2
  pinMode(2,OUTPUT); //PUMP on/off (if low = on)
  
  digitalWrite(4, HIGH); //Sensor, High weil GND geschaltet wird, ist also aus
  digitalWrite(2,LOW); //Pumpe

}
  
void loop() {

  //----------------SOIL MEASURE SENSOR 1---------------


  digitalWrite(4, LOW); //Sensor anschalten (GND wird geschalten)
  delay(500);
  soil = analogRead(A1);
delay(500);
  Serial.println(soil);
  lcd.setCursor(0,0);
  lcd.print(soil);
  lcd.print("\n");
  
  
  digitalWrite(4, HIGH);
  delay(500);
  //--------------------PUMP ON IF TO DRY----------------
 
/*
    if (soil > 500) //wenn trockener als "wert"
    {
    digitalWrite (2, HIGH); //schalte pumpe an
    Serial.println("PUMP IS ON     ");
    lcd.setCursor(0,1);
    lcd.print("PUMP IS ON     ");
    Serial.println(soil);
    }
    else
    {
    Serial.println("PUMP OFF     ");
    lcd.setCursor(0,1);
    lcd.print("PUMP IS OFF     ");
    digitalWrite (2, LOW);
    delay(100);
      } */
}

So I made the changes like you told me (see code at the end).

If I let it serial print the desired Moisture, filtered value and moisture value I get the following results:
If sensor is not touched - like dry
750 (fixed value)
547 (filtered value - like in the test sketch if dry)
133 (moistureValue - this must be wrong cause it should be wet with this value)

If I touch and hold it like wet:
750(fixed value)
300 (filteredValue - like in the test sketch if wet)
758 (moistureValue - this must be wrong too, cause it should be dry with this value)

So is there a calculation mistake somewhere?

int checkMoisture()
{
  const float weight=0.5;   //weight used to average the reading - lower value=slower changing average
  digitalWrite(sensorEn,LOW); //turn on sensor
  delay(250); //wait here for a bit
  float filteredMoisture=(weight*analogRead(sensorPin))+(1-weight)*prevMoisture; //filter -weighted average with new reading and previouse reading
  moistureValue= map(filteredMoisture,rawMoistureMin,rawMoistureMax,moistureMax,0);

Ok so I learnd what the map function is and this explains to me know the random small number I get.
I don´t unterstand why he´s mapping it and turning it into an useless number.

Does anyone has any idea?

I´ll delete the map function and replace it by moistureValue = filteredMoisture, because thats the value I actually need.
I´ll let you know if it works :slight_smile:

So I think I fixed it.
The mapping function just messed up a lot because it kind of inverted the value.

moistureValue is now the actual filteredValue which works great.

For the rest of the code I left the mapping (led color state).
I removed the debuging option and let it serialprint the values all the time.

also I changed this section (cause of the now equivalent values)

  else if(moistureValue>desiredMoisture-moistureDiff) waterNeeded=true; //if moisture is lower than desired moisture minus differential set waterNeeded flag
  else if(moistureValue<=desiredMoisture+moistureDiff)

Working code is attatched.
Thanks for helping me out!

Code.txt (9.54 KB)

The map() function is for integers: map() - Arduino Reference.
It says: "The map() function uses integer math ...". It uses signed long values.

You should check your filter. There are a few things wrong with it.

When the 'filteredMoisture' variable is global, then you don't need a 'prevMoisture'.
It is declared twice :o In the function checkMoisture() you declare another one with the same name.

float filteredMoisture=(weight*analogRead(sensorPin))+(1-weight)*prevMoisture;

The first word is 'float', that means you declare a new variable and the name after it 'filteredMoisture' is the name of that variable (which already exists, but there is no error message because it is not a bug in the "C" language).

thank you! I changed it!