Soil Moisture Sensor + Multiplexing

Hello folks, i tried to search on the internet but unfortunately i cannot find any helpful answer.
I have 0 experience in coding and electronics so be gentle.

I need to build a soil moisture monitor for 20 pots and check for them via html. The html part will be another point but that's not what i need now.

I have an Arduino Mega 2560, an Arduino Ethernet Shield, a CD74HC4067 16 channels multiplexer and 20 soil moisture capacitive sensors.
The arduino shield is connected on the Arduino Mega board.
I plugged the sensors to a 5v power supplier plugged to the wall, so the current will be stable.
I linked 15 sensors from A0 to A5 on the Arduino Shield and from A6 to A14 on the Arduino Mega.

Multiplexer:
A15 is linked to the SIGNAL of the multiplexer.
S0 -S1 -S2- S3 are linked to D0 - D1 -D2 - D3 on the Ethernet Shield.
5v and GND to the Arduino board.
C0 - C2 - C5 - C9 - C15 are the pins i used on the multiplexer, the others are empty.

The code is the following:

#include "Arduino.h"
#include <light_CD74HC4067.h>
               
CD74HC4067 mux(0, 1, 2, 3);
const int signal_pin = A15; // Pin Connected to Sig pin of CD74HC4067

void setup(){

 Serial.begin(9600);  //com enable
 {
    Serial.begin(9600);
    pinMode(A15, INPUT); // Set as input for reading through signal pin
}
 
 int sensorValue1 = analogRead(A0);
  int sensorValue2 = analogRead(A1);
   int sensorValue3 = analogRead(A2);
    int sensorValue4 = analogRead(A3);
     int sensorValue5 = analogRead(A4);
      int sensorValue6 = analogRead(A5);
       int sensorValue7 = analogRead(A6);
        int sensorValue8 = analogRead(A7);
         int sensorValue9 = analogRead(A8);
          int sensorValue10 = analogRead(A9);
           int sensorValue11 = analogRead(A10);
            int sensorValue12 = analogRead(A11);
             int sensorValue13 = analogRead(A12);
              int sensorValue14 = analogRead(A13);
               int sensorValue15 = analogRead(A14);
                
 int moist1;
 int moist2;
 int moist3;
 int moist4;
 int moist5;
 int moist6;
 int moist7;
 int moist8;
 int moist9;
 int moist10;
 int moist11;
 int moist12;
 int moist13;
 int moist14;
 int moist15;
 int moist16;
 int moist17;
 int moist18;
 int moist19;
 int moist20;  //variables for calculations
 }

