Problem reading analogue pins

Hi all,

Can someone that knows more about analogue pins than me please help.

Now, right from the get go I know this is my lack of knowledge so I hope you can help

This is on a Mega 2560 board - Note - I haven't done any analogue configuration in the Set Up function. I have tried this on pins A3 to A1 but no difference in outcome

I'm using 3 DFRobot waterproof capacitive soil moisture sensors.

When the sensor is completely immersed in water I can read 0.39Vdc on a multiple meter

When the sensor is in dry air I can read 2.28Vdc on a multiple meter

I have the following function that is producing some very strange results that I can not for the life of me understand why.

void Read_Soil_Moisture() {

  double Range = 0;

  double Sensor_Value_01 = 0;

  double Sensor_Value_02 = 0;

  double Sensor_Value_03 = 0;

  double Sensor_Min = 0.29;  // 0.29Vdc  - Completely wet (measured via muiltple meter)

  double Sensor_Max = 2.28;  // 2.28Vdc - Completely dry (measured via muiltple meter)

  Range = Sensor_Max - Sensor_Min;  // range is Vdc from completely wet to completely dry

  Sensor_Value_01 = analogRead(Soil_Moisture_01_Pin);                  // read analogue A7

  Soil_Moisture_Percent_01 = 100 - ((Sensor_Value_01 / Range) * 100);  // calculate the inverse percentage reading

  Sensor_Value_02 = analogRead(Soil_Moisture_02_Pin);                  // read analogue A5

  Soil_Moisture_Percent_02 = 100 - ((Sensor_Value_02 / Range) * 100);  // calculate the inverse percentage reading

  Sensor_Value_03 = analogRead(Soil_Moisture_03_Pin);                  // read analogue A6

  Soil_Moisture_Percent_03 = 100 - ((Sensor_Value_03 / Range) * 100);  // calculate the inverse percentage reading

  Serial.print("Range ");

  Serial.print(Range, 2);

  Serial.print(" Vdc  ");

  Serial.print("A7 ");

  Serial.print(Sensor_Value_01, 2);

  Serial.print(" Vdc   ");

  Serial.print(" A6 ");

  Serial.print(Sensor_Value_02, 2);

  Serial.print(" Vdc   ");

  Serial.print(" A5 ");

  Serial.print(Sensor_Value_03, 2);

  Serial.println(" Vdc   ");

}

Note - I have kept local variables purely to make reading the function easier

The Serial Mointer output (examples) are

Range 1.99 Vdc A7 0.00 Vdc A6 461.00 Vdc A5 451.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 459.00 Vdc A5 449.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 462.00 Vdc A5 452.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 460.00 Vdc A5 450.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 462.00 Vdc A5 451.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 459.00 Vdc A5 449.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 461.00 Vdc A5 451.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 459.00 Vdc A5 450.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 462.00 Vdc A5 452.00 Vdc
Range 1.99 Vdc A7 0.00 Vdc A6 459.00 Vdc A5 449.00 Vdc

So the range is correct but not only are the A7, A6 & A5 readings completely wrong they are erratic and all over the place which means I get a very unstable display when in fact the real life values barely change - as one would expect when measuring soil moisture

Question - how do I make the readings more stable - can I use pull up or pull down resistors etc

Where does the 461Vdc type readings come from - if I had 461Vdc on an analogue pin Id have smoke

Anyway, could some one with much grater knowledge than me please help out

As always your help will be very much appreciated

Kind Regards Grant Brown

Hi @grantkbrown.

The value the analogRead function returns is not in volt units:

https://docs.arduino.cc/language-reference/en/functions/analog-io/analogRead/

it will map input voltages between 0 and the operating voltage (+5 VDC) into integer values between 0 and 1023.

You can convert the value returned by the function to volts like so:

const float V_REF = 5.0;  // The board's analog reference (V).
float Sensor_Value_01_Volts = (Sensor_Value_01 / 1023.0) * V_REF;  // Map 10 bit ADC reading to volts.

It would be better to do all calcs with integers.
You can convert the range to integer values by multiplying with 1024/5.0.

