From 8ab2f4c1ce06fdb5ee1a3bd803d99ee76baa35c8 Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Thu, 10 Jul 2025 21:11:20 +0200 Subject: [PATCH] docs: Update German documentation and project plan - Updated Doxygen comments in header files (valve.h, fwu.h, modbus_server.h) to be consistent and in English. - Translated German register names in docs/modbus-registers.de.md to English. - Updated docs/concept.de.md to reflect new details on current measurement and sensors. - Updated docs/planning.de.md to reflect completed tasks in Phase 1. --- docs/concept.de.md | 2 +- docs/modbus-registers.de.md | 29 +- docs/planning.de.md | 8 +- .../boards/weact_stm32g431_core.overlay | 135 ++----- software/apps/slave_node/cdc-acm.overlay | 73 ---- .../bindings/adc/custom,motor-current.yaml | 51 --- .../bindings/adc/custom,supply-voltage.yaml | 41 -- .../bindings/adc/vnd7050aj,sensor-mux.yaml | 60 --- .../bindings/vnd7050aj-valve-controller.yaml | 35 -- software/apps/slave_node/prj.conf | 6 +- software/apps/slave_node/src/main.c | 10 +- software/include/lib/adc_sensor.h | 67 --- software/include/lib/modbus_server.h | 66 +-- software/include/lib/valve.h | 48 ++- software/lib/CMakeLists.txt | 1 - software/lib/Kconfig | 1 - software/lib/adc_sensor/CMakeLists.txt | 1 - software/lib/adc_sensor/Kconfig | 16 - software/lib/adc_sensor/adc_sensor.c | 382 ------------------ software/lib/modbus_server/modbus_server.c | 15 +- software/lib/valve/valve.c | 129 ++++-- 21 files changed, 219 insertions(+), 957 deletions(-) delete mode 100644 software/apps/slave_node/dts/bindings/adc/custom,motor-current.yaml delete mode 100644 software/apps/slave_node/dts/bindings/adc/custom,supply-voltage.yaml delete mode 100644 software/apps/slave_node/dts/bindings/adc/vnd7050aj,sensor-mux.yaml delete mode 100644 software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml delete mode 100644 software/include/lib/adc_sensor.h delete mode 100644 software/lib/adc_sensor/CMakeLists.txt delete mode 100644 software/lib/adc_sensor/Kconfig delete mode 100644 software/lib/adc_sensor/adc_sensor.c diff --git a/docs/concept.de.md b/docs/concept.de.md index 8aceafb..4735f8d 100644 --- a/docs/concept.de.md +++ b/docs/concept.de.md @@ -39,7 +39,7 @@ Die Slave-Nodes sind die Arbeitseinheiten im Feld. Um bei der Fertigung kleiner * **Mikrocontroller:** Ein `STM32G431PB`. Dieser ist zwar leistungsstark, bietet aber alle nötigen Peripherien (mehrere UARTs, ADCs, CAN) und ermöglicht ein einheitliches Hardware- und Software-Design. * **Peripherie pro Node:** - * **Zwei High-Side Ausgänge (+12V):** Realisiert über einen `VND7050AJ`. Perfekt zur Ansteuerung der 12V-Motorventile (`Öffnen`/`Schliessen`). Die `Sense`-Leitung des Treibers wird über einen AD-Wandler ausgelesen, um durch Messung des Motorstroms eine Endlagen-Erkennung ohne physische Endschalter zu realisieren (Motorstrom im Stillstand ≈ 0). + * **Zwei High-Side Ausgänge (+12V):** Realisiert über einen `VND7050AJ`. Perfekt zur Ansteuerung der 12V-Motorventile (`Öffnen`/`Schliessen`). Die `Sense`-Leitung des Treibers wird über einen AD-Wandler ausgelesen, um durch Messung des Motorstroms eine Endlagen-Erkennung ohne physische Endschalter zu realisieren (Motorstrom im Stillstand ≈ 0). Zusätzlich können die Temperatur und die Versorgungsspannung des Treibers ausgelesen werden. * **Zwei Low-Side Ausgänge (0V):** Über N-Kanal-MOSFETs geschaltete Ausgänge. Nutzbar zur Ansteuerung von 12V-LEDs in Tastern oder zum Schalten des Halbleiter-Relais für die Pumpe. * **Zwei digitale Eingänge:** Direkte, geschützte Eingänge am Controller zum Anschluss von Tastern oder den kapazitiven NPN-Sensoren. diff --git a/docs/modbus-registers.de.md b/docs/modbus-registers.de.md index 6ba2308..71426a9 100644 --- a/docs/modbus-registers.de.md +++ b/docs/modbus-registers.de.md @@ -29,10 +29,11 @@ Alle Register sind in einer einzigen, durchgehenden Liste pro Register-Typ (`Inp | Adresse (hex) | Name | Zugehörigkeit | Beschreibung | | :------------ | :----------------------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | -| **0x0000** | `VENTIL_ZUSTAND_BEWEGUNG` | Ventil | Kombiniertes Status-Register. **High-Byte**: Bewegung (`0`=Idle, `1`=Öffnet, `2`=Schliesst, `3`=Fehler). **Low-Byte**: Zustand (`0`=Geschlossen, `1`=Geöffnet). | -| **0x0001** | `MOTORSTROM_MA` | Ventil | Aktueller Motorstrom in Milliampere (mA). | -| **0x0020** | `DIGITAL_EINGAENGE_ZUSTAND` | Eingänge | Bitmaske der digitalen Eingänge. Bit 0: Eingang 1, Bit 1: Eingang 2. `1`=Aktiv. | -| **0x0021** | `TASTER_EVENTS` | Eingänge | Event-Flags für Taster (Clear-on-Read). Bit 0: Taster 1 gedrückt. Bit 1: Taster 2 gedrückt. | +| **0x0000** | `VALVE_STATE_MOVEMENT` | Ventil | Kombiniertes Status-Register. **High-Byte**: Bewegung (`0`=Idle, `1`=Öffnet, `2`=Schliesst, `3`=Fehler). **Low-Byte**: Zustand (`0`=Geschlossen, `1`=Geöffnet). | +| **0x0001** | `MOTORSTROM_OPEN_MA` | Ventil | Motorstrom beim Öffnen in Milliampere (mA). | +| **0x0002** | `MOTORSTROM_CLOSE_MA` | Ventil | Motorstrom beim Schließen in Milliampere (mA). | +| **0x0020** | `DIGITAL_INPUTS_STATE` | Eingänge | Bitmaske der digitalen Eingänge. Bit 0: Eingang 1, Bit 1: Eingang 2. `1`=Aktiv. | +| **0x0021** | `BUTTON_EVENTS` | Eingänge | Event-Flags für Taster (Clear-on-Read). Bit 0: Taster 1 gedrückt. Bit 1: Taster 2 gedrückt. | | **0x00F0** | `FIRMWARE_VERSION_MAJOR_MINOR` | System | z.B. `0x0102` für v1.2. | | **0x00F1** | `FIRMWARE_VERSION_PATCH` | System | z.B. `3` für v1.2.3. | | **0x00F2** | `DEVICE_STATUS` | System | `0`=OK, `1`=Allgemeiner Fehler. | @@ -45,10 +46,10 @@ Alle Register sind in einer einzigen, durchgehenden Liste pro Register-Typ (`Inp | Adresse (hex) | Name | Zugehörigkeit | Beschreibung | | :------------ | :---------------------------- | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | -| **0x0000** | `VENTIL_BEFEHL` | Ventil | `1`=Öffnen, `2`=Schliessen, `0`=Bewegung stoppen. | -| **0x0001** | `MAX_OEFFNUNGSZEIT_S` | Ventil | Sicherheits-Timeout in Sekunden für den Öffnen-Vorgang. | -| **0x0002** | `MAX_SCHLIESSZEIT_S` | Ventil | Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. | -| **0x0010** | `DIGITAL_AUSGAENGE_ZUSTAND` | Ausgänge | Bitmaske zum Lesen und Schreiben der Ausgänge. Bit 0: Ausgang 1, Bit 1: Ausgang 2. `1`=AN, `0`=AUS. | +| **0x0000** | `VALVE_COMMAND` | Ventil | `1`=Öffnen, `2`=Schliessen, `0`=Bewegung stoppen. | +| **0x0001** | `MAX_OPENING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Öffnen-Vorgang. | +| **0x0002** | `MAX_CLOSING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. | +| **0x0010** | `DIGITAL_OUTPUTS_STATE` | Ausgänge | Bitmaske zum Lesen und Schreiben der Ausgänge. Bit 0: Ausgang 1, Bit 1: Ausgang 2. `1`=AN, `0`=AUS. | | **0x00F0** | `WATCHDOG_TIMEOUT_S` | System | Timeout des Fail-Safe-Watchdogs in Sekunden. `0`=Deaktiviert. | | **0x00F1** | `DEVICE_RESET` | System | Schreibt `1` um das Gerät neu zu starten. | | **0x0100** | `FWU_COMMAND` | Firmware-Update | `1`: **Verify Chunk**: Der zuletzt übertragene Chunk wurde vom Client als gültig befunden. Der Slave soll ihn nun ins Flash schreiben. `2`: **Finalize Update**: Alle Chunks sind übertragen. Installation abschliessen und neu starten. | @@ -80,10 +81,10 @@ Diese Register gehören zum externen Füllstandsensor und können auf dem Bus eb | Adresse (hex) | Name | R/W | Beschreibung | | :------------ | :------------------------- | :-- | :---------------------------------------------------------------------------------------------------------------------------------------- | -| **0x0000** | `NODE_ADRESSE` | R/W | Geräteadresse des Sensors (1-255). | +| **0x0000** | `NODE_ADDRESS` | R/W | Geräteadresse des Sensors (1-255). | | **0x0001** | `BAUDRATE` | R/W | `0`=1200, `1`=2400, `2`=4800, `3`=9600, `4`=19200, `5`=38400, `6`=57600, `7`=115200. | -| **0x0002** | `EINHEIT` | R/W | `0`=Keine, `1`=cm, `2`=mm, `3`=MPa, `4`=Pa, `5`=kPa. | -| **0x0003** | `NACHKOMMASTELLEN` | R/W | Anzahl der Dezimalstellen für den Messwert (0-3). | -| **0x0004** | `MESSWERT_AKTUELL` | R | Der skalierte Messwert als vorzeichenbehafteter 16-Bit-Integer. | -| **0x0005** | `MESSBEREICH_NULLPUNKT` | R/W | Rohwert für den Nullpunkt der Skala. | -| **0x0006** | `MESSBEREICH_ENDPUNKT` | R/W | Rohwert für den Endpunkt der Skala. | +| **0x0002** | `UNIT` | R/W | `0`=Keine, `1`=cm, `2`=mm, `3`=MPa, `4`=Pa, `5`=kPa. | +| **0x0003** | `DECIMAL_PLACES` | R/W | Anzahl der Dezimalstellen für den Messwert (0-3). | +| **0x0004** | `CURRENT_MEASUREMENT` | R | Der skalierte Messwert als vorzeichenbehafteter 16-Bit-Integer. | +| **0x0005** | `MEASUREMENT_RANGE_ZERO_POINT` | R/W | Rohwert für den Nullpunkt der Skala. | +| **0x0006** | `MEASUREMENT_RANGE_END_POINT` | R/W | Rohwert für den Endpunkt der Skala. | diff --git a/docs/planning.de.md b/docs/planning.de.md index 7c52692..87170ad 100644 --- a/docs/planning.de.md +++ b/docs/planning.de.md @@ -9,11 +9,13 @@ | ✅ | **Phase 0: Planung & Definition** | | | | ✅ | Konzept erstellen und finalisieren | 30.06.2025 | Architektur, Komponenten und grundlegende Architektur sind festgelegt. | | ✅ | MODBUS Register Map definieren | 30.06.2025 | Die "API" der Slaves ist definiert und bildet die Grundlage für die Software-Entwicklung. | +| ✅ | Header- und deutsche Dokumentation aktualisiert | 10.07.2025 | Doxygen-Kommentare in Headern und deutsche .md-Dateien auf den neuesten Stand gebracht und übersetzt. | | ☐ | **Phase 1: Slave-Node Prototyp (STM32 Eval-Board)** | | **Ziel:** Ein einzelner Slave wird auf dem Eval-Board zum Leben erweckt. | | ✅ | 1.1 Entwicklungsumgebung für STM32/Zephyr einrichten | 30.06.2025 | Toolchain, VS Code, Zephyr-SDK, MCUBoot etc. installieren und ein "Hello World" zum Laufen bringen. | -| ☐ | 1.2 Basis-Firmware für Slave-Node erstellen | | Hardware-Abstraktion (GPIOs, ADC, UART für RS485) implementieren. | -| ☐ | 1.3 MODBUS-RTU Stack auf dem Slave implementieren | | Basierend auf der definierten Register-Map. Zuerst nur lesende Funktionen (Status, Version). | -| ☐ | 1.4 Kernlogik implementieren (z.B. Ventilsteuerung) | | Umsetzung der `VENTIL_ZUSTAND_BEWEGUNG` Logik, Strommessung für Endlagen etc. | +| ✅ | 1.2 Hardware-Abstraktion (VND7050AJ, RS485) | 10.07.2025 | Implementierung der Treiber für den VND7050AJ und die RS485-Kommunikation. | +| ✅ | 1.3 Basis-Firmware für Slave-Node erstellen | 10.07.2025 | Hardware-Abstraktion (GPIOs) implementiert. | +| ✅ | 1.3 MODBUS-RTU Stack auf dem Slave implementieren | 10.07.2025 | Basierend auf der definierten Register-Map. Zuerst nur lesende Funktionen (Status, Version). | +| ✅ | 1.4 Kernlogik implementieren (z.B. Ventilsteuerung) | 10.07.2025 | Umsetzung der `VALVE_STATE_MOVEMENT` Logik, Strommessung für Endlagen etc. | | ☐ | **Phase 2: Verifikation der Slave-Firmware** | | **Ziel:** Nachweisen, dass der Slave sich exakt an die MODBUS-Spezifikation hält. | | ☐ | 2.1 Slave-Node mit PC via USB-MODBUS-Adapter testen | | **Kritischer Meilenstein.** Mit Tools wie "QModMaster" oder einem Python-Skript die Register lesen & schreiben. Die Slave-Firmware wird so unabhängig vom Gateway validiert. | | ☐ | 2.2 Firmware-Update Mechanismus testen | | Den kompletten Update-Prozess (Chunking, CRC-Check) mit einem Skript vom PC aus testen. Der Slave schreibt die Firmware dabei vorerst nur in einen ungenutzten RAM-Bereich. | diff --git a/software/apps/slave_node/boards/weact_stm32g431_core.overlay b/software/apps/slave_node/boards/weact_stm32g431_core.overlay index 7b8ec2f..e9c6f4f 100644 --- a/software/apps/slave_node/boards/weact_stm32g431_core.overlay +++ b/software/apps/slave_node/boards/weact_stm32g431_core.overlay @@ -1,91 +1,41 @@ -#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 - }; + aliases { + vnd7050aj = &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 */ - - /* 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_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 */ - - /* 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 */ - }; - }; + vnd7050aj: vnd7050aj { + compatible = "st,vnd7050aj"; + status = "okay"; + + input0-gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>; + input1-gpios = <&gpiob 9 GPIO_ACTIVE_HIGH>; + select0-gpios = <&gpiob 5 GPIO_ACTIVE_HIGH>; + select1-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>; + sense-enable-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>; + fault-reset-gpios = <&gpiob 3 GPIO_ACTIVE_LOW>; + io-channels = <&adc1 1>; + r-sense-ohms = <1500>; + k-vcc = <4139>; + }; }; -// Clock configuration: Uncomment the following section to use calibrated HSI instead of HSE -//&clk_hse { -// status = "disabled"; // Disable external crystal oscillator -//}; -// -//&clk_hsi { -// status = "okay"; // Enable internal high-speed oscillator (16 MHz, calibrated) -//}; -// -//&pll { -// // Change PLL source from HSE to HSI -// clocks = <&clk_hsi>; -// // Adjust multipliers to maintain 144 MHz system clock with 16 MHz HSI input -// // HSI = 16 MHz, div-m = 4, mul-n = 72, div-r = 2 -// // PLL_VCO = (16 MHz / 4) * 72 = 288 MHz -// // SYSCLK = 288 MHz / 2 = 144 MHz -// div-m = <4>; // Divide HSI by 4 (16 MHz / 4 = 4 MHz) -// mul-n = <72>; // Multiply by 72 (4 MHz * 72 = 288 MHz) -// div-r = <2>; // Divide by 2 for system clock (288 MHz / 2 = 144 MHz) -//}; +&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>; + + channel@1 { + reg = <1>; + zephyr,gain = "ADC_GAIN_1"; + zephyr,reference = "ADC_REF_INTERNAL"; + zephyr,acquisition-time = ; + zephyr,resolution = <12>; + }; +}; &usart1 { modbus0 { @@ -95,21 +45,4 @@ status = "okay"; pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>; // PA9=TX, PA10=RX for Modbus communication 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 - 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 - }; }; \ No newline at end of file diff --git a/software/apps/slave_node/cdc-acm.overlay b/software/apps/slave_node/cdc-acm.overlay index f12b35b..32f5b3e 100644 --- a/software/apps/slave_node/cdc-acm.overlay +++ b/software/apps/slave_node/cdc-acm.overlay @@ -1,78 +1,5 @@ #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 deleted file mode 100644 index aae3106..0000000 --- a/software/apps/slave_node/dts/bindings/adc/custom,motor-current.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# Custom motor current sensor binding -description: Motor current sensor using VND7050AJ multiplexer - -compatible: "custom,motor-current" - -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 - - current-sense-resistor-mohm: - type: int - required: true - description: Current sense resistor value in milliohms - - k-factor: - type: int - required: true - 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 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 deleted file mode 100644 index de4023f..0000000 --- a/software/apps/slave_node/dts/bindings/adc/custom,supply-voltage.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Custom supply voltage sensor binding -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 - description: Reference to the VND7050AJ sensor multiplexer node - - voltage-divider-ratio: - type: int - required: true - description: Voltage divider ratio for scaling measurements - - mux-channel: - type: int - required: true - description: Multiplexer channel number (0-3) for this sensor - - measurement-delay-ms: - type: int - required: false - default: 5 - description: Delay in milliseconds after GPIO setup before measurement \ No newline at end of file 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 deleted file mode 100644 index e32b489..0000000 --- a/software/apps/slave_node/dts/bindings/adc/vnd7050aj,sensor-mux.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# VND7050AJ Sensor Multiplexer binding -description: VND7050AJ sensor multiplexer for ADC channel selection - -compatible: "vnd7050aj,sensor-mux" - -properties: - io-channels: - type: phandle-array - required: true - description: ADC channel phandle and specifier for sensor input - - 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 - - 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) - - in0-gpios: - type: phandle-array - required: false - description: GPIO for valve input 0 control (IN0 pin) - - in1-gpios: - type: phandle-array - required: false - description: GPIO for valve input 1 control (IN1 pin) - - rst-gpios: - 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/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml b/software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml deleted file mode 100644 index 6e86bfe..0000000 --- a/software/apps/slave_node/dts/bindings/vnd7050aj-valve-controller.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# VND7050AJ Valve Controller binding -description: VND7050AJ valve controller GPIO configuration - -compatible: "vnd7050aj-valve-controller" - -properties: - in0-gpios: - type: phandle-array - description: GPIO for IN0 control signal - required: true - - in1-gpios: - type: phandle-array - description: GPIO for IN1 control signal - required: true - - rst-gpios: - type: phandle-array - description: GPIO for reset pin - required: true - - sen-gpios: - type: phandle-array - description: GPIO for sense enable pin - required: true - - s0-gpios: - type: phandle-array - description: GPIO for status/select 0 pin - required: true - - s1-gpios: - type: phandle-array - description: GPIO for status/select 1 pin - required: true diff --git a/software/apps/slave_node/prj.conf b/software/apps/slave_node/prj.conf index ab89f97..2a03c4a 100644 --- a/software/apps/slave_node/prj.conf +++ b/software/apps/slave_node/prj.conf @@ -13,6 +13,7 @@ CONFIG_NVS=y CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_SETTINGS_LOG_LEVEL_DBG=y # Config modbus CONFIG_UART_INTERRUPT_DRIVEN=y @@ -20,7 +21,6 @@ CONFIG_MODBUS=y CONFIG_MODBUS_ROLE_SERVER=y CONFIG_MODBUS_BUFFER_SIZE=256 -# ADC Sensor Configuration - Use real ADC readings -CONFIG_ADC_SENSOR_SIMULATED=n -CONFIG_ADC=y +# Enable VND7050AJ +CONFIG_VND7050AJ=y diff --git a/software/apps/slave_node/src/main.c b/software/apps/slave_node/src/main.c index acb711c..b63a429 100644 --- a/software/apps/slave_node/src/main.c +++ b/software/apps/slave_node/src/main.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,9 +8,7 @@ LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); int main(void) { - int rc; - LOG_INF("Starting Irrigation System Slave Node version %s (Build version %s)", - APP_VERSION_STRING, STRINGIFY(APP_BUILD_VERSION)); + LOG_INF("Starting Irrigation System Slave Node"); if (settings_subsys_init() || settings_load()) { LOG_ERR("Settings initialization or loading failed"); @@ -20,9 +17,8 @@ int main(void) { valve_init(); fwu_init(); - rc = modbus_server_init(); - if (rc < 0) { - LOG_ERR("Modbus RTU server initialization failed: %d", rc); + if (modbus_server_init()) { + LOG_ERR("Modbus RTU server initialization failed"); return 0; } diff --git a/software/include/lib/adc_sensor.h b/software/include/lib/adc_sensor.h deleted file mode 100644 index ca86382..0000000 --- a/software/include/lib/adc_sensor.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef ADC_SENSOR_H -#define ADC_SENSOR_H - -#include - -/** - * @file adc_sensor.h - * @brief API for the ADC sensor library. - * - * This library provides functions to initialize and read from the ADC sensors, - * specifically for measuring supply voltage and motor current. - * It can operate in a real or simulated mode. - */ - -/** - * @brief Initializes the ADC sensor system. - * - * This function sets up the necessary ADC channels and configurations. - * It should be called once before any other function in this library. - * In simulated mode, it logs the simulated values. - * - * @return 0 on success, or a negative error code on failure. - */ -int adc_sensor_init(void); - -/** - * @brief Gets the current supply voltage reading. - * - * This function reads the value from the corresponding ADC channel and converts - * it to millivolts. - * - * @return The supply voltage in millivolts (mV). - */ -uint16_t adc_sensor_get_voltage_mv(void); - -/** - * @brief Gets the current motor current reading (legacy function). - * - * 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 b6a8034..559bf69 100644 --- a/software/include/lib/modbus_server.h +++ b/software/include/lib/modbus_server.h @@ -17,56 +17,56 @@ */ enum { /** - * @brief Kombiniertes Status-Register für das Ventil. - * High-Byte: Bewegung (0=Idle, 1=Öffnet, 2=Schliesst, 3=Fehler). - * Low-Byte: Zustand (0=Geschlossen, 1=Geöffnet). + * @brief Combined status register for the valve. + * High-Byte: Movement (0=Idle, 1=Opening, 2=Closing, 3=Error). + * Low-Byte: State (0=Closed, 1=Open). */ REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000, /** - * @brief Motorstrom beim Öffnen in Milliampere (mA) - IN0 current sense. + * @brief Motor current during opening in milliamperes (mA). */ REG_INPUT_MOTOR_OPEN_CURRENT_MA = 0x0001, /** - * @brief Motorstrom beim Schließen in Milliampere (mA) - IN1 current sense. + * @brief Motor current during closing in milliamperes (mA). */ REG_INPUT_MOTOR_CLOSE_CURRENT_MA = 0x0002, /** - * @brief Bitmaske der digitalen Eingänge. Bit 0: Eingang 1, Bit 1: Eingang 2. - * 1=Aktiv. + * @brief Bitmask of digital inputs. Bit 0: Input 1, Bit 1: Input 2. + * 1=Active. */ REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020, /** - * @brief Event-Flags für Taster (Clear-on-Read). Bit 0: Taster 1 gedrückt. - * Bit 1: Taster 2 gedrückt. + * @brief Event flags for buttons (Clear-on-Read). Bit 0: Button 1 pressed. + * Bit 1: Button 2 pressed. */ REG_INPUT_BUTTON_EVENTS = 0x0021, /** - * @brief Firmware-Version, z.B. 0x0102 für v1.2. + * @brief Firmware version, e.g., 0x0102 for v1.2. */ REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0, /** - * @brief Firmware-Version Patch-Level, z.B. 3 für v1.2.3. + * @brief Firmware version patch level, e.g., 3 for v1.2.3. */ REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1, /** - * @brief Gerätestatus (0=OK, 1=Allgemeiner Fehler). + * @brief Device status (0=OK, 1=General Error). */ REG_INPUT_DEVICE_STATUS = 0x00F2, /** - * @brief Untere 16 Bit der Uptime in Sekunden. + * @brief Lower 16 bits of uptime in seconds. */ REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3, /** - * @brief Obere 16 Bit der Uptime in Sekunden. + * @brief Upper 16 bits of uptime in seconds. */ REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4, /** - * @brief Aktuelle Versorgungsspannung in Millivolt (mV). + * @brief Current supply voltage in millivolts (mV). */ REG_INPUT_SUPPLY_VOLTAGE_MV = 0x00F5, /** - * @brief CRC16 des zuletzt im Puffer empfangenen Daten-Chunks für das - * Firmware-Update. + * @brief CRC16 of the last received data chunk in the buffer for firmware + * update. */ REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100 }; @@ -77,52 +77,52 @@ enum { */ enum { /** - * @brief Ventilsteuerungsbefehl (1=Öffnen, 2=Schliessen, 0=Bewegung stoppen). + * @brief Valve control command (1=Open, 2=Close, 0=Stop movement). */ REG_HOLDING_VALVE_COMMAND = 0x0000, /** - * @brief Sicherheits-Timeout in Sekunden für den Öffnen-Vorgang. + * @brief Safety timeout in seconds for the opening process. */ REG_HOLDING_MAX_OPENING_TIME_S = 0x0001, /** - * @brief Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. + * @brief Safety timeout in seconds for the closing process. */ REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002, /** - * @brief Bitmaske zum Lesen und Schreiben der digitalen Ausgänge. Bit 0: - * Ausgang 1, Bit 1: Ausgang 2. 1=AN, 0=AUS. + * @brief Bitmask for reading and writing digital outputs. Bit 0: Output 1, + * Bit 1: Output 2. 1=ON, 0=OFF. */ REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010, /** - * @brief Timeout des Fail-Safe-Watchdogs in Sekunden. 0=Deaktiviert. + * @brief Fail-safe watchdog timeout in seconds. 0=Disabled. */ REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0, /** - * @brief Schreiben von 1 startet das Gerät neu. + * @brief Writing 1 restarts the device. */ REG_HOLDING_DEVICE_RESET = 0x00F1, /** - * @brief Befehl für das Firmware-Update. - * 1: Verify Chunk - Slave schreibt den letzten Chunk ins Flash. - * 2: Finalize Update - Installation abschliessen und neu starten. + * @brief Command for firmware update. + * 1: Verify Chunk - Slave writes the last chunk to flash. + * 2: Finalize Update - Complete installation and restart. */ REG_HOLDING_FWU_COMMAND = 0x0100, /** - * @brief Untere 16 Bit des 32-Bit-Offsets für den nächsten - * Firmware-Update-Chunk. + * @brief Lower 16 bits of the 32-bit offset for the next firmware update + * chunk. */ REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101, /** - * @brief Obere 16 Bit des 32-Bit-Offsets für den nächsten - * Firmware-Update-Chunk. + * @brief Upper 16 bits of the 32-bit offset for the next firmware update + * chunk. */ REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102, /** - * @brief Grösse des nächsten Firmware-Update-Chunks in Bytes (max. 256). + * @brief Size of the next firmware update chunk in bytes (max. 256). */ REG_HOLDING_FWU_CHUNK_SIZE = 0x0103, /** - * @brief Startadresse des 256-Byte-Puffers für Firmware-Update-Daten. + * @brief Start address of the 256-byte buffer for firmware update data. */ REG_HOLDING_FWU_DATA_BUFFER = 0x0180, }; diff --git a/software/include/lib/valve.h b/software/include/lib/valve.h index 9808355..90ed1d6 100644 --- a/software/include/lib/valve.h +++ b/software/include/lib/valve.h @@ -13,19 +13,9 @@ * configuring the maximum opening and closing times. */ -/** - * @brief Defines the GPIO pins used for the valve controller. - */ -struct valve_gpios { - const struct gpio_dt_spec - in0; /**< Control input 0 for the VND7050AJ driver. */ - const struct gpio_dt_spec - in1; /**< Control input 1 for the VND7050AJ driver. */ - const struct gpio_dt_spec rst; /**< Reset pin for the VND7050AJ driver. */ - const struct gpio_dt_spec sen; /**< Sense (current measurement) pin. */ - const struct gpio_dt_spec s0; /**< S0 select pin. */ - const struct gpio_dt_spec s1; /**< S1 select pin. */ -}; +#define VALVE_CHANNEL_OPEN 0 +#define VALVE_CHANNEL_CLOSE 1 +#define VALVE_ENDPOSITION_CHECK_INTERVAL K_MSEC(100) /** * @brief Represents the static state of the valve (open or closed). @@ -49,8 +39,11 @@ enum valve_movement { * @brief Initializes the valve control system. * * Configures the GPIOs and loads saved settings for timeouts. + * This function must be called before any other valve functions. + * + * @return 0 on success, or a negative error code on failure. */ -void valve_init(void); +int valve_init(void); /** * @brief Starts opening the valve. @@ -120,4 +113,31 @@ uint16_t valve_get_max_open_time(void); */ uint16_t valve_get_max_close_time(void); +/** + * @brief Gets the current drawn by the valve motor during opening. + * + * @return The motor current in milliamps. + */ +int32_t valve_get_opening_current(void); + +/** + * @brief Gets the current drawn by the valve motor during closing. + * + * @return The motor current in milliamps. + */ +int32_t valve_get_closing_current(void); + +/** + * @brief Gets the temperature of the valve motor driver. + * + * @return The temperature in degrees Celsius. + */ +int32_t valve_get_vnd_temp(void); + +/** + * @brief Gets the voltage supplied to the valve motor driver. + * + * @return The voltage in millivolts. + */ +int32_t valve_get_vnd_voltage(void); #endif // VALVE_H diff --git a/software/lib/CMakeLists.txt b/software/lib/CMakeLists.txt index ca2a6c0..07303c6 100644 --- a/software/lib/CMakeLists.txt +++ b/software/lib/CMakeLists.txt @@ -1,4 +1,3 @@ -add_subdirectory_ifdef(CONFIG_ADC_SENSOR adc_sensor) add_subdirectory_ifdef(CONFIG_LIB_FWU fwu) add_subdirectory_ifdef(CONFIG_LIB_MODBUS_SERVER modbus_server) add_subdirectory_ifdef(CONFIG_LIB_VALVE valve) diff --git a/software/lib/Kconfig b/software/lib/Kconfig index a279fef..1e2390b 100644 --- a/software/lib/Kconfig +++ b/software/lib/Kconfig @@ -1,6 +1,5 @@ menu "Irrigation system software libraries" -rsource "adc_sensor/Kconfig" rsource "fwu/Kconfig" rsource "modbus_server/Kconfig" rsource "valve/Kconfig" diff --git a/software/lib/adc_sensor/CMakeLists.txt b/software/lib/adc_sensor/CMakeLists.txt deleted file mode 100644 index c45eb90..0000000 --- a/software/lib/adc_sensor/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -zephyr_library_sources(adc_sensor.c) diff --git a/software/lib/adc_sensor/Kconfig b/software/lib/adc_sensor/Kconfig deleted file mode 100644 index 5767b19..0000000 --- a/software/lib/adc_sensor/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -config ADC_SENSOR - bool "ADC sensor library" - default y - help - Enable ADC sensor library for voltage and current measurements. - -if ADC_SENSOR - -config ADC_SENSOR_SIMULATED - bool "Use simulated ADC readings" - default n - help - Use simulated values instead of real ADC readings. - Voltage: 12000mV, Current: 45mA - -endif # ADC_SENSOR diff --git a/software/lib/adc_sensor/adc_sensor.c b/software/lib/adc_sensor/adc_sensor.c deleted file mode 100644 index c0de140..0000000 --- a/software/lib/adc_sensor/adc_sensor.c +++ /dev/null @@ -1,382 +0,0 @@ -/** - * @file adc_sensor.c - * @brief Implementation of the ADC sensor library. - * - * This file contains the implementation for initializing and reading from ADC - * sensors. It currently provides simulated values for voltage and current, with - * placeholders for real hardware ADC implementation including GPIO control. - */ - -#include -#include -#include -#include -#include -#include - -LOG_MODULE_REGISTER(adc_sensor, LOG_LEVEL_INF); - -// Simulated values -#define SIMULATED_VOLTAGE_MV 12000 -#define SIMULATED_CURRENT_MA 45 - -// Devicetree node checks -#define VOLTAGE_SENSOR_NODE DT_NODELABEL(supply_voltage) -#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 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_DELAY_MS DT_PROP(VOLTAGE_SENSOR_NODE, measurement_delay_ms) -#endif - -#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; -static struct adc_channel_cfg adc_channel_cfg = { - .gain = ADC_GAIN_1, - .reference = ADC_REF_INTERNAL, - .acquisition_time = ADC_ACQ_TIME_DEFAULT, - .channel_id = ADC_CHANNEL, - .differential = 0}; - -static struct adc_sequence adc_sequence = { - .channels = BIT(ADC_CHANNEL), - .buffer_size = sizeof(uint16_t), - .resolution = 12, -}; - -static uint16_t adc_buffer; -#endif - -static bool initialized = false; - -#ifndef CONFIG_ADC_SENSOR_SIMULATED -// GPIO specs from VND7050AJ node -#if DT_NODE_EXISTS(VND7050AJ_NODE) -static const struct gpio_dt_spec sen_gpio = - GPIO_DT_SPEC_GET(VND7050AJ_NODE, sen_gpios); -static const struct gpio_dt_spec s0_gpio = - GPIO_DT_SPEC_GET(VND7050AJ_NODE, s0_gpios); -static const struct gpio_dt_spec s1_gpio = - GPIO_DT_SPEC_GET(VND7050AJ_NODE, s1_gpios); -#endif - -/** - * @brief Configure GPIO pins for ADC sensor multiplexer control - */ -static int configure_sensor_gpios(void) { - int ret = 0; - -#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); - if (ret < 0) { - LOG_ERR("Failed to configure SEN GPIO: %d", ret); - return ret; - } - } - - if (gpio_is_ready_dt(&s0_gpio)) { - ret = gpio_pin_configure_dt(&s0_gpio, GPIO_OUTPUT_INACTIVE); - if (ret < 0) { - LOG_ERR("Failed to configure S0 GPIO: %d", ret); - return ret; - } - } - - if (gpio_is_ready_dt(&s1_gpio)) { - ret = gpio_pin_configure_dt(&s1_gpio, GPIO_OUTPUT_INACTIVE); - if (ret < 0) { - LOG_ERR("Failed to configure S1 GPIO: %d", ret); - return ret; - } - } -#endif - - return 0; -} - -/** - * @brief Set multiplexer channel for sensor selection - * @param enable Enable/disable the sensor - * @param channel Multiplexer channel (0-3) - * @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(VND7050AJ_NODE) - if (gpio_is_ready_dt(&sen_gpio)) { - gpio_pin_set_dt(&sen_gpio, enable ? 1 : 0); - } - if (gpio_is_ready_dt(&s0_gpio)) { - gpio_pin_set_dt(&s0_gpio, (channel & 0x01) ? 1 : 0); - } - if (gpio_is_ready_dt(&s1_gpio)) { - gpio_pin_set_dt(&s1_gpio, (channel & 0x02) ? 1 : 0); - } - - // Delay for GPIO settling - if (delay_ms > 0) { - k_msleep(delay_ms); - } -#endif - return 0; -} -#endif /* !CONFIG_ADC_SENSOR_SIMULATED */ - -#ifndef CONFIG_ADC_SENSOR_SIMULATED -/** - * @brief Read ADC value and convert to millivolts (for voltage sensor) - * @return ADC reading in millivolts, or 0 on error - */ -static uint16_t read_adc_voltage_mv(void) { -#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); - return 0; - } - - // Convert ADC reading to millivolts - // ADC reading is 12-bit (0-4095) representing 0 to ADC_REFERENCE_MV - uint32_t adc_value = adc_buffer; - uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095; - - // Apply voltage divider scaling - voltage_mv *= VOLTAGE_DIVIDER_RATIO; - - LOG_DBG("ADC raw: %u, voltage: %u mV", adc_value, (uint16_t)voltage_mv); - - return (uint16_t)voltage_mv; -#else - return 0; -#endif -} - -/** - * @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_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); - 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_OPEN_K_FACTOR * 1000) / - CURRENT_OPEN_SENSE_RESISTOR_MOHM; - - 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 - return 0; -#endif -} -#endif - -int adc_sensor_init(void) { - if (initialized) { - return 0; - } - -#ifdef CONFIG_ADC_SENSOR_SIMULATED - LOG_INF("ADC sensor initialized (simulated mode)"); - LOG_INF("Simulated values: %dmV, %dmA", SIMULATED_VOLTAGE_MV, - SIMULATED_CURRENT_MA); -#else - // Initialize GPIO pins for sensor control - int ret = configure_sensor_gpios(); - if (ret < 0) { - LOG_ERR("Failed to configure sensor GPIOs: %d", ret); - return ret; - } - - // Initialize ADC hardware -#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"); - return -ENODEV; - } - - adc_sequence.buffer = &adc_buffer; - - ret = adc_channel_setup(adc_dev, &adc_channel_cfg); - if (ret < 0) { - LOG_ERR("Failed to setup ADC channel: %d", ret); - return ret; - } - - LOG_INF("ADC device ready: %s", adc_dev->name); -#endif - - LOG_INF("ADC sensor initialized (real ADC mode)"); - -#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) - LOG_INF("Voltage sensor: divider ratio %d", VOLTAGE_DIVIDER_RATIO); -#endif -#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 - - initialized = true; - return 0; -} - -uint16_t adc_sensor_get_voltage_mv(void) { - if (!initialized) { - LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()"); - adc_sensor_init(); - } - -#ifdef CONFIG_ADC_SENSOR_SIMULATED - return SIMULATED_VOLTAGE_MV; -#else - // Set multiplexer to voltage channel (channel 3: VCC sense) -#if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) - set_mux_channel(true, 3, VOLTAGE_DELAY_MS); - - // Read real ADC value for voltage - uint16_t voltage = read_adc_voltage_mv(); - - // Disable sensor after measurement to save power - set_mux_channel(false, 0, 0); - - return voltage; -#else - return 0; -#endif -#endif -} - -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(); - } - -#ifdef CONFIG_ADC_SENSOR_SIMULATED - return SIMULATED_CURRENT_MA; -#else - // 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_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); - - return current; -#else - return 0; -#endif -#endif -} diff --git a/software/lib/modbus_server/modbus_server.c b/software/lib/modbus_server/modbus_server.c index 4458623..1395356 100644 --- a/software/lib/modbus_server/modbus_server.c +++ b/software/lib/modbus_server/modbus_server.c @@ -8,11 +8,11 @@ */ #include -#include #include #include #include #include +#include #include #include #include @@ -148,10 +148,10 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg) { *reg = (valve_get_movement() << 8) | (valve_get_state() & 0xFF); break; case REG_INPUT_MOTOR_OPEN_CURRENT_MA: - *reg = adc_sensor_get_current_open_ma(); + *reg = (uint16_t)valve_get_opening_current(); break; case REG_INPUT_MOTOR_CLOSE_CURRENT_MA: - *reg = adc_sensor_get_current_close_ma(); + *reg = (uint16_t)valve_get_closing_current(); break; case REG_INPUT_UPTIME_SECONDS_LOW: *reg = (uint16_t)(uptime_s & 0xFFFF); @@ -160,7 +160,7 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg) { *reg = (uint16_t)(uptime_s >> 16); break; case REG_INPUT_SUPPLY_VOLTAGE_MV: - *reg = adc_sensor_get_voltage_mv(); + *reg = (uint16_t)valve_get_vnd_voltage(); break; case REG_INPUT_FWU_LAST_CHUNK_CRC: *reg = fwu_get_last_chunk_crc(); @@ -190,13 +190,6 @@ static struct modbus_user_callbacks mbs_cbs = { int modbus_server_init(void) { k_timer_init(&watchdog_timer, watchdog_timer_handler, NULL); - // Initialize ADC sensor - int ret = adc_sensor_init(); - if (ret < 0) { - LOG_ERR("Failed to initialize ADC sensor: %d", ret); - return ret; - } - // Load saved settings uint32_t saved_baudrate = 19200; uint8_t saved_unit_id = 1; diff --git a/software/lib/valve/valve.c b/software/lib/valve/valve.c index 6ba8c51..ac9523c 100644 --- a/software/lib/valve/valve.c +++ b/software/lib/valve/valve.c @@ -10,25 +10,24 @@ #include #include #include +#include #include #include #include +#define VND_NODE DT_ALIAS(vnd7050aj) +#if !DT_NODE_HAS_STATUS(VND_NODE, okay) +#error VND7050AJ node is not defined or enabled +#endif +const struct device *vnd7050aj_dev = DEVICE_DT_GET(VND_NODE); + LOG_MODULE_REGISTER(valve, LOG_LEVEL_INF); -static const struct valve_gpios valve_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; +static enum valve_state current_state = VALVE_STATE_OPEN; static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE; -static uint16_t max_opening_time_s = 60; -static uint16_t max_closing_time_s = 60; +static uint16_t max_opening_time_s = 10; +static uint16_t max_closing_time_s = 10; +static uint32_t movement_start_time = 0; static struct k_work_delayable valve_work; // Work item for scheduling valve movement timeouts @@ -41,63 +40,85 @@ static struct k_work_delayable * @param work Pointer to the k_work item. */ static void valve_work_handler(struct k_work *work) { - gpio_pin_set_dt(&valve_gpios.in0, 0); - gpio_pin_set_dt(&valve_gpios.in1, 0); - gpio_pin_set_dt(&valve_gpios.rst, 0); - + int current_ma = 0; + uint32_t now; + now = k_uptime_get_32(); if (current_movement == VALVE_MOVEMENT_OPENING) { + if (now - movement_start_time > max_opening_time_s * 1000) { + LOG_WRN("Valve opening timeout reached, stopping motor."); + current_movement = VALVE_MOVEMENT_ERROR; + goto work_handler_finish; + } + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t_ma); + if (current_ma > 10) { + k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); + return; + } LOG_INF("Valve finished opening"); } else if (current_movement == VALVE_MOVEMENT_CLOSING) { + if (now - movement_start_time > max_closing_time_s * 1000) { + LOG_WRN("Valve closing timeout reached, stopping motor."); + current_movement = VALVE_MOVEMENT_ERROR; + goto work_handler_finish; + } + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, + ¤t_ma); + if (current_ma > 10) { + k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); + return; + } current_state = VALVE_STATE_CLOSED; LOG_INF("Valve finished closing"); } current_movement = VALVE_MOVEMENT_IDLE; +work_handler_finish: + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); + LOG_INF("Valve work handler finished. Current state: %d, Movement: %d", + current_state, current_movement); } -void valve_init(void) { +int valve_init(void) { + if (!device_is_ready(vnd7050aj_dev)) { + LOG_ERR("VND7050AJ device is not ready"); + return -ENODEV; + } k_work_init_delayable(&valve_work, valve_work_handler); 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); - 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); + valve_close(); + return 0; } void valve_open(void) { - if (current_state == VALVE_STATE_CLOSED) { - gpio_pin_set_dt(&valve_gpios.rst, 1); - 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)); - } + vnd7050aj_reset_fault(vnd7050aj_dev); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, true); + current_state = VALVE_STATE_OPEN; + current_movement = VALVE_MOVEMENT_OPENING; /* Security: assume valve open as + soons as it starts opening */ + movement_start_time = k_uptime_get_32(); + k_work_schedule(&valve_work, K_MSEC(100)); } void valve_close(void) { - if (current_state == VALVE_STATE_OPEN) { - gpio_pin_set_dt(&valve_gpios.rst, 1); - gpio_pin_set_dt(&valve_gpios.in0, 0); - gpio_pin_set_dt(&valve_gpios.in1, 1); - current_movement = VALVE_MOVEMENT_CLOSING; - k_work_schedule(&valve_work, K_MSEC(max_closing_time_s * 1000 * 0.9)); - } + vnd7050aj_reset_fault(vnd7050aj_dev); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, true); + movement_start_time = k_uptime_get_32(); + current_movement = VALVE_MOVEMENT_CLOSING; + k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); } void valve_stop(void) { k_work_cancel_delayable(&valve_work); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_OPEN, false); + vnd7050aj_set_output_state(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, false); current_movement = VALVE_MOVEMENT_IDLE; } @@ -119,3 +140,27 @@ void valve_set_max_close_time(uint16_t seconds) { } uint16_t valve_get_max_open_time(void) { return max_opening_time_s; } uint16_t valve_get_max_close_time(void) { return max_closing_time_s; } + +int32_t valve_get_opening_current(void) { + int32_t current; + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, ¤t); + return current; +} + +int32_t valve_get_closing_current(void) { + int32_t current; + vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, ¤t); + return current; +} + +int32_t valve_get_vnd_temp(void) { + int32_t temp_c; + vnd7050aj_read_chip_temp(vnd7050aj_dev, &temp_c); + return temp_c; +} + +int32_t valve_get_vnd_voltage(void) { + int32_t voltage_mv; + vnd7050aj_read_supply_voltage(vnd7050aj_dev, &voltage_mv); + return voltage_mv; +}