void loop()
{int i;
  int value = 0;
  int numReadings = 8;

  for (i = 0; i < numReadings; i++){
    // Read light sensor data.
    value = value + analogRead("A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15");

    // 1ms pause adds more stability between reads.
    delay(1);

  }

for (int i = 0; i < 16; i++) {
        mux.channel(i);
        int val = analogRead(A15);                       // Read analog value
        Serial.println("Vaso - "+String(i)+": "+String(val));  // Print value
        delay(500);
    }
  delay(500);
{
     int moist1 = analogRead(A0);
   delay(10);  
   moist1 = analogRead(A0); //take reading 1
   Serial.print("vaso 1 : ");
   Serial.println(moist1);
   delay(500); //delay to allow ADC
   
     int moist2 = analogRead(A1);
   delay(10);
   moist2 = analogRead(A1);  //take reading 2
   Serial.print("vaso 2 : ");
   Serial.println(moist2);
   delay(500);
   
     int moist3 = analogRead(A2);
   delay(10);
   moist3 = analogRead(A2);  //take reading 3
   Serial.print("vaso 3 : ");
   Serial.println(moist3);
   delay(500);
   
     int moist4 = analogRead(A3);
   delay(10);
   moist4 = analogRead(A3); //take reading 4
   Serial.print("vaso 4 : ");
   Serial.println(moist4);
   delay(500);

     int moist5 = analogRead(A4);
   delay(10);
   moist5 = analogRead(A4); //take reading 5
   Serial.print("vaso 5 : ");
   Serial.println(moist5);
   delay(500);

     int moist6 = analogRead(A5);
   delay(10);
   moist6 = analogRead(A5); //take reading 6
   Serial.print("vaso 6 : ");
   Serial.println(moist6);
   delay(500);

     int moist7 = analogRead(A6);
   delay(10);
   moist7 = analogRead(A6); //take reading 7
   Serial.print("vaso 7 : ");
   Serial.println(moist7);
   delay(500);

     int moist8 = analogRead(A7);
   delay(10);
   moist8 = analogRead(A7); //take reading 8
   Serial.print("vaso 8 : ");
   Serial.println(moist8);
   delay(500);

     int moist9 = analogRead(A8);
   delay(10);
   moist9 = analogRead(A8); //take reading 9
   Serial.print("vaso 9 : ");
   Serial.println(moist9);
   delay(500);

     int moist10 = analogRead(A9);
   delay(10);
   moist10 = analogRead(A9); //take reading 10
   Serial.print("vaso 10  : ");
   Serial.println(moist10);
   delay(500);

     int moist11 = analogRead(A10);
   delay(10);
   moist11 = analogRead(A10); //take reading 11
   Serial.print("vaso 11 : ");
   Serial.println(moist11);
   delay(500);

     int moist12 = analogRead(A11);
   delay(10);
   moist12 = analogRead(A11); //take reading 12
   Serial.print("vaso 12  : ");
   Serial.println(moist12);
   delay(500);

     int moist13 = analogRead(A12);
   delay(10);
   moist13 = analogRead(A12); //take reading 13
   Serial.print("vaso 13 : ");
   Serial.println(moist13);
   delay(500);

     int moist14 = analogRead(A13);
   delay(10);
   moist14 = analogRead(A13); //take reading 14
   Serial.print("vaso 14  : ");
   Serial.println(moist14);
   delay(500);

     int moist15 = analogRead(A14);
   delay(10);
   moist15 = analogRead(A14); //take reading 15
   Serial.print("vaso 15 : ");
   Serial.println(moist15);
   delay(500);

    int moist16 = analogRead(A15);
   delay(10);
   moist16 = analogRead(A15); //take reading 16
   Serial.print("vaso 16 : ");
   Serial.println(moist16);
   delay(500);

    int moist17 = analogRead(A15);
   delay(10);
   moist17 = analogRead(A15); //take reading 17
   Serial.print("vaso 17 : ");
   Serial.println(moist17);
   delay(500);

    int moist18 = analogRead(A15);
   delay(10);
   moist18 = analogRead(A15); //take reading 18
   Serial.print("vaso 18 : ");
   Serial.println(moist18);
   delay(500);

    int moist19 = analogRead(A15);
   delay(10);
   moist19 = analogRead(A15); //take reading 19
   Serial.print("vaso 19 : ");
   Serial.println(moist19);
   delay(500);

    int moist20 = analogRead(A15);
   delay(10);
   moist20 = analogRead(A15); //take reading 20
   Serial.print("vaso 20 : ");
   Serial.println(moist20);
   delay(500);
   }
   }