Thank you to all

I knew Id made a mistake

anyway problem solved with the proper conversion to Volts Dc

Thank You all for your help

You are welcome. I'm glad it is working now.

In regards to the other problem you reported:

I guess maybe the reason it seemed like they were changing very significantly was due to the incorrect units?

If you want something to show the human then yes.

But if you are using the readings to make things happen, like with setpoints ferinstance then you are better off with the int read if only to avoid using floats which are very slow (except for power of 10 multiply and divide of course) on all boards w/o FPU.

As for Arduino code, floats and doubles compile in 32-bit IEEE floating point variables where you can work in 64-bit type long long (I prefer unsigned) and achieve 19 places precision, more if you follow remainders that could end up slower than 6-place floats.

Arduino compatible PJRC Teensy's 3.5, 3.6, 4.0 & 4.1 have ARM w/FPU cores, are Franken-duinos sized to fit in tight spots. They do fast FP, 4.1 clocks 600 MHz.

  Sensor_Value_01 = analogRead(Soil_Moisture_01_Pin);                  // read analogue A7

  Soil_Moisture_Percent_01 = 100 - ((Sensor_Value_01 / Range) * 100);  // calculate the inverse percentage reading

  Sensor_Value_02 = analogRead(Soil_Moisture_02_Pin);                  // read analogue A5

  Soil_Moisture_Percent_02 = 100 - ((Sensor_Value_02 / Range) * 100);  // calculate the inverse percentage reading

  Sensor_Value_03 = analogRead(Soil_Moisture_03_Pin);                  // read analogue A6

  Soil_Moisture_Percent_03 = 100 - ((Sensor_Value_03 / Range) * 100);  // calculate the inverse percentage reading

When the one ADC switches what pin to read, the read circuit needs time to settle if you want a good read. What the datasheet recommends is when you change pins, read twice and ignore the first read. IIRC settle time is about 1/2 read time ~55 us.

Maybe those float (code says double, LOL) operations take long enough to settle the pin.. integer calcs would not.

  analogRead(Soil_Moisture_01_Pin);
  Sensor_Value_01 = analogRead(Soil_Moisture_01_Pin);                  // read analogue A7

  Soil_Moisture_Percent_01 = 100 - ((Sensor_Value_01 / Range) * 100);  // calculate the inverse percentage reading

  analogRead(Soil_Moisture_02_Pin);
  Sensor_Value_02 = analogRead(Soil_Moisture_02_Pin);                  // read analogue A5

  Soil_Moisture_Percent_02 = 100 - ((Sensor_Value_02 / Range) * 100);  // calculate the inverse percentage reading

  analogRead(Soil_Moisture_03_Pin);
  Sensor_Value_03 = analogRead(Soil_Moisture_03_Pin);                  // read analogue A6

  Soil_Moisture_Percent_03 = 100 - ((Sensor_Value_03 / Range) * 100);  // calculate the inverse percentage reading

Why convert to volt.
Pre-calculate range limits and go direct from A/D value to moisture.
100% (wet) should be an A/D value of 0.29/51024 = ~59. Dry = 2.28/51024 = ~467.

byte Sensor_01 = map(analogRead(sensor_01_Pin), 59, 467, 0, 100); // map range to 0-100%

Before changing the code any further I'd try one simple test

Disconnect one sensor and feed that analog pin from a 10k/10k divider between 5V and GND. You should get roughly 512 and it should be very steady

If that is stable then the ADC and code are probably fine and I'd start looking at the sensor cable grounding or noise instead

If it still jumps then I'd look at AVCC AREF and the Mega itself

That test should narrow this down pretty quickly

It seems you've found the calculation issue.

Just some thoughts on analog inputs:

  • Always start by displaying the RAW A/D output. This allows you to check the sensor => A/D without any calculations to get in the way.
  • The A/D takes a "snap shot" of the input voltage. It samples for only a few microseconds. Any noise will cause significant variations in the output. For soil moisture (which has no speed requirements you should put a 5k to 8k resistor in series and a 0.1µF capacitor from the input to ground. These should be as close to the board inputs as possible.