# How to call variables from a Struct function for a Soil Moisture Sensor

Hello

I know I'm in over my head but I bought this VH400 soil moisture sensor and I would like to provide the most accurate results to my data logger and display. The volumetric water content does not increase linearly with voltage so some nice fellow from MIT posted a struct function online to sort all that out. I read up a bit on how Struct functions operate but I can't understand how to call variables within that function to Serial.print (for now until I figure out how to output to LCD and data log) so I can see his function work with different soil samples. Note I have a 3.3k resistor in series from 3.3v to AREF. I have 3.005V reading at AREF. I read something about maybe using a union but I really need someone to help with syntax. Any help would be appreciated!

``````int analogPin = analogRead (0);   //Vegetronix soil moisture sensor input
double analogValue;
double analogValue_sd;
double voltage;
double voltage_sd;
double VWC;
double VWC_sd;

void setup()
{
Serial.begin(9600);
analogReference(EXTERNAL); // use AREF for reference voltage
pinMode(0, INPUT);
}

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(0);
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(0);
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);
}

void loop()
{
analogValue = struct VH400.analogValue;            // thought this might work from a video I watched online
analogValue_sd = struct VH400.analogValue_sd;
voltage = struct VH400.voltage;
voltage_sd = struct VH400.voltage_SD;
VWC = struct VH400.VWC;
VWC_sd = struct VH400.VWC_sd;

Serial.print("Analog Value = ");
Serial.println(analogValue);
Serial.print("Analog Value SD = ");
Serial.println(analogValue_sd);
Serial.print("Voltage = ");
Serial.println(voltage);
Serial.print("Voltage SD = ");
Serial.println(voltage_sd);
Serial.print("VWC = ");
Serial.println(VWC);
Serial.print("VWC SD = ");
Serial.println(VWC_sd);
delay(500);
}
``````

IRRIGATION_CONTROL.ino (5.16 KB)

Please post your sketch as described in the forum guide in the sticky post. If it's too long, post a shorter version which is just enough to demonstrate the problem you are having. Also please post a schematic and links to the specs of the sensors and other components. Many forum members use phones and tablets to view the forum and can't open .ino files.

If you replace the loop function with this version, it will compile at least:

``````void loop()
{
VH400 vh400 = readVH400_wStats(A0);
Serial.print("Analog Value = ");
Serial.println(vh400.analogValue);
Serial.print("Analog Value SD = ");
Serial.println(vh400.analogValue_sd);
Serial.print("Voltage = ");
Serial.println(vh400.voltage);
Serial.print("Voltage SD = ");
Serial.println(vh400.voltage_sd);
Serial.print("VWC = ");
Serial.println(vh400.VWC);
Serial.print("VWC SD = ");
Serial.println(vh400.VWC_sd);
delay(500);
}
``````

Whether it works is a different matter. I guessed the Analog pin, so that may need adjustment for your wiring.

OP's complete sketch:

``````int analogPin = analogRead (0);   //Vegetronix soil moisture sensor input
double analogValue;
double analogValue_sd;
double voltage;
double voltage_sd;
double VWC;
double VWC_sd;

void setup()
{
Serial.begin(9600);
analogReference(EXTERNAL); // use AREF for reference voltage
pinMode(0, INPUT);
}

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(0);
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(0);
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);
}

void loop()
{
analogValue = struct VH400.analogValue;            // thought this might work from a video I watched online
analogValue_sd = struct VH400.analogValue_sd;
voltage = struct VH400.voltage;
voltage_sd = struct VH400.voltage_SD;
VWC = struct VH400.VWC;
VWC_sd = struct VH400.VWC_sd;

Serial.print("Analog Value = ");
Serial.println(analogValue);
Serial.print("Analog Value SD = ");
Serial.println(analogValue_sd);
Serial.print("Voltage = ");
Serial.println(voltage);
Serial.print("Voltage SD = ");
Serial.println(voltage_sd);
Serial.print("VWC = ");
Serial.println(VWC);
Serial.print("VWC SD = ");
Serial.println(VWC_sd);
delay(500);
}
``````
``````int analogPin = analogRead (0);
``````

This looks like a mistake. it declares a global variable that never gets used. From its name, I would expect it to be a pin number, but it probably won't be - it will be some number between 0 and 1023 depending on the voltage on pin A0. analogRead(0) will assume you mean analogRead(A0). However...

``````pinMode(0, INPUT);
``````

pinMode() does not assume you mean A0, it will assume you mean D0. You forgot to mention something important - what model of Arduino you are using! On many Arduino models, D0 is also known as the RX pin - the RX and TX pins are used for sketch upload and communication with Serial Monitor. So D0 is already an input anyway, but you don't want to be using it for some other purpose. I don't think you are doing that, I think you meant to put pinMode(A0, INPUT). Not strictly necessary either, as all pins default to INPUT, but it serves as a reminder and does no harm.

some nice fellow from MIT posted a struct function online

A struct is not a function, it does not contain any code, only a group of variables.

I can't understand how to call variables within that function

Well, you can't "call" a variable, it doesn't contain any code either. But you can access, ie. read or write a value to a variable.

I think your confusion over these concepts probably made it hard to understand what is going on in that code.

Try wildbill's code - at least the important readVH400_wStats() function will actually get used!

Thanks for the quick replies Paul and Bill. Yes I needed the "VH400 vh400 = readVH400_wStats(A0);" I will further study the Struct and attempt to understand the syntax.