Now, the issues are:

  1. few sensor displays 1023 as value, others 0 (and they're dry)
    so I thought to average the value, but how? what i did doesn't seems to be correct.

  2. few pins on the Multiplexer gives fake values, they're not connected but they still shows 1023 as value.

  3. how can i transform the values in percentage, so i know exactly how dry the soil is?

Ps: i tried many ways to draw the schematic but i couldn't, hope you can get it anyway..

Did you get the calibration values for each sensor.

How I did that was to put a sensor in water, record its 100% wet reading. Dry the sensor off and record its wet reading. Now you got 0% and 100% for the sensor and just map the value to the range.

here is how I did the thing. You might note RemainingMoisture = 100.0f * (1 - ((adcValue_b - WetValue) / (DryValue - WetValue))); //remaining moisture = 1-(xTarget - xMin) / (xMax - xMin) as a percentage of the sensor wet dry volatges.

void fReadAD( void * parameter )
{
  float    ADbits = 4096.0f;
  float    uPvolts = 3.3f;
  float    adcValue_b = 0.0f; //plant in yellow pot
  uint64_t TimePastKalman  = esp_timer_get_time(); // used by the Kalman filter UpdateProcessNoise, time since last kalman calculation
  float    WetValue = 1.07f; // value found by putting sensor in water
  float    DryValue = 2.732f; // value of probe when held in air
  float    Range = DryValue - WetValue;
  float    RemainingMoisture = 100.0f;
  SimpleKalmanFilter KF_ADC_b( 1.0f, 1.0f, .01f );
  for (;;)
  {
    xEventGroupWaitBits (eg, evtADCreading0, pdTRUE, pdTRUE, portMAX_DELAY ); //
    adcValue_b = float( adc1_get_raw(ADC1_CHANNEL_3) ); //take a raw ADC reading
    adcValue_b = ( adcValue_b * uPvolts ) / ADbits; //calculate voltage
    KF_ADC_b.setProcessNoise( (esp_timer_get_time() - TimePastKalman) / 1000000.0f ); //get time, in microsecods, since last readings
    adcValue_b = KF_ADC_b.updateEstimate( adcValue_b ); // apply simple Kalman filter
    TimePastKalman = esp_timer_get_time(); // time of update complete
    RemainingMoisture = 100.0f * (1 - ((adcValue_b - WetValue) / (DryValue - WetValue))); //remaining moisture =  1-(xTarget - xMin) / (xMax - xMin) as a percentage of the sensor wet dry volatges
    xQueueOverwrite( xQ_RM, (void *) &RemainingMoisture );
    //log_i( "adcValue_b = %f remaining moisture %f%", adcValue_b, RemainingMoisture );
  }
  vTaskDelete( NULL );
}

I'd love to understand what you did, unfortunately i'm a noob and i didn't get a thing of your code
Also, how can i implement it to the multiplexer part?

Anyway, i did not but i don't think i really need a calibration, when it's dry the value is 1023, when it's wet it's 0, i tried a couple of them and they gave me same result, the problem is that if you print just one per time the values you'll get will be something like "1023, 1023, 900, 400, 99, 0, 1023, 1023, 1023" and so on..
So i thought to average the results and i found something called "sum" function to pick just few values (Soil moisture sensor, very noisy readings - #5 by robtillaart) but i have no idea how to implement it.

Hello

You only need an Arduino Uno and the multiplexer to read the sensor values; you dont need the mega. You would wire all of the soil moisture sensor analog out pins to pins C0 - C15 of the multiplexer. The SIG pin of the multiplexer would be wired to the Uno A0. S0 - S3 would be connected to 4 digital pins on the Arduino Uno. Here is an example wiring diagram (minus the voltage dividers on the inputs for testing):


To read a value, you need to set the channel of the mux first, then read the value on that channel. So... you would use a function like this to read each value from each channel:

float readMux(int channel){
  int controlPin[] = {s0, s1, s2, s3};

  int muxChannel[16][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
    {1,1,0,0}, //channel 3
    {0,0,1,0}, //channel 4
    {1,0,1,0}, //channel 5
    {0,1,1,0}, //channel 6
    {1,1,1,0}, //channel 7
    {0,0,0,1}, //channel 8
    {1,0,0,1}, //channel 9
    {0,1,0,1}, //channel 10
    {1,1,0,1}, //channel 11
    {0,0,1,1}, //channel 12
    {1,0,1,1}, //channel 13
    {0,1,1,1}, //channel 14
    {1,1,1,1}  //channel 15
  };

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);

  //return the value
  float voltage = (val * 5.0) / 1024.0;
  return voltage;
}

This function returns a float value of the reading from the soil moisture sensor on whichever channel you selected.

So to read the value from channel 9 in void loop(), you would use float value = readMux(9);. Or, if you were to read all 16 channels once per loop, you would use:

struct  {
  moistureValue;
} moisture[16];

for (int i=0;i<16;i++){
  moisture.moistureValue[i] = readMux(i);
  Serial.println("Moisture Sensor #" + String(i+1) + " = " + String(moisture.moistureValue[i]));
}

With the struct being at the beginning of the code (outside of the void loop()).

Here is a good reference link:

For when I add webpages to my projects, I start with the "Simple Authentication" code found under the "Webserver" examples. Here is an awesome link I use for adding all sorts of cool thinks to my html webpages:

They have way too many awesome things you could add lol. You can also try everything out on their site before adding it to your code.

I hope this helps!

2 Likes

Mate i'm really thankful of what you posted and the time you spent on it, but i'm a complete noob and i can't implement your code into mine.
Where do i have to put the two portions?
one on top and the other? Cuz i tried both in setup and in loop but gives the error that "controlPin was not declared in this scope."

Okay, i made some updates, now my code is the following.
Two questions:

  1. How can i take an average from more samples?
    Because having faulty sensors they gives me different outputs (like 1023 1023 1023 0 0 0 0 1023), so i want to take like 16 samples from the sensors and get an average of thoose numbers
  2. How can i take a percentage also of the A0 to A14 signals?
//Mux control pins
int s0 = 0;
int s1 = 1;
int s2 = 2;
int s3 = 3;

//Mux in "SIG" pin
int SIG_pin = 15;


void setup(){
  pinMode(s0, OUTPUT); 
  pinMode(s1, OUTPUT); 
  pinMode(s2, OUTPUT); 
  pinMode(s3, OUTPUT); 

  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

  Serial.begin(9600);
}


void loop(){
  
    Serial.print(" Percentuale umidita' vaso 1 ");
    Serial.print(" e' : ");
    Serial.println(A0);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 2 ");
    Serial.print(" e' : ");
    Serial.println(A1);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 3 ");
    Serial.print(" e' : ");
    Serial.println(A2);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 4 ");
    Serial.print(" e' : ");
    Serial.println(A3);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 5 ");
    Serial.print(" e' : ");
    Serial.println(A4);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 6 ");
    Serial.print(" e' : ");
    Serial.println(A5);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 7 ");
    Serial.print(" e' : ");
    Serial.println(A6);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 8 ");
    Serial.print(" e' : ");
    Serial.println(A7);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 9 ");
    Serial.print(" e' : ");
    Serial.println(A8);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 10 ");
    Serial.print(" e' : ");
    Serial.println(A9);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 11 ");
    Serial.print(" e' : ");
    Serial.println(A10);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 12 ");
    Serial.print(" e' : ");
    Serial.println(A11);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 13 ");
    Serial.print(" e' : ");
    Serial.println(A12);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 14 ");
    Serial.print(" e' : ");
    Serial.println(A13);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 15 ");
    Serial.print(" e' : ");
    Serial.println(A14);
    delay(1000);
   
 

  //Loop through and read all 16 values
  for(int i = 0; i < 16; i ++){
    Serial.print(" Percentuale umidita'  ");
    Serial.print(i);
    Serial.print(" e' : ");
    Serial.println(readMux(i));
    delay(1000);
  }

}


float readMux(int channel)
{
  int controlPin[] = {s0, s1, s2, s3};

  int muxChannel[16][4]={
    {0,0,0,0}, //channel 0
    {1,0,0,0}, //channel 1
    {0,1,0,0}, //channel 2
    {1,1,0,0}, //channel 3
    {0,0,1,0}, //channel 4
    {1,0,1,0}, //channel 5
    {0,1,1,0}, //channel 6
    {1,1,1,0}, //channel 7
    {0,0,0,1}, //channel 8
    {1,0,0,1}, //channel 9
    {0,1,0,1}, //channel 10
    {1,1,0,1}, //channel 11
    {0,0,1,1}, //channel 12
    {1,0,1,1}, //channel 13
    {0,1,1,1}, //channel 14
    {1,1,1,1}  //channel 15
  };

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], muxChannel[channel][i]);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);

  //return the value
  float voltage = (val * 5.0) / 1024.0;
  return voltage;
}

