diff --git a/code/nrf-connect/CMakeLists.txt b/code/nrf-connect/CMakeLists.txt index 8282b27..bd6089b 100644 --- a/code/nrf-connect/CMakeLists.txt +++ b/code/nrf-connect/CMakeLists.txt @@ -3,4 +3,4 @@ cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(hello_world) -target_sources(app PRIVATE src/main.c src/prst/shtc3.c) +target_sources(app PRIVATE src/main.c src/prst/shtc3.c src/prst/adc.c) diff --git a/code/nrf-connect/nrf52840dk_nrf52840.overlay b/code/nrf-connect/nrf52840dk_nrf52840.overlay index b17fb73..85c6db0 100644 --- a/code/nrf-connect/nrf52840dk_nrf52840.overlay +++ b/code/nrf-connect/nrf52840dk_nrf52840.overlay @@ -61,11 +61,21 @@ zephyr,input-positive = ; zephyr,resolution = <10>; }; + + channel@2 { + reg = <2>; + zephyr,gain = "ADC_GAIN_1_6"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,input-positive = ; + zephyr,resolution = <10>; + // zephyr,oversampling = <8>; + }; }; / { zephyr,user { - io-channels = <&adc 0>, <&adc 1>; + io-channels = <&adc 0>, <&adc 1>, <&adc 2>; }; soil_pwm: soil_pwm { diff --git a/code/nrf-connect/src/main.c b/code/nrf-connect/src/main.c index fe544d4..cb6e022 100644 --- a/code/nrf-connect/src/main.c +++ b/code/nrf-connect/src/main.c @@ -1,69 +1,24 @@ -#include -#include -#include -#include #include #include +#include "prst/adc.h" #include "prst/shtc3.h" LOG_MODULE_REGISTER(main, LOG_LEVEL_DBG); -// PWM_DT_SPEC_GET(DT_LABEL(pwm0)); -// static const struct pwm_dt_spec soil_pwm_dt = -// PWM_DT_SPEC_GET(DT_NODELABEL(soil_pwm)); -// static const uint32_t pulse = DT_PROP(DT_NODELABEL(soil_pwm), pulse); - -// static const struct adc_channel_cfg soil_adc_config = -// // ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_0)); -// ADC_CHANNEL_CFG_DT(DT_NODELABEL(soil_adc_channel)); -// static const struct adc_dt_spec soil_adc_spec = -// ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_0)); -// ADC_DT_SPEC_GET(DT_NODELABEL(soil_adc_channel)); -static const struct adc_dt_spec adc_soil_spec = - ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0); - -// #define ERR_CHECK(expr) \ -// { \ -// err = expr; \ -// if (err != 0) {\ -// LOG_ERR("Error " __lineno__); -// }\ -// } - void main(void) { - // prst_shtc3_read_t shtc3_read = prst_shtc3_read(); - // pwm_set_pulse_dt(&soil_pwm_dt, pulse); - int err; - int16_t buf; - int32_t val_mv; - struct adc_sequence sequence = { - .buffer = &buf, - /* buffer size in bytes, not number of samples */ - .buffer_size = sizeof(buf), - }; - - err = adc_channel_setup_dt(&adc_soil_spec); - if (err) { - LOG_ERR("Error in adc_channel_setup_dt"); + if (prst_adc_init() != 0) { + LOG_ERR("Error initializing ADC."); } - err = adc_sequence_init_dt(&adc_soil_spec, &sequence); - if (err) { - LOG_ERR("Error in adc_sequence_init_dt"); - } while (true) { - err = adc_read(adc_soil_spec.dev, &sequence); - if (err) { - LOG_ERR("Error in adc_read"); - } - val_mv = buf; - err = adc_raw_to_millivolts_dt(&adc_soil_spec, &val_mv); - if (err < 0) { - printk(" (value in mV not available)\n"); - } else { - printk(" = %u mV\n", val_mv); - } + prst_adc_read_t batt = prst_adc_batt_read(); + prst_adc_soil_moisture_t soil = prst_adc_soil_read(batt.voltage); + + // LOG_INF("Batt: %d mV", batt.millivolts); + LOG_INF("Soil: %.0f %% (%.3f mV)", 100 * soil.percentage, + soil.adc_read.voltage); + k_msleep(500); } } diff --git a/code/nrf-connect/src/prst/adc.c b/code/nrf-connect/src/prst/adc.c index ac63d0c..2906c3d 100644 --- a/code/nrf-connect/src/prst/adc.c +++ b/code/nrf-connect/src/prst/adc.c @@ -1,3 +1,139 @@ #include "adc.h" -prst_adc_photo_sensor_t prst_adc_photo_read(double battery_voltage) {} \ No newline at end of file +#include +#include +#include +#include + +LOG_MODULE_REGISTER(adc, LOG_LEVEL_DBG); + +// PWM spec for square wave. Input to the soil sensing circuit. +static const struct pwm_dt_spec soil_pwm_dt = + PWM_DT_SPEC_GET(DT_NODELABEL(soil_pwm)); +static const uint32_t pulse = DT_PROP(DT_NODELABEL(soil_pwm), pulse); + +// Shared buffer and adc_sequennce. +static int16_t buf; +static struct adc_sequence sequence = { + .buffer = &buf, + .buffer_size = sizeof(buf), +}; + +static const struct adc_dt_spec adc_soil_spec = + ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0); + +static const struct adc_dt_spec adc_lux_spec = + ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1); + +static const struct adc_dt_spec adc_batt_spec = + ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 2); + +// TODO: recalibrate with v2 hardware. +static inline float get_soil_moisture_percent(float battery_voltage, + int16_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; + LOG_DBG("Batt: %.2f", x); + LOG_DBG("Dry: %.2f | wet: %.2f", dry, wet); + return (raw_adc_output - dry) / (wet - dry); +} + +static int read_adc_spec(const struct adc_dt_spec* spec, prst_adc_read_t* out) { + int err; + err = adc_sequence_init_dt(spec, &sequence); + if (err) { + LOG_ERR("Error in adc_sequence_init_dt"); + return err; + } + err = adc_read(spec->dev, &sequence); + if (err) { + LOG_ERR("Error in adc_read"); + return err; + } + + int32_t val_mv = buf; + err = adc_raw_to_millivolts_dt(spec, &val_mv); + if (err) { + LOG_ERR("Error in adc_read"); + return err; + } + + out->raw = buf; + out->millivolts = val_mv; + out->voltage = val_mv / 1000.0f; + return 0; +} + +int prst_adc_init() { + const struct adc_dt_spec* all_specs[] = { + &adc_soil_spec, + &adc_lux_spec, + &adc_batt_spec, + }; + int err; + for (int i = 0; i < sizeof(all_specs) / sizeof(all_specs[0]); i++) { + err = adc_channel_setup_dt(all_specs[i]); + if (err) { + LOG_ERR("Error setting up adc_soil_spec"); + return err; + } + } + return 0; +} + +prst_adc_read_t prst_adc_batt_read() { + int err; + prst_adc_read_t adc_read; + err = read_adc_spec(&adc_batt_spec, &adc_read); + if (err) { + LOG_ERR("Error in prst_adc_batt_read"); + } + return adc_read; +} + +prst_adc_soil_moisture_t prst_adc_soil_read(float battery_voltage) { + int err; + // Start PWM. + err = pwm_set_pulse_dt(&soil_pwm_dt, pulse); + if (err) { + LOG_ERR("Error in pwm_set_pulse_dt"); + } + k_msleep(30); + + prst_adc_soil_moisture_t adc_soil_read; + err = read_adc_spec(&adc_soil_spec, &adc_soil_read.adc_read); + if (err) { + LOG_ERR("Error in prst_adc_batt_read"); + } + + // Stop PWM. + err = pwm_set_pulse_dt(&soil_pwm_dt, 0); + if (err) { + LOG_ERR("Error in pwm_set_pulse_dt"); + } + + adc_soil_read.percentage = get_soil_moisture_percent(battery_voltage, buf); + return adc_soil_read; +} + +prst_adc_photo_sensor_t prst_adc_photo_read(float battery_voltage) { + // int err; + // // Start PWM. + // err = pwm_set_pulse_dt(&soil_pwm_dt, pulse); + // if (err) { + // LOG_ERR("Error in pwm_set_pulse_dt"); + // } + // k_msleep(30); + + // err = adc_sequence_init_dt(&adc_soil_spec, &sequence); + // if (err) { + // LOG_ERR("Error in adc_sequence_init_dt"); + // } + // err = adc_read(adc_soil_spec.dev, &sequence); + // if (err) { + // LOG_ERR("Error in adc_read"); + // } + prst_adc_photo_sensor_t ret; + return ret; +} diff --git a/code/nrf-connect/src/prst/adc.h b/code/nrf-connect/src/prst/adc.h index 3b568b8..a40d6dd 100644 --- a/code/nrf-connect/src/prst/adc.h +++ b/code/nrf-connect/src/prst/adc.h @@ -3,32 +3,32 @@ #include -typedef struct prst_adc_batt_val { +typedef struct { int16_t raw; - uint16_t millivolts; - double voltage; -} prst_adc_batt_read_t; + int32_t millivolts; + float voltage; +} prst_adc_read_t; -typedef struct prst_adc_soil_moisture { - int16_t raw; +typedef struct { + prst_adc_read_t adc_read; // A value from 0 (completely dry) to 2^10 (completely wet). uint16_t relative; - double percentage; + float percentage; } prst_adc_soil_moisture_t; typedef struct prst_adc_photo_sensor { int16_t raw; - double voltage; + float voltage; // Value in lux. uint16_t brightness; } prst_adc_photo_sensor_t; -void prst_adc_init(); +int prst_adc_init(); -prst_adc_batt_read_t prst_adc_batt_read(); +prst_adc_read_t prst_adc_batt_read(); -prst_adc_soil_moisture_t prst_adc_soil_read(double battery_voltage); +prst_adc_soil_moisture_t prst_adc_soil_read(float battery_voltage); -prst_adc_photo_sensor_t prst_adc_photo_read(double battery_voltage); +prst_adc_photo_sensor_t prst_adc_photo_read(float battery_voltage); #endif // _PRST_ADC_H_ \ No newline at end of file