diff --git a/software/apps/slave_node/boards/weact_stm32g431_core.overlay b/software/apps/slave_node/boards/weact_stm32g431_core.overlay index 723c21e..7b8ec2f 100644 --- a/software/apps/slave_node/boards/weact_stm32g431_core.overlay +++ b/software/apps/slave_node/boards/weact_stm32g431_core.overlay @@ -1,23 +1,17 @@ +#include + / { - /* VND7050AJ Sensor Multiplexer - Centralized configuration */ - vnd7050aj_mux: sensor-multiplexer { - compatible = "vnd7050aj,sensor-mux"; + vnd7050aj: vnd7050aj { + compatible = "vnd7050aj-valve-controller"; status = "okay"; - /* Shared ADC configuration */ - io-channels = <&adc1 1>; /* ADC1 channel 1 (PA0) */ - io-channel-names = "sensor-input"; - reference-mv = <3300>; - - /* VND7050AJ GPIO pin definitions - shared by all sensors */ - sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - Sense Enable */ - s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - Mux select bit 0 */ - s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - Mux select bit 1 */ - - /* Valve control pins (separate from sensor mux) */ - in0-gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>; /* IN0 (PB7) - Valve input 0 */ - in1-gpios = <&gpiob 9 GPIO_ACTIVE_HIGH>; /* IN1 (PB9) - Valve input 1 */ - rst-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>; /* RST (PB3) - Reset pin */ + // VND7050AJ GPIO pin definitions + in0-gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>; // IN0 (PB7) - Input 0 control signal + in1-gpios = <&gpiob 9 GPIO_ACTIVE_HIGH>; // IN1 (PB9) - Input 1 control signal + rst-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>; // RST (PB3) - Reset pin for VND7050AJ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; // SEN (PB4) - Sense Enable for current monitoring + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; // S0 (PB6) - Status/Select 0 output from VND7050AJ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; // S1 (PB5) - Status/Select 1 output from VND7050AJ }; adc_sensors { @@ -25,22 +19,49 @@ supply_voltage: supply-voltage { compatible = "custom,supply-voltage"; - sensor-mux = <&vnd7050aj_mux>; /* Reference to shared mux config */ - - /* Sensor-specific configuration */ + io-channels = <&adc1 1>; /* ADC1 channel 1 (PA0) */ + io-channel-names = "voltage"; + reference-mv = <3300>; voltage-divider-ratio = <4>; /* Adjust based on your voltage divider */ - mux-channel = <0>; /* Channel 0: s1=0, s0=0 */ - measurement-delay-ms = <5>; /* 5ms delay after GPIO setup */ + + /* GPIO control pins using VND7050AJ pins */ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */ + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */ + + measurement-delay-ms = <5>; /* 5ms delay after GPIO setup */ }; - motor_current: motor-current { + motor_current_open: motor-current-open { compatible = "custom,motor-current"; - sensor-mux = <&vnd7050aj_mux>; /* Reference to shared mux config */ + io-channels = <&adc1 1>; /* Same ADC channel, different mux setting */ + io-channel-names = "current"; + reference-mv = <3300>; + current-sense-resistor-mohm = <1500000>; /* 1.5kΩ sense resistor in mΩ */ + k-factor = <10>; /* Current sense amplification factor */ - /* Sensor-specific configuration */ - current-sense-resistor-mohm = <100>; /* 100mΩ sense resistor */ - mux-channel = <1>; /* Channel 1: s1=0, s0=1 */ - measurement-delay-ms = <10>; /* 10ms delay for current settling */ + /* GPIO control pins using VND7050AJ pins */ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */ + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */ + + measurement-delay-ms = <10>; /* 10ms delay for current settling */ + }; + + motor_current_close: motor-current-close { + compatible = "custom,motor-current"; + io-channels = <&adc1 1>; /* Same ADC channel, different mux setting */ + io-channel-names = "current"; + reference-mv = <3300>; + current-sense-resistor-mohm = <1500000>; /* 1.5kΩ sense resistor in mΩ */ + k-factor = <10>; /* Current sense amplification factor */ + + /* GPIO control pins using VND7050AJ pins */ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */ + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */ + + measurement-delay-ms = <10>; /* 10ms delay for current settling */ }; }; }; diff --git a/software/apps/slave_node/cdc-acm.overlay b/software/apps/slave_node/cdc-acm.overlay index 8e7ad41..f12b35b 100644 --- a/software/apps/slave_node/cdc-acm.overlay +++ b/software/apps/slave_node/cdc-acm.overlay @@ -1,3 +1,78 @@ +#include + +/ { + vnd7050aj: vnd7050aj { + compatible = "vnd7050aj-valve-controller"; + status = "okay"; + + // VND7050AJ GPIO pin definitions + in0-gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>; // IN0 (PB7) - Input 0 control signal + in1-gpios = <&gpiob 9 GPIO_ACTIVE_HIGH>; // IN1 (PB9) - Input 1 control signal + rst-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>; // RST (PB3) - Reset pin for VND7050AJ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; // SEN (PB4) - Sense Enable for current monitoring + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; // S0 (PB6) - Status/Select 0 output from VND7050AJ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; // S1 (PB5) - Status/Select 1 output from VND7050AJ + }; + + adc_sensors { + compatible = "adc-sensors"; + + supply_voltage: supply-voltage { + compatible = "custom,supply-voltage"; + io-channels = <&adc1 1>; /* ADC1 channel 1 (PA0) */ + io-channel-names = "voltage"; + reference-mv = <3300>; + voltage-divider-ratio = <4>; /* Adjust based on your voltage divider */ + sensor-mux = <&vnd7050aj>; /* Reference to VND7050AJ mux */ + mux-channel = <3>; /* VCC sense channel */ + measurement-delay-ms = <5>; /* 5ms delay after GPIO setup */ + }; + + motor_current_open: motor-current-open { + compatible = "custom,motor-current"; + io-channels = <&adc1 1>; /* Same ADC channel, different mux setting */ + io-channel-names = "current"; + reference-mv = <3300>; + current-sense-resistor-mohm = <1500000>; /* 1.5kΩ sense resistor in mΩ */ + k-factor = <10>; /* Current sense amplification factor */ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */ + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */ + measurement-delay-ms = <10>; /* 10ms delay for current settling */ + }; + + motor_current_close: motor-current-close { + compatible = "custom,motor-current"; + io-channels = <&adc1 1>; /* Same ADC channel, different mux setting */ + io-channel-names = "current"; + reference-mv = <3300>; + current-sense-resistor-mohm = <1500000>; /* 1.5kΩ sense resistor in mΩ */ + k-factor = <10>; /* Current sense amplification factor */ + sen-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; /* SEN (PB4) - enable sensor */ + s0-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; /* S0 (PB6) - mux select bit 0 */ + s1-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; /* S1 (PB5) - mux select bit 1 */ + measurement-delay-ms = <10>; /* 10ms delay for current settling */ + }; + }; +}; + +&adc1 { // ADC1 wird für PA0 verwendet + status = "okay"; // ADC1 aktivieren + pinctrl-0 = <&adc1_in1_pa0>; // Pinmux für PA0 als ADC1_IN1 + pinctrl-names = "default"; + st,adc-clock-source = "SYNC"; + st,adc-prescaler = <4>; + #address-cells = <1>; + #size-cells = <0>; +}; + +&pinctrl { + // Pinmux für PA0 als ADC1_IN1 (Analogmodus) + adc1_in1_pa0: adc1_in1_pa0 { + pinmux = ; // PA0 in den Analogmodus setzen + }; +}; + &zephyr_udc0 { cdc_acm_uart0: cdc_acm_uart0 { compatible = "zephyr,cdc-acm-uart"; diff --git a/software/apps/slave_node/dts/bindings/adc/custom,motor-current.yaml b/software/apps/slave_node/dts/bindings/adc/custom,motor-current.yaml index 38fd6a5..aae3106 100644 --- a/software/apps/slave_node/dts/bindings/adc/custom,motor-current.yaml +++ b/software/apps/slave_node/dts/bindings/adc/custom,motor-current.yaml @@ -4,29 +4,48 @@ description: Motor current sensor using VND7050AJ multiplexer compatible: "custom,motor-current" properties: - sensor-mux: - type: phandle + io-channels: + type: phandle-array required: true - description: Reference to the VND7050AJ sensor multiplexer node + description: ADC channel phandle and specifier + + io-channel-names: + type: string-array + required: true + description: Names for the ADC channels + + reference-mv: + type: int + required: true + description: ADC reference voltage in millivolts current-sense-resistor-mohm: type: int required: true description: Current sense resistor value in milliohms - amplifier-gain: - type: int - required: false - default: 1 - description: Current sense amplifier gain - - mux-channel: + k-factor: type: int required: true - description: Multiplexer channel number (0-3) for this sensor + description: Current sense amplification factor for VND7050AJ + + sen-gpios: + type: phandle-array + required: true + description: GPIO for sensor enable (SEN pin) + + s0-gpios: + type: phandle-array + required: true + description: GPIO for multiplexer select bit 0 (S0 pin) + + s1-gpios: + type: phandle-array + required: true + description: GPIO for multiplexer select bit 1 (S1 pin) measurement-delay-ms: type: int required: false default: 10 - description: Delay in milliseconds after GPIO setup before measurement + description: Delay in milliseconds after setting GPIO pins before reading ADC \ No newline at end of file diff --git a/software/apps/slave_node/dts/bindings/adc/custom,supply-voltage.yaml b/software/apps/slave_node/dts/bindings/adc/custom,supply-voltage.yaml index 08cbf34..de4023f 100644 --- a/software/apps/slave_node/dts/bindings/adc/custom,supply-voltage.yaml +++ b/software/apps/slave_node/dts/bindings/adc/custom,supply-voltage.yaml @@ -4,6 +4,21 @@ description: Supply voltage sensor using VND7050AJ multiplexer compatible: "custom,supply-voltage" properties: + io-channels: + type: phandle-array + required: true + description: ADC channel phandle and specifier + + io-channel-names: + type: string-array + required: true + description: Names for the ADC channels + + reference-mv: + type: int + required: true + description: ADC reference voltage in millivolts + sensor-mux: type: phandle required: true diff --git a/software/apps/slave_node/dts/bindings/adc/vnd7050aj,sensor-mux.yaml b/software/apps/slave_node/dts/bindings/adc/vnd7050aj,sensor-mux.yaml index de2bb3f..e32b489 100644 --- a/software/apps/slave_node/dts/bindings/adc/vnd7050aj,sensor-mux.yaml +++ b/software/apps/slave_node/dts/bindings/adc/vnd7050aj,sensor-mux.yaml @@ -48,3 +48,13 @@ properties: type: phandle-array required: false description: GPIO for reset control (RST pin) + + sense-resistor-ohm: + type: int + required: true + description: Current sense resistor value in ohms + + k-factor: + type: int + required: true + description: Current sense ratio for VND7050AJ (typical ~1200:1) diff --git a/software/include/lib/adc_sensor.h b/software/include/lib/adc_sensor.h index 67e895b..ca86382 100644 --- a/software/include/lib/adc_sensor.h +++ b/software/include/lib/adc_sensor.h @@ -34,13 +34,34 @@ int adc_sensor_init(void); uint16_t adc_sensor_get_voltage_mv(void); /** - * @brief Gets the current motor current reading. + * @brief Gets the current motor current reading (legacy function). * - * This function reads the value from the motor driver's sense pin via ADC - * and converts it to milliamps. This is used for end-stop detection. + * This function reads the current from the opening channel (IN0). + * For backward compatibility only. Use adc_sensor_get_current_open_ma() + * instead. * * @return The motor current in milliamps (mA). */ uint16_t adc_sensor_get_current_ma(void); +/** + * @brief Gets the motor opening current reading (IN0 current sense). + * + * This function reads the current from the VND7050AJ IN0 current sense channel. + * Used for monitoring valve opening current. + * + * @return The motor opening current in milliamps (mA). + */ +uint16_t adc_sensor_get_current_open_ma(void); + +/** + * @brief Gets the motor closing current reading (IN1 current sense). + * + * This function reads the current from the VND7050AJ IN1 current sense channel. + * Used for monitoring valve closing current. + * + * @return The motor closing current in milliamps (mA). + */ +uint16_t adc_sensor_get_current_close_ma(void); + #endif /* ADC_SENSOR_H */ diff --git a/software/include/lib/modbus_server.h b/software/include/lib/modbus_server.h index f20016c..b6a8034 100644 --- a/software/include/lib/modbus_server.h +++ b/software/include/lib/modbus_server.h @@ -23,9 +23,13 @@ enum { */ REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000, /** - * @brief Aktueller Motorstrom in Milliampere (mA). + * @brief Motorstrom beim Öffnen in Milliampere (mA) - IN0 current sense. */ - REG_INPUT_MOTOR_CURRENT_MA = 0x0001, + REG_INPUT_MOTOR_OPEN_CURRENT_MA = 0x0001, + /** + * @brief Motorstrom beim Schließen in Milliampere (mA) - IN1 current sense. + */ + REG_INPUT_MOTOR_CLOSE_CURRENT_MA = 0x0002, /** * @brief Bitmaske der digitalen Eingänge. Bit 0: Eingang 1, Bit 1: Eingang 2. * 1=Aktiv. diff --git a/software/lib/adc_sensor/adc_sensor.c b/software/lib/adc_sensor/adc_sensor.c index 705be2b..c0de140 100644 --- a/software/lib/adc_sensor/adc_sensor.c +++ b/software/lib/adc_sensor/adc_sensor.c @@ -22,30 +22,40 @@ LOG_MODULE_REGISTER(adc_sensor, LOG_LEVEL_INF); // Devicetree node checks #define VOLTAGE_SENSOR_NODE DT_NODELABEL(supply_voltage) -#define CURRENT_SENSOR_NODE DT_NODELABEL(motor_current) -#define SENSOR_MUX_NODE DT_NODELABEL(vnd7050aj_mux) +#define CURRENT_OPEN_SENSOR_NODE DT_NODELABEL(motor_current_open) +#define CURRENT_CLOSE_SENSOR_NODE DT_NODELABEL(motor_current_close) +#define VND7050AJ_NODE DT_NODELABEL(vnd7050aj) #ifndef CONFIG_ADC_SENSOR_SIMULATED -// ADC device reference from centralized mux node -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) -#define ADC_NODE DT_PHANDLE(SENSOR_MUX_NODE, io_channels) -#define ADC_CHANNEL DT_PHA(SENSOR_MUX_NODE, io_channels, input) -#define ADC_RESOLUTION 12 -#define ADC_REFERENCE_MV DT_PROP(SENSOR_MUX_NODE, reference_mv) +// ADC device reference from voltage sensor node (all sensors use same ADC) +#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) +#define ADC_NODE DT_PHANDLE(VOLTAGE_SENSOR_NODE, io_channels) +#define ADC_REFERENCE_MV DT_PROP(VOLTAGE_SENSOR_NODE, reference_mv) +#endif + +#define ADC_CHANNEL 1 /* ADC1 channel 1 as defined in overlay */ // Sensor-specific properties #if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) #define VOLTAGE_DIVIDER_RATIO \ DT_PROP(VOLTAGE_SENSOR_NODE, voltage_divider_ratio) -#define VOLTAGE_MUX_CHANNEL DT_PROP(VOLTAGE_SENSOR_NODE, mux_channel) #define VOLTAGE_DELAY_MS DT_PROP(VOLTAGE_SENSOR_NODE, measurement_delay_ms) #endif -#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE) -#define CURRENT_SENSE_RESISTOR_MOHM \ - DT_PROP(CURRENT_SENSOR_NODE, current_sense_resistor_mohm) -#define CURRENT_MUX_CHANNEL DT_PROP(CURRENT_SENSOR_NODE, mux_channel) -#define CURRENT_DELAY_MS DT_PROP(CURRENT_SENSOR_NODE, measurement_delay_ms) +#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE) +#define CURRENT_OPEN_SENSE_RESISTOR_MOHM \ + DT_PROP(CURRENT_OPEN_SENSOR_NODE, current_sense_resistor_mohm) +#define CURRENT_OPEN_K_FACTOR DT_PROP(CURRENT_OPEN_SENSOR_NODE, k_factor) +#define CURRENT_OPEN_DELAY_MS \ + DT_PROP(CURRENT_OPEN_SENSOR_NODE, measurement_delay_ms) +#endif + +#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE) +#define CURRENT_CLOSE_SENSE_RESISTOR_MOHM \ + DT_PROP(CURRENT_CLOSE_SENSOR_NODE, current_sense_resistor_mohm) +#define CURRENT_CLOSE_K_FACTOR DT_PROP(CURRENT_CLOSE_SENSOR_NODE, k_factor) +#define CURRENT_CLOSE_DELAY_MS \ + DT_PROP(CURRENT_CLOSE_SENSOR_NODE, measurement_delay_ms) #endif static const struct device *adc_dev; @@ -59,24 +69,23 @@ static struct adc_channel_cfg adc_channel_cfg = { static struct adc_sequence adc_sequence = { .channels = BIT(ADC_CHANNEL), .buffer_size = sizeof(uint16_t), - .resolution = ADC_RESOLUTION, + .resolution = 12, }; static uint16_t adc_buffer; #endif -#endif static bool initialized = false; #ifndef CONFIG_ADC_SENSOR_SIMULATED -// GPIO specs from centralized mux node -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) +// GPIO specs from VND7050AJ node +#if DT_NODE_EXISTS(VND7050AJ_NODE) static const struct gpio_dt_spec sen_gpio = - GPIO_DT_SPEC_GET(SENSOR_MUX_NODE, sen_gpios); + GPIO_DT_SPEC_GET(VND7050AJ_NODE, sen_gpios); static const struct gpio_dt_spec s0_gpio = - GPIO_DT_SPEC_GET(SENSOR_MUX_NODE, s0_gpios); + GPIO_DT_SPEC_GET(VND7050AJ_NODE, s0_gpios); static const struct gpio_dt_spec s1_gpio = - GPIO_DT_SPEC_GET(SENSOR_MUX_NODE, s1_gpios); + GPIO_DT_SPEC_GET(VND7050AJ_NODE, s1_gpios); #endif /** @@ -85,7 +94,7 @@ static const struct gpio_dt_spec s1_gpio = static int configure_sensor_gpios(void) { int ret = 0; -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) +#if DT_NODE_EXISTS(VND7050AJ_NODE) // Configure sensor multiplexer GPIOs if (gpio_is_ready_dt(&sen_gpio)) { ret = gpio_pin_configure_dt(&sen_gpio, GPIO_OUTPUT_INACTIVE); @@ -122,7 +131,7 @@ static int configure_sensor_gpios(void) { * @param delay_ms Delay after setting GPIOs */ static int set_mux_channel(bool enable, uint8_t channel, uint32_t delay_ms) { -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) +#if DT_NODE_EXISTS(VND7050AJ_NODE) if (gpio_is_ready_dt(&sen_gpio)) { gpio_pin_set_dt(&sen_gpio, enable ? 1 : 0); } @@ -148,7 +157,7 @@ static int set_mux_channel(bool enable, uint8_t channel, uint32_t delay_ms) { * @return ADC reading in millivolts, or 0 on error */ static uint16_t read_adc_voltage_mv(void) { -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) && DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) +#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) int ret = adc_read(adc_dev, &adc_sequence); if (ret < 0) { LOG_ERR("ADC read failed: %d", ret); @@ -172,11 +181,11 @@ static uint16_t read_adc_voltage_mv(void) { } /** - * @brief Read ADC value and convert to milliamps (for current sensor) + * @brief Read ADC value and convert to milliamps for opening current * @return ADC reading in milliamps, or 0 on error */ -static uint16_t read_adc_current_ma(void) { -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) && DT_NODE_EXISTS(CURRENT_SENSOR_NODE) +static uint16_t read_adc_current_open_ma(void) { +#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE) int ret = adc_read(adc_dev, &adc_sequence); if (ret < 0) { LOG_ERR("ADC read failed: %d", ret); @@ -187,12 +196,45 @@ static uint16_t read_adc_current_ma(void) { uint32_t adc_value = adc_buffer; uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095; - // Convert voltage to current based on sense resistor - // I = V / R, where R is in milliohms and V is in millivolts + // VND7050AJ current calculation: I = V_sense * K / R_sense + // Where: V_sense in mV, K is the current sense factor, R_sense in mΩ // Result is in milliamps - uint32_t current_ma = (voltage_mv * 1000) / CURRENT_SENSE_RESISTOR_MOHM; + uint32_t current_ma = (voltage_mv * CURRENT_OPEN_K_FACTOR * 1000) / + CURRENT_OPEN_SENSE_RESISTOR_MOHM; - LOG_DBG("ADC raw: %u, current: %u mA", adc_value, (uint16_t)current_ma); + LOG_DBG("Open current - ADC raw: %u, voltage: %u mV, current: %u mA", + adc_value, voltage_mv, (uint16_t)current_ma); + + return (uint16_t)current_ma; +#else + return 0; +#endif +} + +/** + * @brief Read ADC value and convert to milliamps for closing current + * @return ADC reading in milliamps, or 0 on error + */ +static uint16_t read_adc_current_close_ma(void) { +#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE) + int ret = adc_read(adc_dev, &adc_sequence); + if (ret < 0) { + LOG_ERR("ADC read failed: %d", ret); + return 0; + } + + // Convert ADC reading to millivolts first + uint32_t adc_value = adc_buffer; + uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095; + + // VND7050AJ current calculation: I = V_sense * K / R_sense + // Where: V_sense in mV, K is the current sense factor, R_sense in mΩ + // Result is in milliamps + uint32_t current_ma = (voltage_mv * CURRENT_CLOSE_K_FACTOR * 1000) / + CURRENT_CLOSE_SENSE_RESISTOR_MOHM; + + LOG_DBG("Close current - ADC raw: %u, voltage: %u mV, current: %u mA", + adc_value, voltage_mv, (uint16_t)current_ma); return (uint16_t)current_ma; #else @@ -219,7 +261,7 @@ int adc_sensor_init(void) { } // Initialize ADC hardware -#if DT_NODE_EXISTS(SENSOR_MUX_NODE) +#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) adc_dev = DEVICE_DT_GET(ADC_NODE); if (!device_is_ready(adc_dev)) { LOG_ERR("ADC device not ready"); @@ -237,15 +279,18 @@ int adc_sensor_init(void) { LOG_INF("ADC device ready: %s", adc_dev->name); #endif - LOG_INF("ADC sensor initialized (real ADC mode with centralized mux)"); + LOG_INF("ADC sensor initialized (real ADC mode)"); #if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) - LOG_INF("Voltage sensor: channel %d, divider ratio %d", VOLTAGE_MUX_CHANNEL, - VOLTAGE_DIVIDER_RATIO); + LOG_INF("Voltage sensor: divider ratio %d", VOLTAGE_DIVIDER_RATIO); #endif -#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE) - LOG_INF("Current sensor: channel %d, sense resistor %d mOhm", - CURRENT_MUX_CHANNEL, CURRENT_SENSE_RESISTOR_MOHM); +#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE) + LOG_INF("Open current sensor: K-factor %d, sense resistor %d mΩ", + CURRENT_OPEN_K_FACTOR, CURRENT_OPEN_SENSE_RESISTOR_MOHM); +#endif +#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE) + LOG_INF("Close current sensor: K-factor %d, sense resistor %d mΩ", + CURRENT_CLOSE_K_FACTOR, CURRENT_CLOSE_SENSE_RESISTOR_MOHM); #endif #endif @@ -262,9 +307,9 @@ uint16_t adc_sensor_get_voltage_mv(void) { #ifdef CONFIG_ADC_SENSOR_SIMULATED return SIMULATED_VOLTAGE_MV; #else - // Set multiplexer to voltage channel + // Set multiplexer to voltage channel (channel 3: VCC sense) #if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) - set_mux_channel(true, VOLTAGE_MUX_CHANNEL, VOLTAGE_DELAY_MS); + set_mux_channel(true, 3, VOLTAGE_DELAY_MS); // Read real ADC value for voltage uint16_t voltage = read_adc_voltage_mv(); @@ -280,6 +325,11 @@ uint16_t adc_sensor_get_voltage_mv(void) { } uint16_t adc_sensor_get_current_ma(void) { + // Legacy function - redirect to opening current for backward compatibility + return adc_sensor_get_current_open_ma(); +} + +uint16_t adc_sensor_get_current_open_ma(void) { if (!initialized) { LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()"); adc_sensor_init(); @@ -288,12 +338,38 @@ uint16_t adc_sensor_get_current_ma(void) { #ifdef CONFIG_ADC_SENSOR_SIMULATED return SIMULATED_CURRENT_MA; #else - // Set multiplexer to current channel -#if DT_NODE_EXISTS(CURRENT_SENSOR_NODE) - set_mux_channel(true, CURRENT_MUX_CHANNEL, CURRENT_DELAY_MS); + // Set multiplexer to IN0 current sense channel (channel 0) +#if DT_NODE_EXISTS(CURRENT_OPEN_SENSOR_NODE) + set_mux_channel(true, 0, CURRENT_OPEN_DELAY_MS); // Read real ADC value for current - uint16_t current = read_adc_current_ma(); + uint16_t current = read_adc_current_open_ma(); + + // Disable sensor after measurement to save power + set_mux_channel(false, 0, 0); + + return current; +#else + return 0; +#endif +#endif +} + +uint16_t adc_sensor_get_current_close_ma(void) { + if (!initialized) { + LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()"); + adc_sensor_init(); + } + +#ifdef CONFIG_ADC_SENSOR_SIMULATED + return SIMULATED_CURRENT_MA; +#else + // Set multiplexer to IN1 current sense channel (channel 1) +#if DT_NODE_EXISTS(CURRENT_CLOSE_SENSOR_NODE) + set_mux_channel(true, 1, CURRENT_CLOSE_DELAY_MS); + + // Read real ADC value for current + uint16_t current = read_adc_current_close_ma(); // Disable sensor after measurement to save power set_mux_channel(false, 0, 0); diff --git a/software/lib/modbus_server/modbus_server.c b/software/lib/modbus_server/modbus_server.c index ab5e3f4..4458623 100644 --- a/software/lib/modbus_server/modbus_server.c +++ b/software/lib/modbus_server/modbus_server.c @@ -147,8 +147,11 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg) { case REG_INPUT_VALVE_STATE_MOVEMENT: *reg = (valve_get_movement() << 8) | (valve_get_state() & 0xFF); break; - case REG_INPUT_MOTOR_CURRENT_MA: - *reg = adc_sensor_get_current_ma(); + case REG_INPUT_MOTOR_OPEN_CURRENT_MA: + *reg = adc_sensor_get_current_open_ma(); + break; + case REG_INPUT_MOTOR_CLOSE_CURRENT_MA: + *reg = adc_sensor_get_current_close_ma(); break; case REG_INPUT_UPTIME_SECONDS_LOW: *reg = (uint16_t)(uptime_s & 0xFFFF); diff --git a/software/lib/valve/valve.c b/software/lib/valve/valve.c index 7425198..6ba8c51 100644 --- a/software/lib/valve/valve.c +++ b/software/lib/valve/valve.c @@ -17,12 +17,12 @@ LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF); static const struct valve_gpios valve_gpios = { - .in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj_mux), in0_gpios), - .in1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj_mux), in1_gpios), - .rst = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj_mux), rst_gpios), - .sen = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj_mux), sen_gpios), - .s0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj_mux), s0_gpios), - .s1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj_mux), s1_gpios), + .in0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in0_gpios), + .in1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), in1_gpios), + .rst = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), rst_gpios), + .sen = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), sen_gpios), + .s0 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), s0_gpios), + .s1 = GPIO_DT_SPEC_GET(DT_NODELABEL(vnd7050aj), s1_gpios), }; static enum valve_state current_state = VALVE_STATE_CLOSED; @@ -78,8 +78,8 @@ void valve_init(void) { void valve_open(void) { if (current_state == VALVE_STATE_CLOSED) { gpio_pin_set_dt(&valve_gpios.rst, 1); - gpio_pin_set_dt(&valve_gpios.in1, 0); gpio_pin_set_dt(&valve_gpios.in0, 1); + gpio_pin_set_dt(&valve_gpios.in1, 0); current_state = VALVE_STATE_OPEN; current_movement = VALVE_MOVEMENT_OPENING; k_work_schedule(&valve_work, K_MSEC(max_opening_time_s * 1000 * 0.9)); diff --git a/software/tools/modbus_tool/modbus_tool.py b/software/tools/modbus_tool/modbus_tool.py index a05402b..bc20a5b 100755 --- a/software/tools/modbus_tool/modbus_tool.py +++ b/software/tools/modbus_tool/modbus_tool.py @@ -11,7 +11,8 @@ from pymodbus.exceptions import ModbusException # --- Register Definitions --- # (omitted for brevity, no changes here) REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000 -REG_INPUT_MOTOR_CURRENT_MA = 0x0001 +REG_INPUT_MOTOR_OPEN_CURRENT_MA = 0x0001 +REG_INPUT_MOTOR_CLOSE_CURRENT_MA = 0x0002 REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020 REG_INPUT_BUTTON_EVENTS = 0x0021 REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0 @@ -83,14 +84,15 @@ def poll_status(slave_id, interval): continue # If connected, try to read data - ir_valve = client.read_input_registers(REG_INPUT_VALVE_STATE_MOVEMENT, count=2, slave=slave_id) + ir_valve = client.read_input_registers(REG_INPUT_VALVE_STATE_MOVEMENT, count=1, slave=slave_id) + ir_current = client.read_input_registers(REG_INPUT_MOTOR_OPEN_CURRENT_MA, count=2, slave=slave_id) ir_dig = client.read_input_registers(REG_INPUT_DIGITAL_INPUTS_STATE, count=2, slave=slave_id) ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=6, slave=slave_id) hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=2, slave=slave_id) hr_dig = client.read_holding_registers(REG_HOLDING_DIGITAL_OUTPUTS_STATE, count=1, slave=slave_id) hr_sys = client.read_holding_registers(REG_HOLDING_WATCHDOG_TIMEOUT_S, count=1, slave=slave_id) - for res in [ir_valve, ir_dig, ir_sys, hr_valve, hr_dig, hr_sys]: + for res in [ir_valve, ir_current, ir_dig, ir_sys, hr_valve, hr_dig, hr_sys]: if res.isError(): raise ModbusException(str(res)) @@ -99,7 +101,8 @@ def poll_status(slave_id, interval): state_map = {0: "Closed", 1: "Open"} new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown') new_data["state"] = state_map.get(valve_state_raw & 0xFF, 'Unknown') - new_data["motor_current"] = f"{ir_valve.registers[1]} mA" + new_data["motor_current_open"] = f"{ir_current.registers[0]} mA" + new_data["motor_current_close"] = f"{ir_current.registers[1]} mA" new_data["open_time"] = f"{hr_valve.registers[0]}s" new_data["close_time"] = f"{hr_valve.registers[1]}s" new_data["digital_inputs"] = f"0x{ir_dig.registers[0]:04X}" @@ -295,7 +298,8 @@ def main_menu(stdscr, slave_id): col1, col2, col3, col4 = 2, 30, 58, 88 stdscr.addstr(1, col1, "State:", bold); stdscr.addstr(1, col1 + 18, str(current_data.get('state', 'N/A')), normal) stdscr.addstr(2, col1, "Movement:", bold); stdscr.addstr(2, col1 + 18, str(current_data.get('movement', 'N/A')), normal) - stdscr.addstr(3, col1, "Motor Current:", bold); stdscr.addstr(3, col1 + 18, str(current_data.get('motor_current', 'N/A')), normal) + stdscr.addstr(3, col1, "Open Current:", bold); stdscr.addstr(3, col1 + 18, str(current_data.get('motor_current_open', 'N/A')), normal) + stdscr.addstr(4, col1, "Close Current:", bold); stdscr.addstr(4, col1 + 18, str(current_data.get('motor_current_close', 'N/A')), normal) stdscr.addstr(1, col2, "Digital Inputs:", bold); stdscr.addstr(1, col2 + 18, str(current_data.get('digital_inputs', 'N/A')), normal) stdscr.addstr(2, col2, "Digital Outputs:", bold); stdscr.addstr(2, col2 + 18, str(current_data.get('digital_outputs', 'N/A')), normal) stdscr.addstr(3, col2, "Button Events:", bold); stdscr.addstr(3, col2 + 18, str(current_data.get('button_events', 'N/A')), normal) @@ -306,7 +310,7 @@ def main_menu(stdscr, slave_id): stdscr.addstr(2, col4, "Uptime:", bold); stdscr.addstr(2, col4 + 14, str(current_data.get('uptime', 'N/A')), normal) stdscr.addstr(3, col4, "Dev. Status:", bold); stdscr.addstr(3, col4 + 14, str(current_data.get('device_status', 'N/A')), normal) stdscr.addstr(4, col4, "Supply V:", bold); stdscr.addstr(4, col4 + 14, str(current_data.get('supply_voltage', 'N/A')), normal) - stdscr.addstr(5, 0, "─" * (w - 1), normal) + stdscr.addstr(6, 0, "─" * (w - 1), normal) for idx, row in enumerate(menu): draw_button(stdscr, h // 2 - len(menu) + (idx * 2), w // 2 - len(row) // 2, row, idx == current_row_idx) if time.time() - message_time < 2.0: stdscr.addstr(h - 2, 0, message.ljust(w - 1), curses.color_pair(1) | curses.A_BOLD)