I’m running errands right now, but I’ll try to get back to you later today.

1 Like

Thank you so much man, I really appreciate it.
No worries, take your time!

For make things quicker i found about the "sum" command to pick from various samples (Soil moisture sensor, very noisy readings - #6 by robtillaart) but i can't add it

  int total=0;
  //read the value at the SIG pin
  for(int i=0;i<16;i++)
    total+= analogRead(SIG_pin);
  float voltage = (total/16.0 * 5.0) / 1024.0;

Not sure how good your readings will be if the sensors are regularly throwing you 1023 or zero though.

Where and how can i implement this?
In the upper part of the sketch results in this error code:

Arduino:1.8.19 (Windows Store 1.8.57.0) (Windows 10), Scheda:"Arduino Mega or Mega 2560, ATmega2560 (Mega 2560)"
sketch_oct01c:13:3: error: expected unqualified-id before 'for'

for(int i=0;i<16;i++)

^~~

sketch_oct01c:13:15: error: 'i' does not name a type

for(int i=0;i<16;i++)

           ^

sketch_oct01c:13:20: error: 'i' does not name a type

for(int i=0;i<16;i++)

                ^

C:\Users\Loller12x\Documents\Arduino\sketch_oct01c\sketch_oct01c.ino: In function 'float readMux(int)':

sketch_oct01c:163:1: error: expected primary-expression before '}' token

}

