From 463fdb5ef43f6fcecb6c6f7386bb783350607ad7 Mon Sep 17 00:00:00 2001 From: rbaron Date: Wed, 31 Mar 2021 20:12:47 +0200 Subject: [PATCH] Corrects the raw ADC soil mesasure given the current input voltage --- code/b-parasite/config/prst_config.h | 5 +-- code/b-parasite/src/main.c | 2 +- code/b-parasite/src/prst/adc.c | 47 ++++++++++++++++++++++------ code/b-parasite/src/prst/adc.h | 5 +-- 4 files changed, 44 insertions(+), 15 deletions(-) diff --git a/code/b-parasite/config/prst_config.h b/code/b-parasite/config/prst_config.h index 1e7319a..8821b31 100644 --- a/code/b-parasite/config/prst_config.h +++ b/code/b-parasite/config/prst_config.h @@ -12,14 +12,15 @@ // Analog to digital converter (ADC). // Prints out ADC debug info, such as the values read for battery and soil // moisture. -#define PRST_ADC_DEBUG 0 +#define PRST_ADC_BATT_DEBUG 0 +#define PRST_ADC_SOIL_DEBUG 0 // BLE. // Prints out BLE debug info, such as the final encoded advertisement packet. #define PRST_BLE_DEBUG 0 #define PRST_BLE_PROTOCOL_VERSION 1 #define PRST_BLE_MAC_ADDR_LSB1 0x00 -#define PRST_BLE_MAC_ADDR_LSB0 0x01 +#define PRST_BLE_MAC_ADDR_LSB0 0x02 #define PRST_BLE_ADV_NAME "prst" // Total time spend advertising. #define PRST_BLE_ADV_TIME_IN_MS 1000 diff --git a/code/b-parasite/src/main.c b/code/b-parasite/src/main.c index 0d69b13..dccd4bc 100644 --- a/code/b-parasite/src/main.c +++ b/code/b-parasite/src/main.c @@ -59,7 +59,7 @@ static void rtc_callback() { prst_pwm_init(); prst_pwm_start(); prst_adc_batt_read_t batt_read = prst_adc_batt_read(); - prst_adc_soil_moisture_t soil_read = prst_adc_soil_read(); + prst_adc_soil_moisture_t soil_read = prst_adc_soil_read(batt_read.voltage); prst_pwm_stop(); nrf_gpio_pin_clear(PRST_FAST_DISCH_PIN); prst_ble_update_adv_data(batt_read.millivolts, temp_humi.temp_millicelcius, diff --git a/code/b-parasite/src/prst/adc.c b/code/b-parasite/src/prst/adc.c index 851bd78..b198f26 100644 --- a/code/b-parasite/src/prst/adc.c +++ b/code/b-parasite/src/prst/adc.c @@ -27,7 +27,7 @@ static nrf_saadc_value_t sample_adc_channel(uint8_t channel) { } // Caps the argument to the [0, 1] range. -static inline double cap_value(double value) { +static inline double cap_percentage(double value) { return value > 1.0 ? 1.0 : (value < 0.0 ? 0.0 : value); } @@ -72,21 +72,48 @@ prst_adc_batt_read_t prst_adc_batt_read() { ret.raw = (uint16_t)result; ret.voltage = (3.6 * result) / (1 << PRST_ADC_RESOLUTION); ret.millivolts = ret.voltage * 1000; -#if PRST_ADC_DEBUG - NRF_LOG_INFO("[adc] Read battery voltage: %d (raw); %d mV; ", ret.raw, - ret.millivolts, ret.voltage); +#if PRST_ADC_BATT_DEBUG + NRF_LOG_INFO( + "[adc] Read battery voltage: %d (raw); %d mV; " NRF_LOG_FLOAT_MARKER " V", + ret.raw, ret.millivolts, NRF_LOG_FLOAT(ret.voltage)); #endif return ret; } -prst_adc_soil_moisture_t prst_adc_soil_read() { - nrf_saadc_value_t result = sample_adc_channel(PRST_ADC_SOIL_CHANNEL); - double percentage = cap_value(((double)result - PRST_SOIL_DRY) / - (PRST_SOIL_WET - PRST_SOIL_DRY)); +// If you got this far and really want to see how the sausage is made, +// this function estimates the soil moisture percent based on the raw +// ADC value as returned from the saadc. It assumes 10 bits resolution. +// Ideally, we're taking the ADC sample relative to the VDD voltage, so +// this input value should be stable across the range of input voltages. +// In practice, when varying the input voltage, this value is drifting +// enough to be annoying. To account for this drift, I collected ADC readings +// while varying the input voltage from 2V to 3V (CR2032 voltage range) and +// fitted two second degree polynomials over them - one for the sensor +// out in the air (representing a dry soil) and one while holding the +// sensor in my hand (representing a wet soil). +// This raw data is available at the data/ dir at the root of this repository. +static inline double get_soil_moisture_percent( + double battery_voltage, nrf_saadc_value_t raw_adc_output) { + const double x = battery_voltage; + const double dry = -12.9 * x * x + 111 * x + 228; + const double wet = -5.71 * x * x + 60.2 * x + 126; +#if PRST_ADC_SOIL_DEBUG + NRF_LOG_INFO("[adc] batt: " NRF_LOG_FLOAT_MARKER, NRF_LOG_FLOAT(x)); + NRF_LOG_INFO("[adc] dry: " NRF_LOG_FLOAT_MARKER " wet: " NRF_LOG_FLOAT_MARKER, + NRF_LOG_FLOAT(dry), NRF_LOG_FLOAT(wet)); +#endif + return (raw_adc_output - dry) / (wet - dry); +} + +prst_adc_soil_moisture_t prst_adc_soil_read(double battery_voltage) { + nrf_saadc_value_t raw_adc_output = sample_adc_channel(PRST_ADC_SOIL_CHANNEL); + const double percentage = + get_soil_moisture_percent(battery_voltage, raw_adc_output); prst_adc_soil_moisture_t ret; - ret.raw = result; + ret.raw = raw_adc_output; + ret.percentage = cap_percentage(percentage); ret.relative = percentage * (1 << 16); -#if PRST_ADC_DEBUG +#if PRST_ADC_SOIL_DEBUG NRF_LOG_INFO("[adc] Read soil moisture: %d (raw); " NRF_LOG_FLOAT_MARKER " %% (percentage); %u (relative)", ret.raw, NRF_LOG_FLOAT(percentage * 100), ret.relative); diff --git a/code/b-parasite/src/prst/adc.h b/code/b-parasite/src/prst/adc.h index 93c8ed0..95b9a0a 100644 --- a/code/b-parasite/src/prst/adc.h +++ b/code/b-parasite/src/prst/adc.h @@ -18,14 +18,15 @@ typedef struct prst_adc_batt_val { typedef struct prst_adc_soil_moisture { int16_t raw; - // A value from 0 (cmopletely dry) to 2^10 (completely wet). + // A value from 0 (completely dry) to 2^10 (completely wet). uint16_t relative; + double percentage; } prst_adc_soil_moisture_t; void prst_adc_init(); prst_adc_batt_read_t prst_adc_batt_read(); -prst_adc_soil_moisture_t prst_adc_soil_read(); +prst_adc_soil_moisture_t prst_adc_soil_read(double battery_voltage); #endif // _PRST_ADC_H_ \ No newline at end of file