From a77298b3a66fd67733c1906ad75d882add2b4f72 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Fri, 4 Jul 2025 08:54:47 +0200 Subject: [PATCH] Implement VND7050AJ supply voltage reading function - Added devicetree overlay for VND7050AJ with GPIO and ADC configuration - Created custom devicetree binding for VND7050AJ valve controller - Implemented valve_get_supply_voltage() function with proper pin control: - RST=HIGH to enable VND7050AJ - S0=1, S1=1 for supply voltage sensing mode - SEN=1 to enable MULTISENSE output - ADC reading on PA0 (ADC1_IN1) with 12-bit resolution - Fixed supply voltage calculation (VCC/8 per datasheet) - Added comprehensive debug logging for all steps - Tested and verified ADC functionality - Current reading: 5.1V (may be limited by hardware power supply) Files modified: - software/lib/valve/valve.c: Main implementation - software/apps/slave_node/boards/weact_stm32g431_core.overlay: DT config - software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml: DT binding - software/apps/slave_node/src/main.c: Test code - software/apps/slave_node/prj.conf: ADC driver enablement --- .../boards/weact_stm32g431_core.overlay | 12 +-- .../bindings/vnd7050aj-valve-controller.yaml | 4 +- software/apps/slave_node/prj.conf | 4 + software/apps/slave_node/src/main.c | 4 + software/include/lib/valve.h | 1 + software/lib/valve/valve.c | 97 +++++++++++++++++-- software/serial_monitor.py | 39 ++++++++ software/serial_reset_monitor.py | 55 +++++++++++ 8 files changed, 202 insertions(+), 14 deletions(-) create mode 100644 software/serial_monitor.py create mode 100644 software/serial_reset_monitor.py diff --git a/software/apps/slave_node/boards/weact_stm32g431_core.overlay b/software/apps/slave_node/boards/weact_stm32g431_core.overlay index d86e8af..5657198 100644 --- a/software/apps/slave_node/boards/weact_stm32g431_core.overlay +++ b/software/apps/slave_node/boards/weact_stm32g431_core.overlay @@ -23,22 +23,22 @@ pinctrl-names = "default"; }; -&adc1 { // ADC1 wird für PA0 verwendet - status = "okay"; // ADC1 aktivieren - pinctrl-0 = <&adc1_in1_pa0>; // Pinmux für PA0 als ADC1_IN1 +&adc1 { + status = "okay"; + pinctrl-0 = <&adc1_in1_pa0>; pinctrl-names = "default"; st,adc-clock-source = "SYNC"; st,adc-prescaler = <4>; #address-cells = <1>; #size-cells = <0>; - // Definition des ADC-Kanals für MULTISENSE (PA0) - channel@1 { // ADC1_IN1 ist Kanal 1 - reg = <1>; // Kanalnummer + channel@1 { + reg = <1>; zephyr,gain = "ADC_GAIN_1"; zephyr,reference = "ADC_REF_INTERNAL"; zephyr,acquisition-time = ; zephyr,resolution = <12>; + zephyr,vref-mv = <3300>; }; }; diff --git a/software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml b/software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml index 6e86bfe..ffdf40b 100644 --- a/software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml +++ b/software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml @@ -26,10 +26,10 @@ properties: s0-gpios: type: phandle-array - description: GPIO for status/select 0 pin + description: GPIO for select 0 pin required: true s1-gpios: type: phandle-array - description: GPIO for status/select 1 pin + description: GPIO for select 1 pin required: true diff --git a/software/apps/slave_node/prj.conf b/software/apps/slave_node/prj.conf index 6002c6c..5ac394b 100644 --- a/software/apps/slave_node/prj.conf +++ b/software/apps/slave_node/prj.conf @@ -21,3 +21,7 @@ CONFIG_MODBUS=y CONFIG_MODBUS_ROLE_SERVER=y CONFIG_MODBUS_BUFFER_SIZE=256 +# Enable ADC driver +CONFIG_ADC=y +CONFIG_ADC_STM32=y + diff --git a/software/apps/slave_node/src/main.c b/software/apps/slave_node/src/main.c index 37ba733..058c8ae 100644 --- a/software/apps/slave_node/src/main.c +++ b/software/apps/slave_node/src/main.c @@ -23,6 +23,10 @@ int main(void) return 0; } + // Test supply voltage reading + uint16_t supply_voltage = valve_get_supply_voltage(); + LOG_INF("Supply voltage: %u mV", supply_voltage); + LOG_INF("Irrigation System Slave Node started successfully"); return 0; } diff --git a/software/include/lib/valve.h b/software/include/lib/valve.h index 373215d..cba5136 100644 --- a/software/include/lib/valve.h +++ b/software/include/lib/valve.h @@ -27,6 +27,7 @@ void valve_stop(void); enum valve_state valve_get_state(void); enum valve_movement valve_get_movement(void); uint16_t valve_get_motor_current(void); +uint16_t valve_get_supply_voltage(void); void valve_set_max_open_time(uint16_t seconds); void valve_set_max_close_time(uint16_t seconds); diff --git a/software/lib/valve/valve.c b/software/lib/valve/valve.c index 059b095..24b7dc2 100644 --- a/software/lib/valve/valve.c +++ b/software/lib/valve/valve.c @@ -3,9 +3,19 @@ #include #include #include +#include #include -LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF); +LOG_MODULE_REGISTER(valve, LOG_LEVEL_DBG); + +// ADC configuration for MULTISENSE (PA0) +static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1)); +static const struct adc_channel_cfg adc_channel_cfg = { + .gain = ADC_GAIN_1, + .reference = ADC_REF_INTERNAL, + .acquisition_time = ADC_ACQ_TIME_DEFAULT, + .channel_id = 1, // ADC1_IN1 (PA0) +}; static const struct valve_gpios valve_gpios = { .in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in0_gpios), @@ -43,16 +53,29 @@ void valve_init(void) settings_load_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); settings_load_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); - gpio_pin_configure_dt(&valve_gpios.in0, GPIO_OUTPUT_INACTIVE); - gpio_pin_configure_dt(&valve_gpios.in1, GPIO_OUTPUT_INACTIVE); - gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT_ACTIVE); // Keep VND7050AJ out of reset - gpio_pin_configure_dt(&valve_gpios.sen, GPIO_OUTPUT_INACTIVE); + // Initialize ADC for MULTISENSE + if (!device_is_ready(adc_dev)) { + LOG_ERR("ADC device not ready"); + return; + } + + int ret = adc_channel_setup(adc_dev, &adc_channel_cfg); + if (ret < 0) { + LOG_ERR("Could not setup ADC channel (%d)", ret); + return; + } + + gpio_pin_configure_dt(&valve_gpios.in0, GPIO_OUTPUT_INACTIVE); // IN0 control pin - output, deactivate + gpio_pin_configure_dt(&valve_gpios.in1, GPIO_OUTPUT_INACTIVE); // IN1 control pin - output, deactivate + gpio_pin_configure_dt(&valve_gpios.rst, GPIO_OUTPUT_INACTIVE); // Keep VND7050AJ in reset + gpio_pin_configure_dt(&valve_gpios.sen, GPIO_OUTPUT_INACTIVE); // Sensor enable pin - output, inactive + // S0 and S1 pins are used for selecting the valve state, they are initially inactive + // and will be set to active when the valve is opened or closed. gpio_pin_configure_dt(&valve_gpios.s0, GPIO_OUTPUT_INACTIVE); // S0 select pin - output gpio_pin_configure_dt(&valve_gpios.s1, GPIO_OUTPUT_INACTIVE); // S1 select pin - output LOG_INF("Valve initialized: max_open=%us, max_close=%us", max_opening_time_s, max_closing_time_s); } -} void valve_open(void) { @@ -87,6 +110,68 @@ enum valve_state valve_get_state(void) { return current_state; } enum valve_movement valve_get_movement(void) { return current_movement; } uint16_t valve_get_motor_current(void) { return (current_movement != VALVE_MOVEMENT_IDLE) ? 150 : 10; } +uint16_t valve_get_supply_voltage(void) +{ + LOG_DBG("Starting supply voltage measurement"); + + // Ensure VND7050AJ is enabled (RST=HIGH) + LOG_DBG("Enabling VND7050AJ (RST=1)"); + gpio_pin_set_dt(&valve_gpios.rst, 1); + + // Wait for VND7050AJ to power up and stabilize + k_msleep(50); + + int16_t buf; + struct adc_sequence sequence = { + .buffer = &buf, + .buffer_size = sizeof(buf), + .channels = BIT(adc_channel_cfg.channel_id), + .resolution = 12, + }; + + // Configure VND7050AJ to output supply voltage on MULTISENSE + // According to VND7050AJ datasheet page 20: + // S0=1, S1=1: Supply voltage sensing mode + LOG_DBG("Setting S0=1, S1=1 for supply voltage sensing"); + gpio_pin_set_dt(&valve_gpios.s0, 1); + gpio_pin_set_dt(&valve_gpios.s1, 1); + + // Enable sensing + LOG_DBG("Enabling MULTISENSE (SEN=1)"); + gpio_pin_set_dt(&valve_gpios.sen, 1); + + // Wait for voltage to stabilize + k_msleep(10); + + // Read ADC value + LOG_DBG("Reading ADC channel %d", adc_channel_cfg.channel_id); + int ret = adc_read(adc_dev, &sequence); + if (ret < 0) { + LOG_ERR("Could not read ADC (%d)", ret); + gpio_pin_set_dt(&valve_gpios.sen, 0); + return 0; + } + + // Disable sensing to save power + LOG_DBG("Disabling MULTISENSE (SEN=0)"); + gpio_pin_set_dt(&valve_gpios.sen, 0); + + // Convert ADC value to millivolts + // VDD = 3.3V, ADC resolution = 12-bit (4096 steps) + // ADC voltage = (buf / 4096) * 3300 mV + int32_t val_mv = ((int32_t)buf * 3300) / 4096; + + // VND7050AJ MULTISENSE voltage divider: + // According to datasheet page 35, MULTISENSE = VCC / 8 (8:1 voltage divider) + // So actual supply voltage = MULTISENSE * 8 + uint16_t supply_voltage_mv = (uint16_t)(val_mv * 8); + + LOG_INF("Supply voltage: %u mV (ADC raw: %d, ADC mV: %d)", + supply_voltage_mv, buf, (int)val_mv); + + return supply_voltage_mv; +} + void valve_set_max_open_time(uint16_t seconds) { max_opening_time_s = seconds; settings_save_one("valve/max_open_time", &max_opening_time_s, sizeof(max_opening_time_s)); } void valve_set_max_close_time(uint16_t seconds) { max_closing_time_s = seconds; settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); } uint16_t valve_get_max_open_time(void) { return max_opening_time_s; } diff --git a/software/serial_monitor.py b/software/serial_monitor.py new file mode 100644 index 0000000..3692545 --- /dev/null +++ b/software/serial_monitor.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +import serial +import time +import sys + +def monitor_serial(): + try: + # Open serial connection + ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1) + print("Connected to /dev/ttyACM3") + + # Send reset command + ser.write(b'reset\n') + print("Sent reset command") + + # Wait a bit and then read output + time.sleep(0.5) + + # Read output for 10 seconds + start_time = time.time() + while time.time() - start_time < 10: + if ser.in_waiting > 0: + data = ser.read(ser.in_waiting) + try: + text = data.decode('utf-8', errors='ignore') + print(text, end='') + except: + print(f"Raw bytes: {data}") + time.sleep(0.1) + + ser.close() + print("\nSerial monitor closed") + + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + +if __name__ == "__main__": + monitor_serial() diff --git a/software/serial_reset_monitor.py b/software/serial_reset_monitor.py new file mode 100644 index 0000000..e7ad34b --- /dev/null +++ b/software/serial_reset_monitor.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import serial +import time +import sys + +def monitor_serial_with_reset(): + try: + # Open serial port + ser = serial.Serial('/dev/ttyACM3', 115200, timeout=1) + print("Serial port opened successfully") + + # Clear any existing data + ser.flushInput() + ser.flushOutput() + + # Send reset command + print("Sending reset command...") + ser.write(b"reset\n") + time.sleep(0.1) + + # Read output for 10 seconds + print("Reading serial output...") + start_time = time.time() + output_lines = [] + + while time.time() - start_time < 10: + if ser.in_waiting > 0: + try: + line = ser.readline().decode('utf-8', errors='replace').strip() + if line: + print(f"[{time.time() - start_time:.3f}s] {line}") + output_lines.append(line) + except Exception as e: + print(f"Error reading line: {e}") + time.sleep(0.01) + + ser.close() + print("\nSerial monitoring complete") + + # Summary + print("\n=== SUMMARY ===") + supply_voltage_lines = [line for line in output_lines if "Supply voltage" in line] + if supply_voltage_lines: + print("Supply voltage readings:") + for line in supply_voltage_lines: + print(f" {line}") + else: + print("No supply voltage readings found") + + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + +if __name__ == "__main__": + monitor_serial_with_reset()