^

exit status 1

expected unqualified-id before 'for' "

Post what you have.

Shorter version:

float readMux(int channel)
{
  int controlPin[] = {s0, s1, s2, s3};

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], bitRead(channel, i) == 1 ? HIGH : LOW);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);

  //return the value
  float voltage = (val * 5.0) / 1024.0;
  return voltage;
}
//Mux control pins
int s0 = 0;
int s1 = 1;
int s2 = 2;
int s3 = 3;
#include "Arduino.h"
#include <light_CD74HC4067.h>
CD74HC4067 mux(0, 1, 2, 3);
//Mux in "SIG" pin
int SIG_pin = 15;

  

void setup(){

  int total=0;
  //read the value at the SIG pin
  for(int i=0;i<16;i++)
    total+= analogRead(SIG_pin);
  float voltage = (total/16.0 * 5.0) / 1024.0;
  
  pinMode(s0, OUTPUT); 
  pinMode(s1, OUTPUT); 
  pinMode(s2, OUTPUT); 
  pinMode(s3, OUTPUT); 

  digitalWrite(s0, LOW);
  digitalWrite(s1, LOW);
  digitalWrite(s2, LOW);
  digitalWrite(s3, LOW);

  Serial.begin(9600);
}


void loop(){

  
  
    Serial.print(" Percentuale umidita' vaso 1 ");
    Serial.print(" e' : ");
    Serial.println(A0);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 2 ");
    Serial.print(" e' : ");
    Serial.println(A1);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 3 ");
    Serial.print(" e' : ");
    Serial.println(A2);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 4 ");
    Serial.print(" e' : ");
    Serial.println(A3);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 5 ");
    Serial.print(" e' : ");
    Serial.println(A4);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 6 ");
    Serial.print(" e' : ");
    Serial.println(A5);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 7 ");
    Serial.print(" e' : ");
    Serial.println(A6);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 8 ");
    Serial.print(" e' : ");
    Serial.println(A7);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 9 ");
    Serial.print(" e' : ");
    Serial.println(A8);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 10 ");
    Serial.print(" e' : ");
    Serial.println(A9);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 11 ");
    Serial.print(" e' : ");
    Serial.println(A10);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 12 ");
    Serial.print(" e' : ");
    Serial.println(A11);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 13 ");
    Serial.print(" e' : ");
    Serial.println(A12);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 14 ");
    Serial.print(" e' : ");
    Serial.println(A13);
    delay(1000);

    Serial.print(" Percentuale umidita' vaso 15 ");
    Serial.print(" e' : ");
    Serial.println(A14);
    delay(1000);
   
 

  //Loop through and read all 16 values
  for(int i = 0; i < 16; i ++){
    Serial.print(" Percentuale umidita'  ");
    Serial.print(i);
    Serial.print(" e' : ");
    Serial.println(readMux(i));
    delay(1000);
  }

}


float readMux(int channel)
{
  int controlPin[] = {s0, s1, s2, s3};

  //loop through the 4 sig
  for(int i = 0; i < 4; i ++){
    digitalWrite(controlPin[i], bitRead(channel, i) == 1 ? HIGH : LOW);
  }

  //read the value at the SIG pin
  int val = analogRead(SIG_pin);

  //return the value
  float voltage = (val * 5.0) / 1024.0;
  return voltage;
}

Problems: i cannot pick the sample from multireading (so 16 samples and the average of them) and i can't understand how to get a percentage of each read

Pointless! An average of readings that are either 0 or 1023 is a meaningless number. If your sensors are faulty, you need to replace them. But maybe you connected them incorrectly? Please post a schematic.

How? Can you help me? Can't find a pcb maker, neither on Electropeak.com

Who are you replying to?

To you @PaulRB, how can i draw a schematic?

Pencil and paper. Then take photo and post it.

1 Like

Dunno if that is understandable


No, a schematic please. Google to find examples and study them to see what they look like.