Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Arduino function to read a Vegetronix VH400 Soil Moisture Sensor. See http://www.vegetronix.com/Products/VH400/
// MIT License (MIT)
//
// Copyright (c) 2015. Michael Ewald, GeomaticsResearch LLC.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Author: Michael Ewald (mewald@geomaticsresearch.com)
// Web: https://GeomaticsResearch.com
// Last-updated: 2015-07-04
float readVH400(int analogPin) {
// This function returns Volumetric Water Content by converting the analogPin value to voltage
// and then converting voltage to VWC using the piecewise regressions provided by the manufacturer
// at http://www.vegetronix.com/Products/VH400/VH400-Piecewise-Curve.phtml
// NOTE: You need to set analogPin to input in your setup block
// ex. pinMode(<analogPin>, INPUT);
// replace <analogPin> with the number of the pin you're going to read from.
// Read value and convert to voltage
int sensor1DN = analogRead(analogPin);
float sensorVoltage = sensor1DN*(3.0 / 1023.0);
float VWC;
// Calculate VWC
if(sensorVoltage <= 1.1) {
VWC = 10*sensorVoltage-1;
} else if(sensorVoltage > 1.1 && sensorVoltage <= 1.3) {
VWC = 25*sensorVoltage-17.5;
} else if(sensorVoltage > 1.3 && sensorVoltage <= 1.82) {
VWC = 48.08*sensorVoltage-47.5;
} else if(sensorVoltage > 1.82) {
VWC = 26.32*sensorVoltage-7.89;
}
return(VWC);
}
struct VH400 {
double analogValue;
double analogValue_sd;
double voltage;
double voltage_sd;
double VWC;
double VWC_sd;
};
struct VH400 readVH400_wStats(int analogPin, int nMeasurements = 100, int delayBetweenMeasurements = 50) {
// This variant calculates the mean and standard deviation of 100 measurements over 5 seconds.
// It reports mean and standard deviation for the analog value, voltage, and WVC.
// This function returns Volumetric Water Content by converting the analogPin value to voltage
// and then converting voltage to VWC using the piecewise regressions provided by the manufacturer
// at http://www.vegetronix.com/Products/VH400/VH400-Piecewise-Curve.phtml
// NOTE: You need to set analogPin to input in your setup block
// ex. pinMode(<analogPin>, INPUT);
// replace <analogPin> with the number of the pin you're going to read from.
struct VH400 result;
// Sums for calculating statistics
int sensorDNsum = 0;
double sensorVoltageSum = 0.0;
double sensorVWCSum = 0.0;
double sqDevSum_DN = 0.0;
double sqDevSum_volts = 0.0;
double sqDevSum_VWC = 0.0;
// Arrays to hold multiple measurements
int sensorDNs[nMeasurements];
double sensorVoltages[nMeasurements];
double sensorVWCs[nMeasurements];
// Make measurements and add to arrays
for (int i = 0; i < nMeasurements; i++) {
// Read value and convert to voltage
int sensorDN = analogRead(analogPin);
double sensorVoltage = sensorDN*(3.0 / 1023.0);
// Calculate VWC
float VWC;
if(sensorVoltage <= 1.1) {
VWC = 10*sensorVoltage-1;
} else if(sensorVoltage > 1.1 && sensorVoltage <= 1.3) {
VWC = 25*sensorVoltage-17.5;
} else if(sensorVoltage > 1.3 && sensorVoltage <= 1.82) {
VWC = 48.08*sensorVoltage-47.5;
} else if(sensorVoltage > 1.82) {
VWC = 26.32*sensorVoltage-7.89;
}
// Add to statistics sums
sensorDNsum += sensorDN;
sensorVoltageSum += sensorVoltage;
sensorVWCSum += VWC;
// Add to arrays
sensorDNs[i] = sensorDN;
sensorVoltages[i] = sensorVoltage;
sensorVWCs[i] = VWC;
// Wait for next measurement
delay(delayBetweenMeasurements);
}
// Calculate means
double DN_mean = double(sensorDNsum)/double(nMeasurements);
double volts_mean = sensorVoltageSum/double(nMeasurements);
double VWC_mean = sensorVWCSum/double(nMeasurements);
// Loop back through to calculate SD
for (int i = 0; i < nMeasurements; i++) {
sqDevSum_DN += pow((DN_mean - double(sensorDNs[i])), 2);
sqDevSum_volts += pow((volts_mean - double(sensorVoltages[i])), 2);
sqDevSum_VWC += pow((VWC_mean - double(sensorVWCs[i])), 2);
}
double DN_stDev = sqrt(sqDevSum_DN/double(nMeasurements));
double volts_stDev = sqrt(sqDevSum_volts/double(nMeasurements));
double VWC_stDev = sqrt(sqDevSum_VWC/double(nMeasurements));
// Setup the output struct
result.analogValue = DN_mean;
result.analogValue_sd = DN_stDev;
result.voltage = volts_mean;
result.voltage_sd = volts_stDev;
result.VWC = VWC_mean;
result.VWC_sd = VWC_stDev;
// Return the result
return(result);
}
@Hichi

This comment has been minimized.

Copy link

Hichi commented Sep 9, 2016

I got a question concerning the calculation of sensorVoltage:
// Read value and convert to voltage int sensor1DN = analogRead(analogPin); float sensorVoltage = sensor1DN*(3.0 / 1023.0); float VWC;
For the above calculation to work, you have to put a 3V source to your ref_pin on your Arduino.
Usually the ArduinoADC compares the input voltage to 5V. Since the sensor can only put out 3V max that would correspond to 613 analogRead value. In that case your sensorVoltage would never go higher than 1.798V.

I think you have to replace the 3.0 with a 5.0:
// Read value and convert to voltage int sensor1DN = analogRead(analogPin); float sensorVoltage = sensor1DN*(5.0 / 1023.0); float VWC;

Can someone confirm, that what I'am thinking is right, or am I wrong?

@pta-

This comment has been minimized.

Copy link

pta- commented Oct 6, 2016

I also was wondering 5.0 multiplier and the I had other thoughts why not to use internal 3.3V regulator for reference. By doing this you will lose only 10% resolution compared to 3.0V reference.

Connect 3.3V output to AREF and then call analogReference(EXTERNAL)
In this case you should get the sensor voltage with this:
float sensorVoltage = sensor1DN*(3.3 / 1023.0);

3V3 pin: A 3.3 volt supply generated by the on-board regulator. Maximum current draw is 50 mA. AREF pin: The Uno has 6 analog inputs, labeled A0 through A5, each of which provide 10 bits of resolution (i.e. 1024 different values). By default they measure from ground to 5 volts, though is it possible to change the upper end of their range using the AREF pin and the analogReference() function.

To get even better resolution you can connect 3.3V output to AREF through 3.2kΩ resistor giving exact 3.0V reference. (Closest in E12 series is 3.3kΩ => 2,991501416V reference)
You can connect the external reference voltage to the AREF pin through a 3.2K resistor, allowing you to switch between external and internal reference voltages. Note that the resistor will alter the voltage that gets used as the reference because there is an internal 32K resistor on the AREF pin. The two act as a voltage divider, so, for example, 3.3V applied through the resistor will yield 3.3 * 32 / (32 + 3.2) = 3.0V at the AREF pin.
In this case you should get the sensor voltage with this:
float sensorVoltage = sensor1DN*(3.0 / 1023.0);

@afitterling

This comment has been minimized.

Copy link

afitterling commented Jul 23, 2017

I written a cloud service, you can do all this stuff on my cloud for free... no code for it on your micro controller anymore. If you have interest contact me over www.iot-upstream.ch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.