47 Commits

Author SHA1 Message Date
0c4a728c17 feat(can): Add CAN ID definitions
This commit introduces the CAN ID definitions for the irrigation system.
It defines the structure of the CAN IDs and macros for priority and function.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-22 11:02:27 +02:00
d92a1d9533 feat(app): Add CAN node application
This commit introduces a new CAN node application for the irrigation system.

The application initializes the settings subsystem and the valve system. It is intended to be used on a CAN bus to control a valve.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-22 10:54:21 +02:00
9325fa20c8 feat(valve): Add current measurement callbacks and shell commands
This commit introduces several enhancements to the valve library.

- New weak callback functions `valve_current_open_callback` and `valve_current_close_callback` are added to allow the application to monitor the current during valve opening and closing operations.
- The `movement_timeout_handler` now correctly sets the valve state to `VALVE_STATE_OPEN` and movement to `VALVE_MOVEMENT_IDLE` upon timeout.
- New shell commands `valve open`, `valve close`, and `valve stop` are added for direct control of the valve.
- The existing setting commands are reorganized under a `valve set` subcommand, and their names are shortened (e.g., `set_open_t` to `open_t`).
- The default configuration for `LIB_MODBUS_SERVER` and `LIB_VALVE` is changed to `n`.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-22 10:53:24 +02:00
08c47f00f8 feat(esphome): Add CAN configuration
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-22 08:55:30 +02:00
1cba00df8c feat: Add mqtt_gateway application and .gitignore
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-22 08:53:23 +02:00
35bd208cc0 refactor: Remove unused files
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-22 08:49:03 +02:00
48cfcd5d4c Fixed board definition
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-21 17:30:14 +02:00
d76b897eb2 feat: Integrate VND7050AJ driver and enhance gateway settings
This commit introduces the VND7050AJ driver as a new submodule and integrates it into the project.

Key changes include:
- Added  as a git submodule.
- Enhanced the gateway application () with LittleFS and the settings subsystem.
  - Implemented new shell commands (, , ) for managing custom settings.
  - Added functionality to compact the settings file.
- Updated  to include new library dependencies and log  return code.
- Adjusted include paths for  in relevant files.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-17 15:18:22 +02:00
0713f8255e gateway: working shell on uart0 and mcumgr on usb_serial, minimal OS mgmt config\n
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-15 11:21:19 +02:00
fc089e5a33 gateway: working shell on uart0 and mcumgr on usb_serial, minimal OS mgmt config\n
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-15 11:20:18 +02:00
ef966cb078 Played around with the irrigaton system yaml
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-14 14:12:48 +02:00
6f304efb57 feat(esphome): Add initial irrigation system configuration
This commit introduces the initial ESPHome configuration for the irrigation system.

- `irrigation_system.yaml`: ESPHome configuration with Modbus valve control.

- `create_secrets.py`: Script to generate `secrets.yaml`.

- `secrets.yaml.example`: Example secrets file.

- `requirements.txt`: Python dependencies.

- `.gitignore`: Standard ESPHome gitignore file.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-14 11:32:25 +02:00
e1ae96506d (fix) RTU gateway
fixed rtu gateway
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-12 16:08:49 +02:00
6cb17be451 (feat) added RTU gateway
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-12 14:13:09 +02:00
54e991294b feat(docs): align modbus register documentation with implementation
The modbus register documentation in `docs/modbus-registers.de.md` has been updated to be consistent with the implementation in `software/include/lib/modbus_server.h`.

- Register names in the documentation now match the programmatic names in the header file.
- Missing registers `REG_HOLDING_OBSTACLE_THRESHOLD_OPEN_MA` and `REG_HOLDING_OBSTACLE_THRESHOLD_CLOSE_MA` have been added to the documentation.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-12 09:31:50 +02:00
0227e54198 bootloader somehow working
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 15:10:10 +02:00
c3c23efc95 Cleaning up
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 11:43:47 +02:00
4466b677a6 refactor: Integrate Modbus register defines into enums in modbus_server.h
Moved Modbus register definitions from  into enums within . This centralizes register definitions, improves type safety, and enhances code readability.

- : Added  and  to the  of holding registers.
- : Removed the  directive for .
- : Deleted this file as its contents are now integrated into .
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 09:58:51 +02:00
dcbd02ad7a refactor: Rename obstacle current settings in Modbus tool UI
Renamed "Set Obstacle Open" and "Set Obstacle Close" menu options to "Set Obstacle Current Open" and "Set Obstacle Current Close" respectively in . This provides more precise terminology for the obstacle detection current thresholds.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 09:25:19 +02:00
8467b3e347 feat: Make valve obstacle detection parameters configurable via settings and shell
This commit introduces configurable obstacle detection thresholds for the valve, allowing them to be set and persisted via the Zephyr settings subsystem and controlled through the shell and Modbus tool.

- `software/lib/valve/Kconfig`: Added new Kconfig options `VALVE_OBSTACLE_THRESHOLD_OPEN_MA` and `VALVE_OBSTACLE_THRESHOLD_CLOSE_MA` for compile-time configuration and default values.
- `software/include/lib/valve.h`: Removed hardcoded defines and added API functions for setting and getting obstacle thresholds.
- `software/lib/valve/valve.c`:
    - Updated `valve_work_handler` to use the new configurable obstacle thresholds.
    - Integrated loading and saving of obstacle thresholds via the settings subsystem in `valve_init`.
    - Implemented the new setter and getter functions for obstacle thresholds.
    - Updated the `LOG_INF` message in `valve_init` to display the new obstacle threshold values.
- `software/apps/slave_node/prj.conf`: Added default values for the new Kconfig options.
- `software/lib/shell_valve/shell_valve.c`: Added new shell commands `valve set_obstacle_open` and `valve set_obstacle_close` to modify the obstacle thresholds, and updated `valve show` to display them.
- `software/tools/modbus_tool/modbus_tool.py`:
    - Defined new Modbus holding registers (`REG_HOLDING_OBSTACLE_THRESHOLD_OPEN_MA`, `REG_HOLDING_OBSTACLE_THRESHOLD_CLOSE_MA`).
    - Updated `poll_status` to read these new registers.
    - Modified the `main_menu` to include "Set Obstacle Open" and "Set Obstacle Close" options in the settings menu, allowing users to view and modify these parameters.
- `software/lib/modbus_server/modbus_server.c`:
    - Updated `holding_reg_rd` to read the new obstacle threshold registers.
    - Updated `holding_reg_wr` to write to the new obstacle threshold registers.
    - Removed incorrect `REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA` and `REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA` cases from `input_reg_rd`.
- `software/include/lib/modbus_registers.h`: Created a new header file to centralize Modbus register definitions, which were previously hardcoded in `modbus_tool.py`.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 09:15:19 +02:00
fc0add8583 feat: Adjust valve obstacle detection thresholds
Reduced the current thresholds for obstacle detection during valve opening and closing from 500mA to 200mA. This makes the obstacle detection more sensitive.

refactor: Simplify valve_work_handler logic

Refactored the  function to directly call  when an obstacle is detected or the valve reaches its end position. This removes redundant code and improves the clarity of the control flow.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 08:57:02 +02:00
66cdc3ae27 fix: Force UI redraw on successful Modbus reconnection
Implemented a mechanism to force a full UI redraw in the Modbus tool upon successful reconnection to the serial port. The  function now sets a  flag in the shared status data, which is then detected by the  function. Upon detection,  clears the screen and removes the flag, ensuring that any stale error messages are cleared and the UI is fully refreshed.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 08:53:16 +02:00
32bb77926f fix: Reduce flickering in Modbus tool UI over SSH
Replaced  with  and  with  in the  and  functions of . This change optimizes screen updates in the Curses-based UI, which should significantly reduce flickering when running the tool over SSH connections.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 08:14:43 +02:00
4df0181d7f feat: Remove 'Toggle Output' options from Modbus tool
Removed the 'Toggle Output 1' and 'Toggle Output 2' menu options from the Modbus tool's main menu. This simplifies the user interface by removing functionality that is not directly related to the core valve control.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 08:12:50 +02:00
d6fb501594 docs: Add Doxygen comment for movement_timeout_handler
feat: Configure valve logging via Kconfig

This commit adds a Kconfig option  to control the log level of the valve library.
- : Added the new Kconfig option.
- : Updated  to use  and adjusted log levels for debug messages.
- : Enabled debug logging for the valve library by setting .

refactor: Adjust k-vcc calibration value for VND7050AJ

Updated the  calibration value in  from 4139 to 3816 for the VND7050AJ driver.
Signed-off-by: Eduard Iten <eduard@iten.pro>
2025-07-11 08:07:41 +02:00
Your Name
76d0d0647c feat: Implement obstacle detection for valve movement
Implement obstacle detection for valve movement that stops the motor if the current exceeds a predefined threshold during opening or closing.

- :
    - Added new defines  and  with a default value of 500 mA.
- :
    - Modified  function to compare the measured current with the new obstacle thresholds.
    - If the threshold is exceeded, the valve movement is stopped and the status is set to .

Signed-off-by: Your Name <your.email@example.com>
2025-07-11 01:21:41 +02:00
3de42a46c2 refactor: Entferne ungenutzte Funktion
Die Funktion  wurde aus  und  entfernt, da sie im Code nicht mehr verwendet wird. Die Stromwerte werden stattdessen über  und  abgerufen.

Diese Änderung entfernt ungenutzten Code und verbessert die Code-Sauberkeit.
2025-07-11 01:09:46 +02:00
ddaaa8988d feat: Modbus-Register für Endstromschwellenwerte korrigiert
Behebt ein Problem, bei dem das Python-Tool 0 mA für die Endstromschwellenwerte anzeigte.

Die Zephyr-Anwendung definierte zuvor nur ein einzelnes Modbus-Register für den Endstromschwellenwert, während das Python-Tool separate Register für das Öffnen und Schließen erwartete.

Änderungen:
- :
    -  wurde in  umbenannt.
    -  wurde als neues Register hinzugefügt.
- :
    - Implementierung der Lese- und Schreib-Callbacks für  und  unter Verwendung der entsprechenden -Bibliotheksfunktionen.

Diese Änderungen stellen sicher, dass die Zephyr-Anwendung die Endstromschwellenwerte korrekt über Modbus bereitstellt und das Python-Tool diese Werte nun richtig lesen und schreiben kann.
2025-07-11 01:01:45 +02:00
b937c52bcc Revert "feat(valve): Implement obstacle detection with configurable thresholds"
This reverts commit 3c2235733b.
2025-07-11 00:35:19 +02:00
3c2235733b feat(valve): Implement obstacle detection with configurable thresholds
Introduces separate configurable current thresholds for obstacle detection
during valve opening and closing movements.

- Added  state to .
- Added  and
   to .
- Modified  to implement obstacle detection in ,
  setting  on high current, and to load/save
  these new thresholds via settings.
- Added new setter/getter functions for obstacle thresholds to  and .
- Updated  with new shell commands (, )
  and updated  to display these settings.
- Updated  to document the new registers and error states.
- Updated  to include new register definitions, menu options,
  and display of obstacle current thresholds.
2025-07-11 00:27:31 +02:00
a3e8d5c168 refactor(shell): Improve shell command naming and output formatting
- Renamed shell commands in  and  to be shorter
  and remove underscores (e.g.,  to ).
- Consolidated get functions into a single show command for both valve
  and Modbus settings (e.g., , ).
- Adjusted output formatting for show commands to be right-aligned and
  remove horizontal lines for better readability.
- Fixed missing getter function implementations in  and their
  declarations in .
- Ensured  is correctly selected in
  to make valve shell commands available.
2025-07-11 00:16:43 +02:00
5fd904de9e fix(valve): Start movement timer only if timeout is greater than 0
Ensures that the k_timer for valve movement timeouts is only started if
the configured max_opening_time_s or max_closing_time_s is greater than 0.
This prevents unnecessary timer activations when timeouts are disabled or zero.
2025-07-10 23:45:21 +02:00
92bb171e85 feat(valve): Make end-position current thresholds configurable
Introduces separate Modbus holding registers for configurable end-position
current thresholds for both opening and closing valve movements.

- Added REG_HOLDING_VALVE_END_CURRENT_THRESHOLD_OPEN_MA and
  REG_HOLDING_VALVE_END_CURRENT_THRESHOLD_CLOSE_MA to modbus_server.h.
- Modified valve.c to use these new thresholds and save/load them via settings.
- Added new setter functions to valve.h.
- Created new shell_valve library with commands to set/get these thresholds.
- Updated modbus_tool.py to include new menu options for setting thresholds.
- Updated docs/modbus-registers.de.md to document the new registers.

This enhances the flexibility and calibration of the valve control system.
2025-07-10 23:42:41 +02:00
bd8a7a766c style: Apply clang-format to C/C++ source files
Applied consistent code formatting using clang-format to all C/C++ source
and header files in the 'software/' directory.
2025-07-10 23:33:50 +02:00
8f89713866 feat(modbus_tool): Add set functions for max open/close times
Adds new menu options to the Modbus tool to allow setting the maximum
opening and closing times for the valve via Modbus registers.
2025-07-10 23:31:32 +02:00
bf29061db6 feat(slave): Implement VND7050AJ and core valve functionality; 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.
- Implemented VND7050AJ and core functionality including current and voltage measurement and end-position detection.
2025-07-10 21:13:17 +02:00
c1622bb01c Just saving 2025-07-10 17:23:22 +02:00
222ffea568 Refactor VND7050AJ sensor configuration to eliminate redundancy
- Create centralized sensor multiplexer node (vnd7050aj_mux) with shared configuration
- Consolidate ADC channel, GPIO pins, and reference voltage in single location
- Update sensor bindings to reference centralized mux via sensor-mux property
- Add channel-based sensor selection using mux-channel property (0-3)
- Refactor ADC sensor library to use centralized GPIO and channel control
- Update valve library to use new vnd7050aj_mux node reference
- Eliminate duplicate ADC/GPIO definitions between voltage and current sensors
- Ensure configuration consistency and prevent mismatched settings

Benefits:
- Single source of truth for VND7050AJ hardware configuration
- Impossible to have inconsistent GPIO/ADC settings between sensors
- Simplified maintenance and scalability for additional sensors
- Clean channel-based multiplexer selection interface
2025-07-08 17:05:34 +02:00
a9a0626913 Implement real ADC readings with VND7050AJ sensor multiplexing
- Switch from simulated to real ADC readings in adc_sensor library
- Add GPIO control for VND7050AJ sensor selection (sen, s0, s1 pins)
- Implement proper ADC device and channel setup for voltage/current measurements
- Enable ADC driver in prj.conf (CONFIG_ADC=y)
- Disable simulation mode (CONFIG_ADC_SENSOR_SIMULATED=n)
- Add devicetree bindings for custom supply voltage and motor current sensors
- Update overlay with adc_sensors nodes using PB4, PB5, PB6 pins
- Integrate real ADC readings into Modbus server registers
- Support HSE/HSI clock source toggling in overlay configuration
2025-07-08 16:50:27 +02:00
b11f844415 feat: Add ADC sensor device tree bindings and configuration
Introduces device tree bindings for custom ADC voltage and current sensors,
allowing for flexible configuration of sensor inputs and associated GPIOs.
This enables proper hardware abstraction for ADC measurements.

The example overlay file
has been removed as its content is now integrated or superseded by the new
binding definitions.
2025-07-08 16:43:27 +02:00
2e8a86bc54 Added return code when modubs server init fails 2025-07-08 16:08:49 +02:00
224adccf6b testing precommit hook 2025-07-08 16:06:37 +02:00
9b7159d5a4 added formatting 2025-07-08 16:06:11 +02:00
bc327acc41 docs: Add Doxygen comments to library files
Added Doxygen-style comments to all C source and header files in the
 and  directories. This improves
code documentation and enables VSCode tooltip help.

Additionally, short inline comments were added to all global variables
for better clarity.
2025-07-08 15:48:13 +02:00
c9b0f38576 feat(lib): Introduce adc_sensor library
Adds a new `adc_sensor` library to abstract reading analog values from ADC channels. The output of this library is currently simulated.

This library is now used by the `modbus_server` to read the motor current and the main supply voltage, replacing the previous implementation. This change improves modularity by centralizing ADC-related code into a dedicated module.

The build system has been updated to include the new library.
2025-07-08 15:19:44 +02:00
edf0fb2563 feat(slave_node): Add HSI clock configuration and cleanup
Adds a commented-out clock configuration to the  file. This allows switching the clock source from the external high-speed oscillator (HSE) to the internal high-speed oscillator (HSI), which can be useful if an external crystal is not present.

Also, removes the debug log level for the settings subsystem from the project configuration.
2025-07-08 15:06:31 +02:00
537d76ef5d feat(app): Integrate application versioning
This commit introduces application versioning, exposing version information through the Modbus server and logging it at startup.

- Add  to provide version information
- Update  to log the application version at startup
- Update  to expose firmware version via Modbus
- Add file association for  in
2025-07-08 14:41:01 +02:00
116 changed files with 3817 additions and 1276 deletions

64
.gitignore vendored
View File

@@ -1 +1,65 @@
**/build
# Zephyr build directories
build/
build-*/
*/build/
**/build/
# Zephyr out-of-tree build directories
out-of-tree-build/
# Files generated by the build system
zephyr.elf
zephyr.bin
zephyr.hex
zephyr.map
zephyr.strip
zephyr.lst
zephyr.asm
zephyr.stat
zephyr.a
zephyr.o
*.o
*.a
*.so
*.so.*
*.dll
*.exe
# Cmake
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
CTestTestfile.cmake
compile_commands.json
# Kconfig generated files
.config
.config.old
autoconf.h
# Doxygen
doxygen/
# west
.west/
west.yml.bak
# Editor-specific files
.vscode/
.idea/
*.swp
*~
*.bak
*.orig
# Python
__pycache__/
*.pyc
# Mac OS X
.DS_Store
# Windows
Thumbs.db

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "software/modules/zephyr_vnd7050aj_driver"]
path = software/modules/zephyr_vnd7050aj_driver
url = https://gitea.iten.pro/edi/zephyr_vnd7050aj_driver.git

14
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build Zephyr app",
"command": "west build -b weact_stm32g431_core .",
"group": "build",
"problemMatcher": [
"$gcc"
]
}
]
}

View File

@@ -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.

View File

@@ -29,33 +29,38 @@ 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. |
| **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. |
| **0x00F3** | `UPTIME_SECONDS_LOW` | System | Untere 16 Bit der Uptime in Sekunden. |
| **0x00F4** | `UPTIME_SECONDS_HIGH` | System | Obere 16 Bit der Uptime. |
| **0x00F5** | `SUPPLY_VOLTAGE_MV` | System | Aktuelle Versorgungsspannung in Millivolt (mV). |
| **0x0100** | `FWU_LAST_CHUNK_CRC` | Firmware-Update | Enthält den CRC16 des zuletzt im Puffer empfangenen Daten-Chunks. |
| **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** | `REG_INPUT_MOTOR_OPEN_CURRENT_MA` | Ventil | Motorstrom beim Öffnen in Milliampere (mA). |
| **0x0002** | `REG_INPUT_MOTOR_CLOSE_CURRENT_MA` | Ventil | Motorstrom beim Schließen in Milliampere (mA). |
| **0x0020** | `REG_INPUT_DIGITAL_INPUTS_STATE` | Eingänge | Bitmaske der digitalen Eingänge. Bit 0: Eingang 1, Bit 1: Eingang 2. `1`=Aktiv. |
| **0x0021** | `REG_INPUT_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** | `REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR` | System | z.B. `0x0102` für v1.2. |
| **0x00F1** | `REG_INPUT_FIRMWARE_VERSION_PATCH` | System | z.B. `3` für v1.2.3. |
| **0x00F2** | `REG_INPUT_DEVICE_STATUS` | System | `0`=OK, `1`=Allgemeiner Fehler. |
| **0x00F3** | `REG_INPUT_UPTIME_SECONDS_LOW` | System | Untere 16 Bit der Uptime in Sekunden. |
| **0x00F4** | `REG_INPUT_UPTIME_SECONDS_HIGH` | System | Obere 16 Bit der Uptime. |
| **0x00F5** | `REG_INPUT_SUPPLY_VOLTAGE_MV` | System | Aktuelle Versorgungsspannung in Millivolt (mV). |
| **0x0100** | `REG_INPUT_FWU_LAST_CHUNK_CRC` | Firmware-Update | Enthält den CRC16 des zuletzt im Puffer empfangenen Daten-Chunks. |
## 3. Holding Registers (4xxxx, Read/Write)
| 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. |
| **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. |
| **0x0101** | `FWU_CHUNK_OFFSET_LOW` | Firmware-Update | Untere 16 Bit des 32-Bit-Offsets, an den der nächste Chunk geschrieben werden soll. |
| **0x0102** | `FWU_CHUNK_OFFSET_HIGH` | Firmware-Update | Obere 16 Bit des 32-Bit-Offsets. |
| **0x0103** | `FWU_CHUNK_SIZE` | Firmware-Update | Grösse des nächsten Chunks in Bytes (max. 256). |
| **0x0180** | `FWU_DATA_BUFFER` | Firmware-Update | **Startadresse** eines 128x16-bit Puffers (256 Bytes). Entspricht den Registern `40384` bis `40511`. |
| **0x0000** | `REG_HOLDING_VALVE_COMMAND` | Ventil | `1`=Öffnen, `2`=Schliessen, `0`=Bewegung stoppen. |
| **0x0001** | `REG_HOLDING_MAX_OPENING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Öffnen-Vorgang. |
| **0x0002** | `REG_HOLDING_MAX_CLOSING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. |
| **0x0003** | `REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Öffnen. |
| **0x0004** | `REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Schliessen. |
| **0x0005** | `REG_HOLDING_OBSTACLE_THRESHOLD_OPEN_MA` | Ventil | Stromschwellenwert in mA für die Hinderniserkennung beim Öffnen. |
| **0x0006** | `REG_HOLDING_OBSTACLE_THRESHOLD_CLOSE_MA` | Ventil | Stromschwellenwert in mA für die Hinderniserkennung beim Schließen. |
| **0x0010** | `REG_HOLDING_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** | `REG_HOLDING_WATCHDOG_TIMEOUT_S` | System | Timeout des Fail-Safe-Watchdogs in Sekunden. `0`=Deaktiviert. |
| **0x00F1** | `REG_HOLDING_DEVICE_RESET` | System | Schreibt `1` um das Gerät neu zu starten. |
| **0x0100** | `REG_HOLDING_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. |
| **0x0101** | `REG_HOLDING_FWU_CHUNK_OFFSET_LOW` | Firmware-Update | Untere 16 Bit des 32-Bit-Offsets, an den der nächste Chunk geschrieben werden soll. |
| **0x0102** | `REG_HOLDING_FWU_CHUNK_OFFSET_HIGH` | Firmware-Update | Obere 16 Bit des 32-Bit-Offsets. |
| **0x0103** | `REG_HOLDING_FWU_CHUNK_SIZE` | Firmware-Update | Grösse des nächsten Chunks in Bytes (max. 256). |
| **0x0180** | `REG_HOLDING_FWU_DATA_BUFFER` | Firmware-Update | **Startadresse** eines 128x16-bit Puffers (256 Bytes). Entspricht den Registern `40384` bis `40511`. |
## 4. Detaillierter Firmware-Update-Prozess
@@ -80,10 +85,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. |

View File

@@ -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. |

56
setup-format-hook.sh Executable file
View File

@@ -0,0 +1,56 @@
#!/bin/sh
# This script sets up a Git pre-commit hook to automatically format C/C++ files
# in the 'software/' subdirectory using clang-format.
# Define the path for the pre-commit hook
HOOK_DIR=".git/hooks"
HOOK_FILE="$HOOK_DIR/pre-commit"
# Create the hooks directory if it doesn't exist
mkdir -p "$HOOK_DIR"
# Create the pre-commit hook script using a 'here document'
cat > "$HOOK_FILE" << 'EOF'
#!/bin/sh
# --- Pre-commit hook for clang-format ---
#
# This hook formats staged C, C++, and Objective-C files in the 'software/'
# subdirectory before a commit is made.
# It automatically finds the .clang-format file in the software/ directory.
#
# Directory to be formatted
TARGET_DIR="software/"
# Use git diff to find staged files that are Added (A), Copied (C), or Modified (M).
# We filter for files only within the TARGET_DIR.
# The grep regex matches common C/C++ and Objective-C file extensions.
FILES_TO_FORMAT=$(git diff --cached --name-only --diff-filter=ACM "$TARGET_DIR" | grep -E '\.(c|h|cpp|hpp|cxx|hxx|cc|hh|m|mm)$')
if [ -z "$FILES_TO_FORMAT" ]; then
# No relevant files to format, exit successfully.
exit 0
fi
echo " Running clang-format on staged files in '$TARGET_DIR'..."
# Run clang-format in-place on the identified files.
# clang-format will automatically find the .clang-format file in the software/ directory
# or any of its parent directories.
echo "$FILES_TO_FORMAT" | xargs clang-format -i
# Since clang-format may have changed the files, we need to re-stage them.
echo "$FILES_TO_FORMAT" | xargs git add
echo " Formatting complete."
exit 0
EOF
# Make the hook executable
chmod +x "$HOOK_FILE"
echo "✅ Git pre-commit hook has been set up successfully."
echo " It will now automatically format files in the '$PWD/software' directory before each commit."

142
software/.clang-format Normal file
View File

@@ -0,0 +1,142 @@
# Zephyr Project .clang-format configuration
# Based on Linux kernel style with Zephyr-specific adaptations
# Use LLVM as the base style and customize from there
BasedOnStyle: LLVM
# Language settings
Language: Cpp
# Indentation settings
IndentWidth: 8
TabWidth: 8
UseTab: ForIndentation
# Line length
ColumnLimit: 100
# Brace settings
BreakBeforeBraces: Linux
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
# Always add braces for control statements (Zephyr requirement)
RemoveBracesLLVM: false
# Control statement settings
SpaceBeforeParens: ControlStatements
SpacesInParentheses: false
# Function settings
AllowShortFunctionsOnASingleLine: None
AllowShortBlocksOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
# Pointer and reference alignment
PointerAlignment: Right
ReferenceAlignment: Right
# Spacing settings
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInSquareBrackets: false
# Alignment settings
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: false
AlignTrailingComments: false
# Breaking settings
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
# Penalties (used for line breaking decisions)
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
# Comment settings
ReflowComments: true
CommentPragmas: '^ IWYU pragma:'
# Sorting settings
SortIncludes: true
SortUsingDeclarations: true
# Preprocessor settings
IndentPPDirectives: None
MacroBlockBegin: ''
MacroBlockEnd: ''
# Misc settings
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros: ['LISTIFY', 'FOR_EACH', 'FOR_EACH_FIXED_ARG', 'FOR_EACH_IDX', 'FOR_EACH_IDX_FIXED_ARG', 'FOR_EACH_NONEMPTY_TERM', 'Z_FOR_EACH', 'Z_FOR_EACH_FIXED_ARG', 'Z_FOR_EACH_IDX', 'Z_FOR_EACH_IDX_FIXED_ARG']
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<zephyr/.*\.h>'
Priority: 1
- Regex: '^<.*\.h>'
Priority: 2
- Regex: '^<.*'
Priority: 3
- Regex: '.*'
Priority: 4
IndentCaseLabels: false
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true

View File

@@ -1,12 +1,15 @@
{
// Hush CMake
"cmake.configureOnOpen": false,
// IntelliSense
"C_Cpp.default.compilerPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc.exe",
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
// File Associations
"files.associations": {
}
"app_version.h": "c"
},
"C_Cpp.clang_format_style": "file",
"nrf-connect.applications": [
"${workspaceFolder}/apps/slave_node"
],
}

View File

@@ -2,31 +2,19 @@
"version": "2.0.0",
"tasks": [
{
"label": "West Build",
"label": "Format All C/C++ Files",
"type": "shell",
"command": "find . -name \"*.c\" -o -name \"*.h\" | xargs clang-format -i",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
},
"linux": {
"command": "${userHome}/zephyrproject/.venv/bin/west"
},
"windows": {
"command": "${userHome}/zephyrproject/.venv/Scripts/west.exe"
},
"osx": {
"command": "${userHome}/zephyrproject/.venv/bin/west"
},
"args": [
"build",
"-p",
"auto",
"-b",
"valve_node"
],
"problemMatcher": [
"$gcc"
]
"presentation": {
"reveal": "silent",
"clear": true,
"panel": "shared"
}
},
{
"label": "West Configurable Build",

View File

@@ -1,62 +0,0 @@
.. zephyr:code-sample:: adc_dt
:name: Analog-to-Digital Converter (ADC) with devicetree
:relevant-api: adc_interface
Read analog inputs from ADC channels.
Overview
********
This sample demonstrates how to use the :ref:`ADC driver API <adc_api>`.
Depending on the target board, it reads ADC samples from one or more channels
and prints the readings on the console. If voltage of the used reference can
be obtained, the raw readings are converted to millivolts.
The pins of the ADC channels are board-specific. Please refer to the board
or MCU datasheet for further details.
Building and Running
********************
The ADC peripheral and pinmux is configured in the board's ``.dts`` file. Make
sure that the ADC is enabled (``status = "okay";``).
In addition to that, this sample requires an ADC channel specified in the
``io-channels`` property of the ``zephyr,user`` node. This is usually done with
a devicetree overlay. The example overlay in the ``boards`` subdirectory for
the ``nucleo_l073rz`` board can be easily adjusted for other boards.
Configuration of channels (settings like gain, reference, or acquisition time)
also needs to be specified in devicetree, in ADC controller child nodes. Also
the ADC resolution and oversampling setting (if used) need to be specified
there. See :zephyr_file:`boards/nrf52840dk_nrf52840.overlay
<samples/drivers/adc/adc_dt/boards/nrf52840dk_nrf52840.overlay>` for an example of
such setup.
Building and Running for ST Nucleo L073RZ
=========================================
The sample can be built and executed for the
:zephyr:board:`nucleo_l073rz` as follows:
.. zephyr-app-commands::
:zephyr-app: samples/drivers/adc/adc_dt
:board: nucleo_l073rz
:goals: build flash
:compact:
To build for another board, change "nucleo_l073rz" above to that board's name
and provide a corresponding devicetree overlay.
Sample output
=============
You should get a similar output as below, repeated every second:
.. code-block:: console
ADC reading:
- ADC_0, channel 7: 36 = 65mV
.. note:: If the ADC is not supported, the output will be an error message.

View File

@@ -1,38 +0,0 @@
/ {
vdd_sense: voltage-divider {
compatible = "voltage-divider";
/*
* This reference must provide one argument (the channel number)
* because of the "#io-channel-cells = <1>" in the &adc1 node.
*/
io-channels = <&adc1 1>;
output-ohms = <2200>;
full-ohms = <3200>;
};
};
&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>;
/*
* This line is required by the st,stm32-adc driver binding.
* It declares that references to its channels need one extra argument.
*/
#io-channel-cells = <1>;
adc_channel_1: channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -1,4 +0,0 @@
CONFIG_ADC=y
CONFIG_SENSOR=y
CONFIG_VOLTAGE_DIVIDER=y
CONFIG_LOG=y

View File

@@ -1,53 +0,0 @@
sample:
name: ADC devicetree driver sample
tests:
sample.drivers.adc.adc_dt:
tags:
- adc
depends_on: adc
platform_allow:
- nucleo_l073rz
- disco_l475_iot1
- cc3220sf_launchxl
- cc3235sf_launchxl
- cy8cproto_063_ble
- stm32l496g_disco
- stm32h735g_disco
- nrf51dk/nrf51822
- nrf52840dk/nrf52840
- nrf54l15dk/nrf54l15/cpuapp
- nrf54h20dk/nrf54h20/cpuapp
- ophelia4ev/nrf54l15/cpuapp
- mec172xevb_assy6906
- gd32f350r_eval
- gd32f450i_eval
- gd32vf103v_eval
- gd32f403z_eval
- esp32_devkitc/esp32/procpu
- esp32s2_saola
- esp32c3_devkitm
- gd32l233r_eval
- lpcxpresso55s36
- mr_canhubk3
- longan_nano
- longan_nano/gd32vf103/lite
- rd_rw612_bga
- frdm_mcxn947/mcxn947/cpu0
- mcx_n9xx_evk/mcxn947/cpu0
- frdm_mcxc242
- ucans32k1sic
- xg24_rb4187c
- xg29_rb4412a
- raytac_an54l15q_db/nrf54l15/cpuapp
- frdm_mcxa166
- frdm_mcxa276
integration_platforms:
- nucleo_l073rz
- nrf52840dk/nrf52840
harness: console
timeout: 10
harness_config:
type: multi_line
regex:
- "ADC reading\\[\\d+\\]:"
- "- .+, channel \\d+: -?\\d+"

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) 2022 Wolter HV <wolterhv@gmx.de>
*
* SPDX-License-Identifier: Apache-2.0
*/
/ {
zephyr,user {
io-channels = <&adc0 0>;
};
};
&adc0 {
status = "okay";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1_4";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -1,45 +0,0 @@
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(adc_dt_example, LOG_LEVEL_DBG);
/* Get the voltage divider device */
#define VOLTAGE_DIVIDER_NODE DT_NODELABEL(vdd_sense)
int main(void)
{
const struct device *vdd_dev = DEVICE_DT_GET(VOLTAGE_DIVIDER_NODE);
struct sensor_value val;
int err;
if (!device_is_ready(vdd_dev)) {
LOG_ERR("Voltage divider device not ready");
return 0;
}
LOG_INF("Voltage divider device ready!");
while (1) {
err = sensor_sample_fetch(vdd_dev);
if (err < 0) {
LOG_ERR("Could not fetch sample (%d)", err);
k_sleep(K_MSEC(1000));
continue;
}
err = sensor_channel_get(vdd_dev, SENSOR_CHAN_VOLTAGE, &val);
if (err < 0) {
LOG_ERR("Could not get channel (%d)", err);
k_sleep(K_MSEC(1000));
continue;
}
LOG_INF("Voltage reading: %d.%06d V", val.val1, val.val2);
k_sleep(K_MSEC(1000));
}
return 0;
}

View File

@@ -1,6 +0,0 @@
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(adc_test)
target_sources(app PRIVATE src/main.c)

View File

@@ -1,8 +0,0 @@
&adc1 {
pinctrl-0 = <&adc1_in1_pa0>;
pinctrl-names = "default";
status = "okay";
st,adc-clock-source = "SYNC";
st,adc-prescaler = <4>;
};

View File

@@ -1,3 +0,0 @@
CONFIG_ADC=y
CONFIG_ADC_STM32=y
CONFIG_LOG=y

View File

@@ -1,73 +0,0 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/device.h>
#include <zephyr/sys/printk.h>
// ADC-Knoten holen
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
// Kanaldefinitionen
#define MY_SIGNAL_CHANNEL 1 // PA0
#define ADC_VREFINT_CHANNEL 18 // Intern
// Puffer für ZWEI Messwerte
static int16_t sample_buffer[2];
void main(void)
{
int err;
// Die VREFINT-Spannung in mV aus dem Datenblatt deines Controllers
#define VREFINT_MV 1212
printk("*** ADC Ratiometric Measurement (Single Sequence) ***\n");
if (!device_is_ready(adc_dev)) {
printk("ADC device not ready!\n");
return;
}
// --- Einmaliges Setup der beiden Kanäle ---
const struct adc_channel_cfg signal_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL,
.acquisition_time = ADC_ACQ_TIME_DEFAULT, // Kurz für niederohmige Quellen
.channel_id = MY_SIGNAL_CHANNEL,
};
const struct adc_channel_cfg vrefint_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL,
.acquisition_time = ADC_ACQ_TIME_MAX, // Lang für VREFINT
.channel_id = ADC_VREFINT_CHANNEL,
};
adc_channel_setup(adc_dev, &signal_channel_cfg);
adc_channel_setup(adc_dev, &vrefint_channel_cfg);
// --- EINE Sequenz, die BEIDE Kanäle enthält ---
const struct adc_sequence sequence = {
.channels = BIT(MY_SIGNAL_CHANNEL) | BIT(ADC_VREFINT_CHANNEL),
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 12,
};
while (1) {
err = adc_read(adc_dev, &sequence);
if (err != 0) {
printk("ADC read failed with code %d\n", err);
} else {
// Die Ergebnisse sind in der Reihenfolge der Kanalnummern im Puffer
// Kanal 1 (MY_SIGNAL_CHANNEL) kommt vor Kanal 18 (ADC_VREFINT_CHANNEL)
int16_t signal_raw = sample_buffer[0];
int16_t vrefint_raw = sample_buffer[1];
// Ratiometrische Berechnung
int32_t signal_mv = (int32_t)signal_raw * VREFINT_MV / vrefint_raw;
printk("Signal: raw=%4d | VREFINT: raw=%4d | Calculated Voltage: %d mV\n",
signal_raw, vrefint_raw, signal_mv);
}
k_msleep(2000);
}
}

View File

@@ -1,80 +0,0 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/device.h>
// Definiere die Kanäle
#define ADC_VREFINT_CHANNEL 18 // Muss mit dem DTS übereinstimmen
#define MY_SIGNAL_CHANNEL 1 // Muss mit dem pinctrl im DTS übereinstimmen
// ADC Device
static const struct device *adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc1));
// ADC Kanal Konfigurationen
static const struct adc_channel_cfg vrefint_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL, // Bedeutet VDDA
.acquisition_time = ADC_ACQ_TIME_MAX,
.channel_id = ADC_VREFINT_CHANNEL,
.differential = 0,
};
static const struct adc_channel_cfg signal_channel_cfg = {
.gain = ADC_GAIN_1,
.reference = ADC_REF_INTERNAL, // Bedeutet VDDA
.acquisition_time = ADC_ACQ_TIME_MAX,
.channel_id = MY_SIGNAL_CHANNEL,
.differential = 0,
};
// Puffer für die Messwerte
#define BUFFER_SIZE 1
static int16_t sample_buffer[BUFFER_SIZE];
// Sequenz für die Messungen
struct adc_sequence sequence_vrefint = {
.channels = BIT(ADC_VREFINT_CHANNEL),
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 12, // STM32G4 hat 12-bit
};
struct adc_sequence sequence_signal = {
.channels = BIT(MY_SIGNAL_CHANNEL),
.buffer = sample_buffer,
.buffer_size = sizeof(sample_buffer),
.resolution = 12,
};
void main(void) {
if (!device_is_ready(adc_dev)) {
printk("ADC device not found\n");
return;
}
// Kanäle konfigurieren
adc_channel_setup(adc_dev, &vrefint_channel_cfg);
adc_channel_setup(adc_dev, &signal_channel_cfg);
while (1) {
// 1. VREFINT messen zur Kalibrierung
adc_read(adc_dev, &sequence_vrefint);
int16_t vrefint_raw = sample_buffer[0];
// 2. Dein eigentliches Signal messen
adc_read(adc_dev, &sequence_signal);
int16_t signal_raw = sample_buffer[0];
// 3. Spannung berechnen
// VREFINT Wert für STM32G431 bei 3.0V Vdda ist typ. 1.212V (1212 mV)
// Überprüfe den genauen Wert im Datenblatt für deinen Controller!
#define VREFINT_MV 1212
int32_t signal_mv = (int32_t)signal_raw * VREFINT_MV / vrefint_raw;
printk("VREFINT raw: %d, Signal raw: %d, Calculated Voltage: %d mV\n",
vrefint_raw, signal_raw, signal_mv);
k_msleep(1000);
}
}

View File

@@ -1,38 +0,0 @@
#include <zephyr.h>
#include <drivers/adc.h>
#define PA0_PIN 0x04
#define ADC_CHANNEL 0x03
int main(void) {
int16_t adc_value = 0;
// Initialize the ADC
adc_config_t adc_config;
adc_config.mode = ADC_MODE_SINGLE_SHOT;
adc_config.channel = ADC_CHANNEL_PA0;
adc_config.sampling_rate = ADC_SAMP_RATE_1MS;
adc_config.data_rate = ADC_DATA_RATE_4MS;
adc_config.aux = ADC_AUX_ALL;
adc_config.atten = ADC_ATTEN_DB_11;
adc_config.ref = ADC_REF_INTERNAL;
adc_config.cal = ADC_CAL_ALL;
if (adc_config_data(&adc_config, &adc_context) < 0) {
zephyr_printf("Failed to configure ADC\n");
return -1;
}
// Read the analog input value
if (adc_read(&adc_context, &adc_value) < 0) {
zephyr_printf("Failed to read ADC value\n");
return -1;
}
zephyr_printf("ADC Value: %d\n", adc_value);
return 0;
}

View File

@@ -1,8 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ADC)
project(bl_test)
# Add application source files
target_sources(app PRIVATE src/main.c)

View File

@@ -0,0 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 0
PATCHLEVEL = 1
VERSION_TWEAK = 1
EXTRAVERSION = devel

View File

@@ -0,0 +1,46 @@
# Enable Console and printk for logging via UART
CONFIG_CONSOLE=y
CONFIG_LOG=y
CONFIG_UART_CONSOLE=y
# Enable more detailed MCUMGR logging
CONFIG_MCUMGR_LOG_LEVEL_DBG=y
CONFIG_IMG_MANAGER_LOG_LEVEL_DBG=y
CONFIG_STREAM_FLASH_LOG_LEVEL_DBG=y
# Enable USB for MCUMGR only
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_CDC_ACM=y
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y
# USB CDC ACM buffer configuration for better MCUMGR performance
CONFIG_USB_CDC_ACM_RINGBUF_SIZE=1024
# Set log level to info for reasonable size
CONFIG_LOG_DEFAULT_LEVEL=3
# Enable MCUMGR info logging (not debug to save space)
CONFIG_MCUMGR_LOG_LEVEL_INF=y
# Enable USB CDC info logging
CONFIG_USB_CDC_ACM_LOG_LEVEL_INF=y
# STEP 5.2 - Enable mcumgr DFU in application
# Enable MCUMGR
CONFIG_MCUMGR=y # Enable MCUMGR management for both OS and Images
CONFIG_MCUMGR_GRP_OS=y
CONFIG_MCUMGR_GRP_IMG=y
# Configure MCUMGR transport to UART (will use USB-CDC via chosen device)
CONFIG_MCUMGR_TRANSPORT_UART=y
# Dependencies
# Configure dependencies for CONFIG_MCUMGR
CONFIG_NET_BUF=y
CONFIG_ZCBOR=y
CONFIG_CRC=y # Configure dependencies for CONFIG_MCUMGR_GRP_IMG
CONFIG_FLASH=y
CONFIG_IMG_MANAGER=y # Configure dependencies for CONFIG_IMG_MANAGER
CONFIG_STREAM_FLASH=y
CONFIG_FLASH_MAP=y # Configure dependencies for CONFIG_MCUMGR_TRANSPORT_USB_CDC
CONFIG_BASE64=y

View File

@@ -0,0 +1,11 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <app_version.h>
LOG_MODULE_REGISTER(bl_test_app, LOG_LEVEL_INF);
int main(void)
{
LOG_INF("Hello World from bl_test! This is version %s", APP_VERSION_EXTENDED_STRING);
return 0;
}

View File

@@ -0,0 +1 @@
SB_CONFIG_BOOTLOADER_MCUBOOT=y

View File

@@ -0,0 +1,9 @@
#include "common.dtsi"
/* Application Configuration - Firmware wird in slot0_partition geschrieben */
/ {
chosen {
zephyr,code-partition = &slot0_partition;
zephyr,uart-mcumgr = &cdc_acm_uart0;
};
};

View File

@@ -0,0 +1,94 @@
/*
* Common Devicetree Configuration für weact_stm32g431_core
* - Konfiguriert einen W25Q128 Flash-Speicher auf SPI2
* - Konfiguriert USB-CDC für MCUMGR
* - Setzt den Chip Select (CS) Pin auf PA5
* - Weist das Label "flash1" zu
*/
/* Partitions für internes Flash (STM32G431) */
&flash0 {
/delete-node/ partitions; /* Entferne die Standard-Partitionen */
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* MCUboot bootloader - 48 KB */
boot_partition: partition@0 {
label = "mcuboot";
reg = <0x00000000 0x0000C000>;
};
/* Slot0 partition für primäres Application Image - 80 KB (20 sectors @ 4KB) */
slot0_partition: partition@C000 {
label = "image-0";
reg = <0x0000C000 0x00014000>;
};
};
};
/* USB-CDC Konfiguration für MCUMGR */
&usb {
status = "okay";
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
};
};
/ {
chosen {
zephyr,uart-mcumgr = &cdc_acm_uart0;
};
};
&spi2 {
/* Definiere die Pins für SCK, MISO, MOSI auf Port B */
pinctrl-0 = <&spi2_sck_pb13 &spi2_miso_pb14 &spi2_mosi_pb15>;
pinctrl-names = "default";
status = "okay";
/* === Chip Select (CS) auf PA5 gesetzt === */
cs-gpios = <&gpioa 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
/* Definiere den Flash-Chip als SPI NOR Gerät */
flash1: flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
label = "flash1";
/* JEDEC ID für einen Winbond W25Q128 (16 MBytes) */
jedec-id = [ef 40 18];
/* Speichergröße in Bytes (16 MBytes) */
size = <DT_SIZE_M(16)>;
/* Maximale Taktfrequenz - angepasst an STM32G431 Limits */
spi-max-frequency = <1000000>;
/* Partitions für externes Flash */
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Slot1 partition für MCUboot (sekundäres Image) - 80 KB (20 sectors @ 4KB) */
slot1_partition: partition@0 {
label = "image-1";
reg = <0x00000000 0x00014000>;
};
/* Scratch partition für MCUboot - 80 KB (20 sectors @ 4KB) */
scratch_partition: partition@14000 {
label = "scratch";
reg = <0x00014000 0x00014000>;
};
/* Speicher partition für LittleFS - ~15.83 MB */
storage_partition: partition@28000 {
label = "storage";
reg = <0x00028000 0x00FD8000>;
};
};
};
};

View File

@@ -0,0 +1,23 @@
CONFIG_LOG=y
CONFIG_MCUBOOT_LOG_LEVEL_INF=y
# Enable UART console for MCUboot debug output
CONFIG_UART_CONSOLE=y
CONFIG_CONSOLE=y
CONFIG_MCUBOOT_INDICATION_LED=y
# Enable external SPI flash support
CONFIG_SPI=y
CONFIG_SPI_NOR=y
CONFIG_SPI_NOR_SFDP_DEVICETREE=n
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_GPIO=y
# Add SPI NOR specific configurations - use 4KB page size (required by driver)
CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096
CONFIG_SPI_NOR_INIT_PRIORITY=80
# Set maximum image sectors manually since auto doesn't work with external flash
CONFIG_BOOT_MAX_IMG_SECTORS_AUTO=n
CONFIG_BOOT_MAX_IMG_SECTORS=80

View File

@@ -0,0 +1,8 @@
#include "common.dtsi"
/* MCUboot Configuration - Bootloader wird in boot_partition geschrieben */
/ {
chosen {
zephyr,code-partition = &boot_partition;
};
};

View File

@@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(firmware_node LANGUAGES C)
project(can_node LANGUAGES C)
zephyr_include_directories(../../include)
add_subdirectory(../../lib lib)
target_sources(app PRIVATE src/main.c)

View File

@@ -0,0 +1,2 @@
rsource "../../lib/Kconfig"
source "Kconfig.zephyr"

View File

@@ -0,0 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 0
PATCHLEVEL = 1
VERSION_TWEAK = 1
EXTRAVERSION = devel

View File

@@ -0,0 +1,7 @@
# Disable UART console
CONFIG_UART_CONSOLE=n
# Enable RTT console
CONFIG_RTT_CONSOLE=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_SHELL_BACKEND_RTT=y

View File

@@ -0,0 +1,43 @@
/ {
chosen {
zephyr,console = &rtt;
zephyr,shell = &rtt;
zephyr,settings-partition = &storage_partition;
};
rtt: rtt {
compatible = "segger,rtt-uart";
#address-cells = <1>;
#size-cells = <0>;
label = "RTT";
status = "okay";
};
};
&flash0 {
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* Application partition starts at the beginning of flash */
slot0_partition: partition@0 {
label = "image-0";
reg = <0x00000000 DT_SIZE_K(120)>;
};
/* Use the last 8K for settings */
storage_partition: partition@1E000 {
label = "storage";
reg = <0x0001E000 DT_SIZE_K(8)>;
};
};
};
&usart1 {
modbus0 {
compatible = "zephyr,modbus-serial";
status = "okay";
};
status = "okay";
};

View File

@@ -0,0 +1,47 @@
/ {
aliases {
vnd7050aj = &vnd7050aj;
};
vnd7050aj: vnd7050aj {
compatible = "st,vnd7050aj";
status = "okay";
input0-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
input1-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
select0-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
select1-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
sense-enable-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
fault-reset-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
io-channels = <&adc0 0>;
r-sense-ohms = <1500>;
k-vcc = <4000>;
};
modbus_uart: uart_2 {
compatible = "zephyr,native-pty-uart";
status = "okay";
current-speed = <19200>;
modbus0: modbus0 {
compatible = "zephyr,modbus-serial";
status = "okay";
};
};
};
&adc0 {
#address-cells = <1>;
#size-cells = <0>;
ref-internal-mv = <3300>;
ref-external1-mv = <5000>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -0,0 +1,48 @@
/ {
aliases {
vnd7050aj = &vnd7050aj;
};
vnd7050aj: vnd7050aj {
compatible = "st,vnd7050aj";
status = "okay";
input0-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>;
input1-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>;
select0-gpios = <&gpiob 0 GPIO_ACTIVE_HIGH>;
select1-gpios = <&gpiob 1 GPIO_ACTIVE_HIGH>;
sense-enable-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>;
fault-reset-gpios = <&gpiob 5 GPIO_ACTIVE_LOW>;
io-channels = <&adc1 1>;
r-sense-ohms = <1500>;
k-vcc = <4000>;
};
};
&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 = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};
&usart1 {
modbus0 {
compatible = "zephyr,modbus-serial";
status = "okay";
};
status = "okay";
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>; // PA9=TX, PA10=RX for Modbus communication
pinctrl-names = "default";
};

View File

@@ -0,0 +1,16 @@
#include <zephyr/dt-bindings/gpio/gpio.h>
&zephyr_udc0 {
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";
modbus0 {
compatible = "zephyr,modbus-serial";
status = "okay";
};
};
};
&usart1 {
/delete-node/ modbus0;
};

View File

@@ -0,0 +1,88 @@
# Copyright (c) 2024, Eduard Iten
# SPDX-License-Identifier: Apache-2.0
description: |
STMicroelectronics VND7050AJ dual-channel high-side driver.
This is a GPIO and ADC controlled device.
compatible: "st,vnd7050aj"
include: base.yaml
properties:
input0-gpios:
type: phandle-array
required: true
description: GPIO to control output channel 0.
input1-gpios:
type: phandle-array
required: true
description: GPIO to control output channel 1.
select0-gpios:
type: phandle-array
required: true
description: GPIO for MultiSense selection bit 0.
select1-gpios:
type: phandle-array
required: true
description: GPIO for MultiSense selection bit 1.
sense-enable-gpios:
type: phandle-array
required: true
description: GPIO to enable the MultiSense output.
fault-reset-gpios:
type: phandle-array
required: true
description: GPIO to reset a latched fault (active-low).
io-channels:
type: phandle-array
required: true
description: |
ADC channel connected to the MultiSense pin. This should be an
io-channels property pointing to the ADC controller and channel number.
r-sense-ohms:
type: int
required: true
description: |
Value of the external sense resistor connected from the MultiSense
pin to GND, specified in Ohms. This is critical for correct
conversion of the analog readings.
k-factor:
type: int
default: 1500
description: |
Factor between PowerMOS and SenseMOS.
k-vcc:
type: int
default: 8000
description: |
VCC sense ratio multiplied by 1000. Used for supply voltage calculation.
t-sense-0:
type: int
default: 25
description: |
Temperature sense reference temperature in degrees Celsius.
v-sense-0:
type: int
default: 2070
description: |
Temperature sense reference voltage in millivolts.
k-tchip:
type: int
default: -5500
description: |
Temperature sense gain coefficient multiplied by 1000.
Used for chip temperature calculation.

View File

@@ -0,0 +1,4 @@
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_PRODUCT="Modbus slave node"
CONFIG_UART_LINE_CTRL=y
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n

View File

@@ -0,0 +1,31 @@
# Enable Console and printk for logging
CONFIG_CONSOLE=y
CONFIG_LOG=y
# Enable Shell
CONFIG_SHELL=y
CONFIG_REBOOT=y
CONFIG_SHELL_MODBUS=n
CONFIG_SHELL_VALVE=y
CONFIG_SHELL_SYSTEM=y
# Enable Settings Subsystem
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS=y
CONFIG_NVS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FLASH_PAGE_LAYOUT=y
# Config modbus
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_MODBUS=y
CONFIG_MODBUS_ROLE_SERVER=y
CONFIG_MODBUS_LOG_LEVEL_DBG=y
# enable Valve Driver
CONFIG_LIB_VALVE=y
CONFIG_LOG_VALVE_LEVEL=4
# Enable VND7050AJ
CONFIG_VND7050AJ=y

View File

@@ -0,0 +1,40 @@
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <app_version.h>
#include <lib/valve.h>
#include <lib/vnd7050aj.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
int main(void)
{
int rc;
LOG_INF("Starting Irrigation System CAN Node, Version %s", APP_VERSION_EXTENDED_STRING);
/* Initialize settings subsystem */
rc = settings_subsys_init();
if (rc != 0) {
LOG_ERR("Failed to initialize settings subsystem (%d)", rc);
return rc;
}
LOG_INF("Settings subsystem initialized");
/* Load settings from storage */
rc = settings_load();
if (rc == 0) {
LOG_INF("Settings loaded successfully");
} else {
LOG_WRN("Failed to load settings (%d)", rc);
}
/* Initialize valve system */
rc = valve_init();
if (rc != 0) {
LOG_ERR("Failed to initialize valve system (%d)", rc);
return rc;
}
LOG_INF("Valve system initialized");
return 0;
}

View File

@@ -0,0 +1,5 @@
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y
CONFIG_LOG=y
CONFIG_MCUBOOT_LOG_LEVEL_INF=y

View File

@@ -1,34 +0,0 @@
# Firmware Node Application
This Zephyr application provides firmware management capabilities for the irrigation system.
**Tested on Zephyr 4.1.99**
## Features
### Step 1: Shell with Reset Command
- Shell interface with custom "reset" command
- Warm reboot functionality
### Planned Features
- MCUboot support with partition manager
- Firmware version display
- MCUmgr support for OTA updates
## Building
```bash
west build -p auto -b weact_stm32g431_core apps/firmware_node -- -DBOARD_FLASH_RUNNER=blackmagicprobe
```
## Flashing
```bash
west flash
```
## Usage
Connect to the device via serial console and use the shell:
- `reset` - Reboot the system
- `help` - Show available commands

View File

@@ -1,2 +0,0 @@
# Board specific configuration for weact_stm32g431_core
# This file can be used for board-specific overrides if needed

View File

@@ -1,10 +0,0 @@
# Enable Console and printk for logging
CONFIG_CONSOLE=y
CONFIG_LOG=y
# Enable Shell
CONFIG_SHELL=y
CONFIG_REBOOT=y
# Enable the reset command
CONFIG_KERNEL_SHELL=y

View File

@@ -1,28 +0,0 @@
#include <zephyr/kernel.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(firmware_node, LOG_LEVEL_DBG);
static int cmd_reset(const struct shell *shell, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
shell_print(shell, "Rebooting system...");
k_msleep(100); // Give time for message to be sent
sys_reboot(SYS_REBOOT_WARM);
return 0;
}
SHELL_CMD_REGISTER(reset, NULL, "Reset the system", cmd_reset);
int main(void)
{
LOG_INF("Firmware Node starting up");
LOG_INF("Shell with reset command available");
return 0;
}

View File

@@ -1,12 +0,0 @@
# Enable MCUboot
set(SB_CONFIG_BOOTLOADER_MCUBOOT y)
# MCUboot configuration
set(SB_CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY y)
set(SB_CONFIG_MCUBOOT_SIGNATURE_KEY_FILE "bootloader/mcuboot/root-rsa-2048.pem")
set(SB_CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE y)
set(SB_CONFIG_MCUBOOT_GENERATE_CONFIRMED_IMAGE y)
# Enable USB CDC ACM for MCUboot console
set(SB_CONFIG_MCUBOOT_USB_SUPPORT y)
set(SB_CONFIG_MCUBOOT_SERIAL y)

View File

@@ -1,9 +1,8 @@
cmake_minimum_required(VERSION 3.20)
# Include the main 'software' directory as a module to find boards, libs, etc.
list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../..)
cmake_minimum_required(VERSION 3.20.5)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(gateway)
project(gateway)
target_sources(app PRIVATE src/main.c)
target_include_directories(app PRIVATE include)

View File

@@ -0,0 +1,44 @@
# README for the Hello World Zephyr Application
## Overview
This is a minimal Hello World application built using the Zephyr RTOS. The application demonstrates basic logging functionality by printing a message every 5 seconds, including the version number of the application.
## Project Structure
The project consists of the following files:
- `src/main.c`: The entry point of the application that initializes logging and sets up a timer.
- `include/app_version.h`: Header file that defines the application version.
- `VERSION`: A text file containing the version number of the application.
- `prj.conf`: Configuration file for the Zephyr project, specifying necessary options.
- `CMakeLists.txt`: Build configuration file for CMake.
- `README.md`: Documentation for the project.
## Building the Application
To build the application, follow these steps:
1. Ensure you have the Zephyr development environment set up.
2. Navigate to the `apps/gateway` directory.
3. Run the following command to build the application:
```
west build -b <your_board> .
```
Replace `<your_board>` with the appropriate board name.
## Running the Application
After building the application, you can flash it to your board using:
```
west flash
```
Once the application is running, you will see log messages printed every 5 seconds, including the version number.
## Version
The version of this application can be found in the `VERSION` file and is also included in the log messages.

View File

@@ -0,0 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 0
PATCHLEVEL = 1
VERSION_TWEAK = 0
EXTRAVERSION = devel

View File

@@ -0,0 +1,16 @@
&flash0 {
reg = <0x0 0x400000>; /* 4MB flash */
};
#include "espressif/partitions_0x0_default_4M.dtsi"
/ {
chosen {
zephyr,shell-uart = &uart0;
zephyr,uart-mcumgr = &usb_serial;
};
};
&usb_serial {
status = "okay";
};

View File

@@ -0,0 +1 @@
#include "common_4MB.dtsi"

View File

@@ -1,2 +1,47 @@
# Gateway Configuration
CONFIG_NETWORKING=y
# -------------------
# Logging and Console
# -------------------
CONFIG_LOG=y
CONFIG_UART_CONSOLE=y
# -------------
# Zephyr Shell
# -------------
CONFIG_SHELL=y
CONFIG_KERNEL_SHELL=y
CONFIG_REBOOT=y
# -------------------
# MCUmgr OS Management
# -------------------
CONFIG_MCUMGR=y
CONFIG_MCUMGR_GRP_OS=y
CONFIG_MCUMGR_TRANSPORT_UART=y
# -------------------
# MCUmgr Filesystem Group
# -------------------
CONFIG_MCUMGR_GRP_FS=y
# -------------------
# LittleFS and Flash
# -------------------
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_LITTLEFS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
# -------------------
# Settings Subsystem
# -------------------
CONFIG_SETTINGS=y
CONFIG_SETTINGS_FILE=y
CONFIG_SETTINGS_FILE_PATH="/lfs/settings.bin"
# -------------------
# Dependencies
# -------------------
CONFIG_NET_BUF=y
CONFIG_ZCBOR=y
CONFIG_CRC=y
CONFIG_BASE64=y

View File

@@ -1,13 +1,136 @@
/*
* Copyright (c) 2025 Eduard Iten
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/fs/fs.h>
#include <zephyr/fs/littlefs.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <zephyr/shell/shell.h>
#include <app_version.h>
#include <string.h>
LOG_MODULE_REGISTER(hello_world);
/* LittleFS mount configuration for 'storage_partition' partition */
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(storage_partition);
static struct fs_mount_t littlefs_mnt = {
.type = FS_LITTLEFS,
.mnt_point = "/lfs",
.fs_data = &storage_partition, // default config macro
.storage_dev = (void *)FIXED_PARTITION_ID(storage_partition),
};
static char my_setting[32] = "default";
static int my_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
{
if (strcmp(name, "value") == 0) {
if (len > sizeof(my_setting) - 1) {
len = sizeof(my_setting) - 1;
}
if (read_cb(cb_arg, my_setting, len) == len) {
my_setting[len] = '\0';
return 0;
}
return -EINVAL;
}
return -ENOENT;
}
static int my_settings_export(int (*export_func)(const char *, const void *, size_t))
{
return export_func("my/setting/value", my_setting, strlen(my_setting));
}
SETTINGS_STATIC_HANDLER_DEFINE(my, "my/setting", NULL, my_settings_set, NULL, my_settings_export);
static int cmd_my_get(const struct shell *shell, size_t argc, char **argv)
{
shell_print(shell, "my_setting = '%s'", my_setting);
return 0;
}
static int cmd_my_reset(const struct shell *shell, size_t argc, char **argv)
{
strcpy(my_setting, "default");
settings_save();
shell_print(shell, "my_setting reset to default");
return 0;
}
// Improved set command: join all arguments for whitespace support
static int cmd_my_set(const struct shell *shell, size_t argc, char **argv)
{
if (argc < 2) {
shell_error(shell, "Usage: my set <value>");
return -EINVAL;
}
// Join all argv[1..] with spaces
size_t i, pos = 0;
my_setting[0] = '\0';
for (i = 1; i < argc; ++i) {
size_t left = sizeof(my_setting) - 1 - pos;
if (left == 0)
break;
strncat(my_setting, argv[i], left);
pos = strlen(my_setting);
if (i < argc - 1 && left > 1) {
strncat(my_setting, " ", left - 1);
pos = strlen(my_setting);
}
}
my_setting[sizeof(my_setting) - 1] = '\0';
settings_save();
shell_print(shell, "my_setting set to '%s'", my_setting);
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(my_subcmds,
SHELL_CMD(get, NULL, "Get my_setting", cmd_my_get),
SHELL_CMD(set, NULL, "Set my_setting (supports spaces)", cmd_my_set),
SHELL_CMD(reset, NULL, "Reset my_setting to default and compact settings file", cmd_my_reset),
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(my, &my_subcmds, "My settings commands", NULL);
static void compact_settings_file(void)
{
struct fs_file_t file;
fs_file_t_init(&file);
int rc = fs_open(&file, "/lfs/settings.bin", FS_O_WRITE | FS_O_CREATE | FS_O_TRUNC);
if (rc == 0) {
fs_close(&file);
LOG_INF("Settings file compacted (truncated and recreated)");
} else if (rc == -ENOENT) {
LOG_INF("Settings file did not exist, created new");
} else {
LOG_ERR("Failed to compact settings file (%d)", rc);
}
settings_save();
}
int main(void)
{
printk("Hello from Gateway!\n");
int rc = fs_mount(&littlefs_mnt);
if (rc < 0) {
LOG_ERR("Error mounting LittleFS [%d]", rc);
} else {
LOG_INF("LittleFS mounted at /lfs");
}
/* Initialize settings subsystem */
settings_subsys_init();
LOG_INF("Settings subsystem initialized");
/* Load settings from storage */
rc = settings_load();
if (rc == 0) {
LOG_INF("Settings loaded: my_setting='%s'", my_setting);
} else {
LOG_ERR("Failed to load settings (%d)", rc);
}
/* Compact settings file on each start */
compact_settings_file();
LOG_INF("Hello World! Version: %s", APP_VERSION_EXTENDED_STRING);
return 0;
}

View File

@@ -0,0 +1,2 @@
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y

View File

@@ -0,0 +1,8 @@
#include "../boards/common_4MB.dtsi"
/* Application Configuration - Firmware goes to slot0_partition (0x20000) */
/ {
chosen {
zephyr,code-partition = &slot0_partition;
};
};

View File

@@ -0,0 +1,3 @@
CONFIG_LOG=y
CONFIG_MCUBOOT_LOG_LEVEL_INF=y
CONFIG_UART_CONSOLE=n

View File

@@ -0,0 +1,12 @@
#include "../boards/common_4MB.dtsi"
/* MCUboot Configuration - Bootloader goes to boot_partition (0x0) */
/ {
chosen {
zephyr,code-partition = &boot_partition;
};
aliases {
mcuboot-button0 = &user_button1;
};
};

Submodule software/apps/mqtt_gateway added at 6e669cfc4e

View File

@@ -0,0 +1,12 @@
{
"configurations": [
{
"name": "Linux",
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"cStandard": "c99",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-arm"
}
],
"version": 4
}

View File

@@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(slave_node LANGUAGES C)
zephyr_include_directories(../../include)
add_subdirectory(../../lib lib)
target_sources(app PRIVATE src/main.c)

View File

@@ -0,0 +1,5 @@
VERSION_MAJOR = 0
VERSION_MINOR = 0
PATCHLEVEL = 1
VERSION_TWEAK = 1
EXTRAVERSION = devel

View File

@@ -0,0 +1,47 @@
/ {
aliases {
vnd7050aj = &vnd7050aj;
};
vnd7050aj: vnd7050aj {
compatible = "st,vnd7050aj";
status = "okay";
input0-gpios = <&gpio0 1 GPIO_ACTIVE_HIGH>;
input1-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
select0-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
select1-gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
sense-enable-gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
fault-reset-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
io-channels = <&adc0 0>;
r-sense-ohms = <1500>;
k-vcc = <4000>;
};
modbus_uart: uart_2 {
compatible = "zephyr,native-pty-uart";
status = "okay";
current-speed = <19200>;
modbus0: modbus0 {
compatible = "zephyr,modbus-serial";
status = "okay";
};
};
};
&adc0 {
#address-cells = <1>;
#size-cells = <0>;
ref-internal-mv = <3300>;
ref-external1-mv = <5000>;
channel@0 {
reg = <0>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};

View File

@@ -1,15 +1,39 @@
/ {
aliases {
vnd7050aj = &vnd7050aj;
};
vnd7050aj: vnd7050aj {
compatible = "vnd7050aj-valve-controller";
compatible = "st,vnd7050aj";
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
input0-gpios = <&gpiob 3 GPIO_ACTIVE_HIGH>;
input1-gpios = <&gpiob 4 GPIO_ACTIVE_HIGH>;
select0-gpios = <&gpiob 7 GPIO_ACTIVE_HIGH>;
select1-gpios = <&gpiob 9 GPIO_ACTIVE_HIGH>;
sense-enable-gpios = <&gpiob 6 GPIO_ACTIVE_HIGH>;
fault-reset-gpios = <&gpiob 5 GPIO_ACTIVE_LOW>;
io-channels = <&adc1 1>;
r-sense-ohms = <1500>;
k-vcc = <4000>;
};
};
&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 = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
};
};
@@ -22,43 +46,3 @@
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>; // PA9=TX, PA10=RX for Modbus communication
pinctrl-names = "default";
};
&adc1 {
status = "okay";
pinctrl-0 = <&adc1_in1_pa0 &adc1_in15_pb0>;
pinctrl-names = "default";
st,adc-clock-source = "SYNC";
st,adc-prescaler = <1>;
#address-cells = <1>;
#size-cells = <0>;
channel@1 {
reg = <1>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>; // Use maximum acquisition time for stability
zephyr,resolution = <12>;
zephyr,vref-mv = <2048>; // STM32G431 VREFBUF at 2.048V
};
channel@15 {
reg = <15>;
zephyr,gain = "ADC_GAIN_1";
zephyr,reference = "ADC_REF_INTERNAL";
zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
zephyr,resolution = <12>;
zephyr,vref-mv = <2048>; // STM32G431 VREFBUF at 2.048V
};
};
&pinctrl {
// Pinmux für PA0 als ADC1_IN1 (Analogmodus)
adc1_in1_pa0: adc1_in1_pa0 {
pinmux = <STM32_PINMUX('A', 0, ANALOG)>; // PA0 in den Analogmodus setzen
};
// Pinmux für PB0 als ADC1_IN15 (Analogmodus) - for lab supply testing
adc1_in15_pb0: adc1_in15_pb0 {
pinmux = <STM32_PINMUX('B', 0, ANALOG)>; // PB0 in den Analogmodus setzen
};
};

View File

@@ -1,3 +1,5 @@
#include <zephyr/dt-bindings/gpio/gpio.h>
&zephyr_udc0 {
cdc_acm_uart0: cdc_acm_uart0 {
compatible = "zephyr,cdc-acm-uart";

View File

@@ -0,0 +1,88 @@
# Copyright (c) 2024, Eduard Iten
# SPDX-License-Identifier: Apache-2.0
description: |
STMicroelectronics VND7050AJ dual-channel high-side driver.
This is a GPIO and ADC controlled device.
compatible: "st,vnd7050aj"
include: base.yaml
properties:
input0-gpios:
type: phandle-array
required: true
description: GPIO to control output channel 0.
input1-gpios:
type: phandle-array
required: true
description: GPIO to control output channel 1.
select0-gpios:
type: phandle-array
required: true
description: GPIO for MultiSense selection bit 0.
select1-gpios:
type: phandle-array
required: true
description: GPIO for MultiSense selection bit 1.
sense-enable-gpios:
type: phandle-array
required: true
description: GPIO to enable the MultiSense output.
fault-reset-gpios:
type: phandle-array
required: true
description: GPIO to reset a latched fault (active-low).
io-channels:
type: phandle-array
required: true
description: |
ADC channel connected to the MultiSense pin. This should be an
io-channels property pointing to the ADC controller and channel number.
r-sense-ohms:
type: int
required: true
description: |
Value of the external sense resistor connected from the MultiSense
pin to GND, specified in Ohms. This is critical for correct
conversion of the analog readings.
k-factor:
type: int
default: 1500
description: |
Factor between PowerMOS and SenseMOS.
k-vcc:
type: int
default: 8000
description: |
VCC sense ratio multiplied by 1000. Used for supply voltage calculation.
t-sense-0:
type: int
default: 25
description: |
Temperature sense reference temperature in degrees Celsius.
v-sense-0:
type: int
default: 2070
description: |
Temperature sense reference voltage in millivolts.
k-tchip:
type: int
default: -5500
description: |
Temperature sense gain coefficient multiplied by 1000.
Used for chip temperature calculation.

View File

@@ -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 select 0 pin
required: true
s1-gpios:
type: phandle-array
description: GPIO for select 1 pin
required: true

View File

@@ -5,6 +5,9 @@ CONFIG_LOG=y
# Enable Shell
CONFIG_SHELL=y
CONFIG_REBOOT=y
CONFIG_SHELL_MODBUS=y
CONFIG_SHELL_VALVE=y
CONFIG_SHELL_SYSTEM=y
# Enable Settings Subsystem
CONFIG_SETTINGS=y
@@ -19,9 +22,7 @@ CONFIG_SETTINGS_LOG_LEVEL_DBG=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_MODBUS=y
CONFIG_MODBUS_ROLE_SERVER=y
CONFIG_MODBUS_BUFFER_SIZE=256
# Enable ADC driver
CONFIG_ADC=y
CONFIG_ADC_STM32=y
CONFIG_MODBUS_LOG_LEVEL_DBG=y
# Enable VND7050AJ
CONFIG_VND7050AJ=y

View File

@@ -1,14 +1,15 @@
#include <zephyr/kernel.h>
#include <zephyr/settings/settings.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <lib/fwu.h>
#include <lib/modbus_server.h>
#include <lib/valve.h>
#include <lib/fwu.h>
LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
int main(void)
{
int rc;
LOG_INF("Starting Irrigation System Slave Node");
if (settings_subsys_init() || settings_load()) {
@@ -18,16 +19,10 @@ int main(void)
valve_init();
fwu_init();
if (modbus_server_init()) {
LOG_ERR("Modbus RTU server initialization failed");
return 0;
}
// Test supply voltage reading periodically
while (1) {
uint16_t supply_voltage = valve_get_supply_voltage();
LOG_INF("Supply voltage: %u mV", supply_voltage);
k_msleep(5000); // Read every 5 seconds
rc = modbus_server_init();
if (rc) {
LOG_ERR("Modbus server initialization failed: %d", rc);
return rc;
}
LOG_INF("Irrigation System Slave Node started successfully");

View File

@@ -0,0 +1,5 @@
SB_CONFIG_BOOTLOADER_MCUBOOT=y
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y
CONFIG_LOG=y
CONFIG_MCUBOOT_LOG_LEVEL_INF=y

5
software/esphome/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
# Gitignore settings for ESPHome
# This is an example and may include too much for your use-case.
# You can modify this file to suit your needs.
/.esphome/
/secrets.yaml

106
software/esphome/can.yaml Normal file
View File

@@ -0,0 +1,106 @@
# ===================================================================
# ESPHome Configuration
# CAN-Bus Master für ein Bewässerungssystem auf Basis des ESP32-C6
#
# Version 10: Finale Korrektur der Lambda-Signatur gemäß Dokumentation
# ===================================================================
esphome:
name: can-bridge
friendly_name: Irrigation can bridge
esp32:
board: esp32-c6-devkitm-1
framework:
type: esp-idf # Erforderlich für den ESP32-C6
# --- Netzwerk & Sicherheit ---
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
api:
encryption:
key: !secret api_key
ota:
platform: esphome
password: !secret ota_password
logger:
web_server:
# --- Globale Variablen ---
globals:
- id: ventil_2_can_state
type: int
initial_value: '0' # Startet als "geschlossen"
# --- CAN-Bus Konfiguration ---
canbus:
- platform: esp32_can
id: my_can_bus
tx_pin: GPIO5
rx_pin: GPIO4
bit_rate: 125kbps
can_id: 0x000 # Erforderlich, um Parser-Fehler zu beheben.
on_frame:
# Horcht nur auf die Statusmeldung von Knoten 2 (ID 0x422)
- can_id: 0x422
then:
- lambda: |-
if (x.size() < 1) {
ESP_LOGW("on_can_frame", "Received empty Frame for ID 0x422");
return;
}
int received_state = x[0];
id(ventil_2_can_state) = received_state;
ESP_LOGD("on_can_frame", "Received state from Valve 2: %i", received_state);
- valve.template.publish:
id: ventil_2
current_operation: !lambda |-
int state = id(ventil_2_can_state);
if (state == 2) {
return VALVE_OPERATION_OPENING;
} else if (state == 3) {
return VALVE_OPERATION_CLOSING;
} else {
return VALVE_OPERATION_IDLE;
}
# --- Home Assistant Entitäten ---
valve:
- platform: template
name: "Ventil 2"
id: ventil_2
# Diese Lambda meldet nur den binären End-Zustand (offen/geschlossen)
lambda: |-
if (id(ventil_2_can_state) == 0) {
return VALVE_CLOSED;
} else if (id(ventil_2_can_state) == 1) {
return VALVE_OPEN;
} else {
return NAN;
}
# Aktionen zum Steuern des Ventils
open_action:
- canbus.send:
canbus_id: my_can_bus
can_id: 0x210
data: [0x02, 0x01]
close_action:
- canbus.send:
canbus_id: my_can_bus
can_id: 0x210
data: [0x02, 0x00]
stop_action:
- canbus.send:
canbus_id: my_can_bus
can_id: 0x210
data: [0x02, 0x03]

View File

@@ -0,0 +1,55 @@
#!/usr/bin/env python3
import secrets
import string
import os
import base64
from ruamel.yaml import YAML
def generate_password(length=32):
"""Generate a random password."""
alphabet = string.ascii_letters + string.digits
return ''.join(secrets.choice(alphabet) for i in range(length))
def generate_api_key():
"""Generate a random 32-byte key and base64 encode it."""
return base64.b64encode(secrets.token_bytes(32)).decode('utf-8')
SECRETS_FILE = 'secrets.yaml'
# In a real ESPHome project, secrets are often included from a central location
# but for this script, we'll assume it's in the current directory.
# You might need to adjust this path.
secrets_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), SECRETS_FILE)
yaml = YAML()
yaml.preserve_quotes = True
# To prevent line wrapping
yaml.width = 4096
try:
with open(secrets_path, 'r') as f:
secrets_data = yaml.load(f)
if secrets_data is None:
secrets_data = {}
except FileNotFoundError:
print(f"Info: '{SECRETS_FILE}' not found. A new file will be created.")
secrets_data = {}
# Generate new random passwords
new_api_key = generate_api_key()
new_ota_password = generate_password()
# Update the dictionary with the new passwords
if 'api_password' in secrets_data:
del secrets_data['api_password']
secrets_data['api_key'] = new_api_key
secrets_data['ota_password'] = new_ota_password
# Write the updated dictionary back to the YAML file
with open(secrets_path, 'w') as f:
yaml.dump(secrets_data, f)
print(f"Successfully updated '{SECRETS_FILE}'.")
print("New values:")
print(f" api_key: {new_api_key}")
print(f" ota_password: {new_ota_password}")

View File

@@ -0,0 +1,166 @@
# ===================================================================
# ESPHome Configuration - Final Version
#
# This version corrects the C++ function call inside the valve actions
# to use the correct `send` method from the ModbusDevice base class,
# which is compatible with the esp-idf framework.
# ===================================================================
esphome:
name: irrigation-system
friendly_name: Bewässerung
esp32:
board: esp32-c6-devkitm-1
framework:
type: esp-idf # Set to esp-idf as required by the ESP32-C6 board
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
api:
encryption:
key: !secret api_key
ota:
platform: esphome
password: !secret ota_password
logger:
web_server:
# ===================================================================
# HARDWARE SETUP - COMPLETE
# ===================================================================
# --- UART for RS485 Communication ---
uart:
id: uart_bus
tx_pin: GPIO1
rx_pin: GPIO2
baud_rate: 9600
data_bits: 8
stop_bits: 1
parity: NONE
# --- Base Modbus component for the bus ---
modbus:
- id: modbus_hub
uart_id: uart_bus
# --- Modbus Controller for the specific valve device ---
modbus_controller:
- id: valve_controller
modbus_id: modbus_hub
address: 0 # The Modbus address of your valve. Change if not 0.
# update_interval: 1s
# ===================================================================
# SENSORS - COMPLETE
# ===================================================================
sensor:
# This sensor reads the raw 16-bit value from the valve's input register.
- platform: modbus_controller
modbus_controller_id: valve_controller
name: "Valve Raw Status"
id: valve_raw_status
internal: true # Hide from Home Assistant UI
register_type: read # 'read' is the valid type for input registers
address: 0x0000 # The address of the register to read
value_type: U_WORD # Read the full 16-bit unsigned word
- platform: modbus_controller
modbus_controller_id: valve_controller
name: "VDD"
id: valve_vdd
register_type: read # 'read' is the valid type for input registers
address: 0x00FC # The address of the register to read
value_type: U_WORD # Read the full 16-bit unsigned word
entity_category: diagnostic # Mark as diagnostic
unit_of_measurement: "V"
accuracy_decimals: 2 # Show two decimal places
# Apply filters to convert the raw value to volts and update periodically
filters:
- lambda: |-
// Convert the raw VDD value to volts
return x / 1000.0; // Assuming the value is in millivolts
- heartbeat: 60s # Update every 60 seconds
- delta: 200 # Only update if the value changes by more than 200 mV
# ===================================================================
# TEXT SENSORS FOR HUMAN-READABLE STATUS
# ===================================================================
text_sensor:
# 1. This text sensor extracts the HIGH BYTE for the operation status.
- platform: template
name: "Valve Operation"
id: valve_operation_status
icon: "mdi:state-machine"
lambda: |-
// Extract the high byte from the raw status sensor
// using a bitwise right shift.
int operation_code = (int)id(valve_raw_status).state >> 8;
switch (operation_code) {
case 0: return {"Idle"};
case 1: return {"Opening"};
case 2: return {"Closing"};
case 3: return {"Obstacle Detected"};
case 4: return {"End Position Not Reached"};
default: return {"Unknown Operation"};
}
# 2. This text sensor extracts the LOW BYTE for the current valve state.
- platform: template
name: "Valve Position"
id: valve_position_status
icon: "mdi:valve"
lambda: |-
// Extract the low byte from the raw status sensor
// using a bitwise AND mask.
int state_code = (int)id(valve_raw_status).state & 0xFF;
switch (state_code) {
case 0: return {"Closed"};
case 1: return {"Open"};
default: return {"Unknown"};
}
# ===================================================================
# THE MAIN VALVE COMPONENT
# ===================================================================
valve:
- platform: template
name: "Modbus Controlled Valve"
id: modbus_valve
optimistic: false
# The lambda determines the current state (open or closed) of the valve.
lambda: |-
int state_code = (int)id(valve_raw_status).state & 0xFF;
if (state_code == 1) {
return true; // Open
} else if (state_code == 0) {
return false; // Closed
} else {
return {}; // Unknown
}
# Action to execute when the "OPEN" button is pressed.
open_action:
- lambda: |-
// Use the send() command inherited from ModbusDevice
// Function 0x06: Write Single Register
// Payload for value 1 is {0x00, 0x01}
const uint8_t data[] = {0x00, 0x01};
id(valve_controller).send(0x06, 0x0000, 1, 2, data);
# Action to execute when the "CLOSE" button is pressed.
close_action:
- lambda: |-
// Payload for value 2 is {0x00, 0x02}
const uint8_t data[] = {0x00, 0x02};
id(valve_controller).send(0x06, 0x0000, 1, 2, data);
# Action to execute when the "STOP" button is pressed.
stop_action:
- lambda: |-
// Payload for value 3 is {0x00, 0x03}
const uint8_t data[] = {0x00, 0x03};
id(valve_controller).send(0x06, 0x0000, 1, 2, data);

View File

@@ -0,0 +1,2 @@
ruamel.yaml
esphome

View File

@@ -0,0 +1,4 @@
wifi_ssid: 'PUT YOUR WIFI SSID HERE'
wifi_password: 'PUT YOUR WIFI PASSWORD HERE'
api_key: 'PUT YOUR KEY HERE OR USE create_secrets.py'
ota_password: 'PUT YOUR KEY HERE OR USE create_secrets.py'

View File

@@ -0,0 +1,41 @@
#ifndef CAN_IDS_H
#define CAN_IDS_H
/*
CAN ID structure for the irrigation system.
PPP FFFF NNNN
PPP: Priority
000: Network segment
001: Critical error
010: Commands
100: Status messages
110: measurements
111: Info messages
FFFF: Function
0001: Valve Commands
0010: Valve States
0011: IO Commands
0100: IO States
0101: Measurements
0111: Sysem Functions (e.g. reset, firmware update)
NNNN: Node ID
*/
#define CAN_ID_PRIORITY_NETWORK 0x000
#define CAN_ID_PRIORITY_CRITICAL_ERROR 0x100
#define CAN_ID_PRIORITY_COMMANDS 0x200
#define CAN_ID_PRIORITY_STATUS 0x400
#define CAN_ID_PRIORITY_MEASUREMENTS 0x600
#define CAN_ID_PRIORITY_INFO 0x700
#define CAN_ID_FUNCTION_VALVE_COMMANDS 0x010
#define CAN_ID_FUNCTION_VALVE_STATES 0x020
#define CAN_ID_FUNCTION_IO_COMMANDS 0x030
#define CAN_ID_FUNCTION_IO_STATES 0x040
#define CAN_ID_FUNCTION_MEASUREMENTS 0x050
#define CAN_ID_FUNCTION_SYSTEM_FUNCTIONS 0x070
#endif // CAN_IDS_H

View File

@@ -3,8 +3,45 @@
#include <stdint.h>
/**
* @file fwu.h
* @brief API for the Firmware Update (FWU) library.
*
* This library provides the core logic for handling the over-the-air firmware
* update process via Modbus. It manages the data buffer, processes commands,
* and calculates CRC checksums for data verification.
*/
/**
* @brief Initializes the firmware update module.
*
* This function currently does nothing but is a placeholder for future
* initialization logic.
*/
void fwu_init(void);
/**
* @brief Handles incoming Modbus register writes related to firmware updates.
*
* This function is the main entry point for the FWU process. It parses the
* address and value from a Modbus write operation and takes appropriate action,
* such as storing metadata (offset, size) or data chunks, and processing
* commands (verify, finalize).
*
* @param addr The Modbus register address being written to.
* @param reg The 16-bit value being written to the register.
*/
void fwu_handler(uint16_t addr, uint16_t reg);
/**
* @brief Gets the CRC16-CCITT of the last received firmware chunk.
*
* After a data chunk is fully received into the buffer, this function can be
* called to retrieve the calculated CRC checksum. The master can then compare
* this with its own calculated CRC to verify data integrity.
*
* @return The 16-bit CRC of the last chunk.
*/
uint16_t fwu_get_last_chunk_crc(void);
#endif // FWU_H

View File

View File

@@ -4,51 +4,182 @@
#include <stdint.h>
/**
* @brief Modbus Input Register Addresses.
* @file modbus_server.h
* @brief API for the Modbus server implementation.
*
* This file defines the Modbus register map and provides functions to
* initialize and manage the Modbus server.
*/
/**
* @brief Modbus Input Register Addresses (Read-Only).
* @see docs/modbus-registers.de.md
*/
enum {
/**
* @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).
*/
enum
{
/* Valve Control & Status */
REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000,
REG_INPUT_MOTOR_CURRENT_MA = 0x0001,
/* Digital Inputs */
/**
* @brief Motor current during opening in milliamperes (mA).
*/
REG_INPUT_MOTOR_OPEN_CURRENT_MA = 0x0001,
/**
* @brief Motor current during closing in milliamperes (mA).
*/
REG_INPUT_MOTOR_CLOSE_CURRENT_MA = 0x0002,
/**
* @brief Bitmask of digital inputs. Bit 0: Input 1, Bit 1: Input 2.
* 1=Active.
*/
REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020,
/**
* @brief Event flags for buttons (Clear-on-Read). Bit 0: Button 1 pressed.
* Bit 1: Button 2 pressed.
*/
REG_INPUT_BUTTON_EVENTS = 0x0021,
/* System Config & Status */
/**
* @brief Firmware version, e.g., 0x0102 for v1.2.
*/
REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0,
/**
* @brief Firmware version patch level, e.g., 3 for v1.2.3.
*/
REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1,
/**
* @brief Device status (0=OK, 1=General Error).
*/
REG_INPUT_DEVICE_STATUS = 0x00F2,
/**
* @brief Lower 16 bits of uptime in seconds.
*/
REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3,
/**
* @brief Upper 16 bits of uptime in seconds.
*/
REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4,
/**
* @brief Current supply voltage in millivolts (mV).
*/
REG_INPUT_SUPPLY_VOLTAGE_MV = 0x00F5,
/**
* @brief CRC16 of the last received data chunk in the buffer for firmware
* update.
*/
REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100
};
/**
* @brief Modbus Holding Register Addresses.
* @brief Modbus Holding Register Addresses (Read/Write).
* @see docs/modbus-registers.de.md
*/
enum {
/**
* @brief Valve control command (1=Open, 2=Close, 0=Stop movement).
*/
enum
{
/* Valve Control */
REG_HOLDING_VALVE_COMMAND = 0x0000,
/**
* @brief Safety timeout in seconds for the opening process.
*/
REG_HOLDING_MAX_OPENING_TIME_S = 0x0001,
/**
* @brief Safety timeout in seconds for the closing process.
*/
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
/* Digital Outputs */
/**
* @brief Minimum current threshold in mA for end-position detection.
*/
REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003,
/**
* @brief Minimum current threshold in mA for end-position detection during
* closing.
*/
REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA = 0x0004,
/**
* @brief Current threshold in mA for obstacle detection during opening.
*/
REG_HOLDING_OBSTACLE_THRESHOLD_OPEN_MA = 0x0005,
/**
* @brief Current threshold in mA for obstacle detection during closing.
*/
REG_HOLDING_OBSTACLE_THRESHOLD_CLOSE_MA = 0x0006,
/**
* @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,
/* System Config */
/**
* @brief Fail-safe watchdog timeout in seconds. 0=Disabled.
*/
REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0,
/**
* @brief Writing 1 restarts the device.
*/
REG_HOLDING_DEVICE_RESET = 0x00F1,
/* Firmware Update */
/**
* @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 Lower 16 bits of the 32-bit offset for the next firmware update
* chunk.
*/
REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101,
/**
* @brief Upper 16 bits of the 32-bit offset for the next firmware update
* chunk.
*/
REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102,
/**
* @brief Size of the next firmware update chunk in bytes (max. 256).
*/
REG_HOLDING_FWU_CHUNK_SIZE = 0x0103,
/**
* @brief Start address of the 256-byte buffer for firmware update data.
*/
REG_HOLDING_FWU_DATA_BUFFER = 0x0180,
};
/**
* @brief Initializes the Modbus server.
*
* This function sets up the Modbus RTU server interface, loads saved settings
* (baudrate, unit ID), and starts listening for requests.
*
* @return 0 on success, or a negative error code on failure.
*/
int modbus_server_init(void);
/**
* @brief Reconfigures the Modbus server at runtime.
*
* Updates the baudrate and unit ID of the server. If the reconfiguration
* fails, the settings are saved and will be applied after a device reset.
*
* @param baudrate The new baudrate to set.
* @param unit_id The new Modbus unit ID (slave address).
* @return 0 on success, or a negative error code if immediate reconfiguration
* fails. Returns 0 even on failure if settings could be saved for the next
* boot.
*/
int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id);
/**
* @brief Gets the current baudrate of the Modbus server.
*
* @return The current baudrate.
*/
uint32_t modbus_get_baudrate(void);
/**
* @brief Gets the current unit ID of the Modbus server.
*
* @return The current unit ID.
*/
uint8_t modbus_get_unit_id(void);
#endif // MODBUS_SERVER_H

View File

@@ -1,37 +1,214 @@
#ifndef VALVE_H
#define VALVE_H
#include <stdint.h>
#include <zephyr/drivers/gpio.h>
#include <stdint.h>
struct valve_gpios {
const struct gpio_dt_spec in0;
const struct gpio_dt_spec in1;
const struct gpio_dt_spec rst;
const struct gpio_dt_spec sen;
const struct gpio_dt_spec s0;
const struct gpio_dt_spec s1;
};
/**
* @file valve.h
* @brief API for controlling the motorized valve.
*
* This library provides functions to initialize, open, close, and stop the
* valve. It also allows getting the valve's state and movement status, and
* configuring the maximum opening and closing times.
*/
#define VALVE_CHANNEL_OPEN 0
#define VALVE_CHANNEL_CLOSE 1
#define VALVE_CURRENT_CHECK_INTERVAL K_MSEC(CONFIG_VALVE_INTERVALL_CURRENT_CHECK_MS)
#define VALVE_INITIAL_CURRENT_CHECK_INTERVAL K_MSEC(CONFIG_VALVE_INITIAL_INTERVALL_CURRENT_CHECK_MS)
/**
* @brief Represents the static state of the valve (open or closed).
*/
enum valve_state {
VALVE_STATE_CLOSED,
VALVE_STATE_OPEN,
VALVE_STATE_CLOSED, /**< The valve is fully closed. */
VALVE_STATE_OPEN, /**< The valve is fully open. */
};
enum valve_movement { VALVE_MOVEMENT_IDLE, VALVE_MOVEMENT_OPENING, VALVE_MOVEMENT_CLOSING, VALVE_MOVEMENT_ERROR };
void valve_init(void);
/**
* @brief Represents the dynamic movement status of the valve.
*/
enum valve_movement {
VALVE_MOVEMENT_IDLE, /**< The valve is not moving. */
VALVE_MOVEMENT_OPENING, /**< The valve is currently opening. */
VALVE_MOVEMENT_CLOSING, /**< The valve is currently closing. */
VALVE_MOVEMENT_ERROR /**< An error occurred during 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.
*/
int valve_init(void);
/**
* @brief Starts opening the valve.
*
* The valve will open for the configured maximum opening time.
*/
void valve_open(void);
/**
* @brief Starts closing the valve.
*
* The valve will close for the configured maximum closing time.
*/
void valve_close(void);
/**
* @brief Stops any ongoing valve movement immediately.
*/
void valve_stop(void);
/**
* @brief Gets the current static state of the valve.
*
* @return The current valve state (VALVE_STATE_CLOSED or VALVE_STATE_OPEN).
*/
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);
/**
* @brief Gets the current movement status of the valve.
*
* @return The current movement status.
*/
enum valve_movement valve_get_movement(void);
/**
* @brief Sets the maximum time for the valve to open.
*
* @param seconds The timeout in seconds.
*/
void valve_set_max_open_time(uint16_t seconds);
/**
* @brief Sets the maximum time for the valve to close.
*
* @param seconds The timeout in seconds.
*/
void valve_set_max_close_time(uint16_t seconds);
/**
* @brief Sets the current threshold for end-position detection during opening.
*
* @param current_ma The current threshold in milliamps.
*/
void valve_set_end_current_threshold_open(uint16_t current_ma);
/**
* @brief Sets the current threshold for end-position detection during closing.
*
* @param current_ma The current threshold in milliamps.
*/
void valve_set_end_current_threshold_close(uint16_t current_ma);
/**
* @brief Gets the current threshold for end-position detection during opening.
*
* @return The current threshold in milliamps.
*/
uint16_t valve_get_end_current_threshold_open(void);
/**
* @brief Gets the current threshold for end-position detection during closing.
*
* @return The current threshold in milliamps.
*/
uint16_t valve_get_end_current_threshold_close(void);
/**
* @brief Gets the configured maximum opening time.
*
* @return The timeout in seconds.
*/
uint16_t valve_get_max_open_time(void);
/**
* @brief Gets the configured maximum closing time.
*
* @return The timeout in seconds.
*/
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);
/**
* @brief Sets the current threshold for obstacle detection during opening.
*
* @param current_ma The current threshold in milliamps.
*/
void valve_set_obstacle_threshold_open(uint16_t current_ma);
/**
* @brief Sets the current threshold for obstacle detection during closing.
*
* @param current_ma The current threshold in milliamps.
*/
void valve_set_obstacle_threshold_close(uint16_t current_ma);
/**
* @brief Gets the current threshold for obstacle detection during opening.
*
* @return The current threshold in milliamps.
*/
uint16_t valve_get_obstacle_threshold_open(void);
/**
* @brief Gets the current threshold for obstacle detection during closing.
*
* @return The current threshold in milliamps.
*/
uint16_t valve_get_obstacle_threshold_close(void);
/**
* @brief Callback function called during valve opening with current readings.
*
* This is a weak function that can be overridden to provide custom handling
* of current readings during valve opening operations.
*
* @param current_ma The current reading in milliamps.
*/
void valve_current_open_callback(int current_ma);
/**
* @brief Callback function called during valve closing with current readings.
*
* This is a weak function that can be overridden to provide custom handling
* of current readings during valve closing operations.
*
* @param current_ma The current reading in milliamps.
*/
void valve_current_close_callback(int current_ma);
#endif // VALVE_H

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2025, Eduard Iten
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_MISC_VND7050AJ_H_
#define ZEPHYR_INCLUDE_DRIVERS_MISC_VND7050AJ_H_
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Channel identifiers for the VND7050AJ.
*/
#define VND7050AJ_CHANNEL_0 0
#define VND7050AJ_CHANNEL_1 1
/**
* @brief Sets the state of a specific output channel.
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The channel to control (VND7050AJ_CHANNEL_0 or VND7050AJ_CHANNEL_1).
* @param state The desired state (true for ON, false for OFF).
* @return 0 on success, negative error code on failure.
*/
int vnd7050aj_set_output_state(const struct device *dev, uint8_t channel, bool state);
/**
* @brief Reads the load current for a specific channel.
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The channel to measure (VND7050AJ_CHANNEL_0 or VND7050AJ_CHANNEL_1).
* @param[out] current_ma Pointer to store the measured current in milliamperes (mA).
* @return 0 on success, negative error code on failure.
*/
int vnd7050aj_read_load_current(const struct device *dev, uint8_t channel, int32_t *current_ma);
/**
* @brief Reads the VCC supply voltage.
*
* @param dev Pointer to the device structure for the driver instance.
* @param[out] voltage_mv Pointer to store the measured voltage in millivolts (mV).
* @return 0 on success, negative error code on failure.
*/
int vnd7050aj_read_supply_voltage(const struct device *dev, int32_t *voltage_mv);
/**
* @brief Reads the internal chip temperature.
*
* @param dev Pointer to the device structure for the driver instance.
* @param[out] temp_c Pointer to store the measured temperature in degrees Celsius (°C).
* @return 0 on success, negative error code on failure.
*/
int vnd7050aj_read_chip_temp(const struct device *dev, int32_t *temp_c);
/**
* @brief Resets a latched fault condition.
*
* This function sends a low pulse to the FaultRST pin.
*
* @param dev Pointer to the device structure for the driver instance.
* @return 0 on success, negative error code on failure.
*/
int vnd7050aj_reset_fault(const struct device *dev);
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_MISC_VND7050AJ_H_ */

View File

@@ -3,3 +3,5 @@ add_subdirectory_ifdef(CONFIG_LIB_MODBUS_SERVER modbus_server)
add_subdirectory_ifdef(CONFIG_LIB_VALVE valve)
add_subdirectory_ifdef(CONFIG_SHELL_SYSTEM shell_system)
add_subdirectory_ifdef(CONFIG_SHELL_MODBUS shell_modbus)
add_subdirectory_ifdef(CONFIG_SHELL_VALVE shell_valve)
add_subdirectory_ifdef(CONFIG_VND7050AJ vnd7050aj)

View File

@@ -5,4 +5,6 @@ rsource "modbus_server/Kconfig"
rsource "valve/Kconfig"
rsource "shell_system/Kconfig"
rsource "shell_modbus/Kconfig"
rsource "shell_valve/Kconfig"
rsource "vnd7050aj/Kconfig"
endmenu

View File

@@ -1,26 +1,44 @@
/**
* @file fwu.c
* @brief Implementation of the Firmware Update (FWU) library.
*
* This file implements the logic for receiving a new firmware image in chunks
* over Modbus. It maintains a buffer for the incoming data, calculates the CRC
* of the received chunk, and handles commands to verify the chunk and finalize
* the update process. The actual writing to flash is simulated.
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/crc.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/crc.h>
#include <lib/fwu.h>
LOG_MODULE_REGISTER(fwu, LOG_LEVEL_INF);
#define FWU_BUFFER_SIZE 256
static uint8_t fwu_buffer[FWU_BUFFER_SIZE];
static uint32_t fwu_chunk_offset = 0;
static uint16_t fwu_chunk_size = 0;
static uint16_t fwu_last_chunk_crc = 0;
static uint8_t fwu_buffer[FWU_BUFFER_SIZE]; // Buffer to store incoming
// firmware data chunks
static uint32_t fwu_chunk_offset = 0; // Offset for the current firmware chunk in the overall image
static uint16_t fwu_chunk_size = 0; // Size of the current firmware chunk
static uint16_t fwu_last_chunk_crc = 0; // CRC16 of the last received firmware chunk
void fwu_init(void) {}
void fwu_init(void)
{
}
void fwu_handler(uint16_t addr, uint16_t reg)
{
// This is a simplified handler. In a real scenario, you would have a proper mapping
// between register addresses and actions.
// This is a simplified handler. In a real scenario, you would have a proper
// mapping between register addresses and actions.
if (addr == 0x0100) { // FWU_COMMAND
if (reg == 1) { LOG_INF("FWU: Chunk at offset %u (size %u) verified.", fwu_chunk_offset, fwu_chunk_size); }
else if (reg == 2) { LOG_INF("FWU: Finalize command received. Rebooting (simulated)."); }
if (reg == 1) {
LOG_INF("FWU: Chunk at offset %u (size %u) verified.",
fwu_chunk_offset,
fwu_chunk_size);
} else if (reg == 2) {
LOG_INF("FWU: Finalize command received. Rebooting (simulated).");
}
} else if (addr == 0x0101) { // FWU_CHUNK_OFFSET_LOW
fwu_chunk_offset = (fwu_chunk_offset & 0xFFFF0000) | reg;
} else if (addr == 0x0102) { // FWU_CHUNK_OFFSET_HIGH
@@ -32,7 +50,8 @@ void fwu_handler(uint16_t addr, uint16_t reg)
if (index < sizeof(fwu_buffer)) {
sys_put_be16(reg, &fwu_buffer[index]);
if (index + 2 >= fwu_chunk_size) {
fwu_last_chunk_crc = crc16_ccitt(0xffff, fwu_buffer, fwu_chunk_size);
fwu_last_chunk_crc =
crc16_ccitt(0xffff, fwu_buffer, fwu_chunk_size);
LOG_INF("FWU: Chunk received, CRC is 0x%04X", fwu_last_chunk_crc);
}
}

View File

@@ -1,5 +1,5 @@
config LIB_MODBUS_SERVER
bool "Enable Modbus Server Library"
default y
default n
help
Enable the Modbus Server Library.

View File

@@ -1,15 +1,25 @@
#include <zephyr/kernel.h>
#include <zephyr/drivers/uart.h>
/**
* @file modbus_server.c
* @brief Modbus RTU server implementation for the irrigation system slave node.
*
* This file implements the Modbus server logic, including register callbacks,
* watchdog handling, and dynamic reconfiguration. It interfaces with other
* libraries like valve control, ADC sensors, and firmware updates.
*/
#include <zephyr/device.h>
#include <zephyr/modbus/modbus.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/modbus/modbus.h>
#include <zephyr/settings/settings.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/usb/usb_device.h>
#include <app_version.h>
#include <lib/fwu.h>
#include <lib/modbus_server.h>
#include <lib/valve.h>
#include <lib/fwu.h>
#include <zephyr/usb/usb_device.h>
#include <lib/vnd7050aj.h>
LOG_MODULE_REGISTER(modbus_server, LOG_LEVEL_INF);
@@ -23,25 +33,45 @@ static struct modbus_iface_param server_param = {
static uint16_t watchdog_timeout_s = 0;
static struct k_timer watchdog_timer;
/**
* @brief Timer handler for the Modbus watchdog.
*
* This function is called when the watchdog timer expires, indicating a loss
* of communication with the Modbus master. It triggers a fail-safe action,
* which is to close the valve.
*
* @param timer_id Pointer to the timer instance.
*/
static void watchdog_timer_handler(struct k_timer *timer_id)
{
LOG_WRN("Modbus watchdog expired! Closing valve as a fail-safe.");
valve_close();
}
/**
* @brief Resets the Modbus watchdog timer.
*
* This function should be called upon receiving any valid Modbus request
* to prevent the watchdog from expiring.
*/
static inline void reset_watchdog(void)
{
if (watchdog_timeout_s > 0)
{
if (watchdog_timeout_s > 0) {
k_timer_start(&watchdog_timer, K_SECONDS(watchdog_timeout_s), K_NO_WAIT);
}
}
/**
* @brief Callback for reading Modbus holding registers.
*
* @param addr Register address.
* @param reg Pointer to store the read value.
* @return 0 on success.
*/
static int holding_reg_rd(uint16_t addr, uint16_t *reg)
{
reset_watchdog();
switch (addr)
{
switch (addr) {
case REG_HOLDING_MAX_OPENING_TIME_S:
*reg = valve_get_max_open_time();
break;
@@ -51,6 +81,18 @@ static int holding_reg_rd(uint16_t addr, uint16_t *reg)
case REG_HOLDING_WATCHDOG_TIMEOUT_S:
*reg = watchdog_timeout_s;
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA:
*reg = valve_get_end_current_threshold_open();
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA:
*reg = valve_get_end_current_threshold_close();
break;
case REG_HOLDING_OBSTACLE_THRESHOLD_OPEN_MA:
*reg = valve_get_obstacle_threshold_open();
break;
case REG_HOLDING_OBSTACLE_THRESHOLD_CLOSE_MA:
*reg = valve_get_obstacle_threshold_close();
break;
default:
*reg = 0;
break;
@@ -58,22 +100,23 @@ static int holding_reg_rd(uint16_t addr, uint16_t *reg)
return 0;
}
/**
* @brief Callback for writing Modbus holding registers.
*
* @param addr Register address.
* @param reg Value to write.
* @return 0 on success.
*/
static int holding_reg_wr(uint16_t addr, uint16_t reg)
{
reset_watchdog();
switch (addr)
{
switch (addr) {
case REG_HOLDING_VALVE_COMMAND:
if (reg == 1)
{
if (reg == 1) {
valve_open();
}
else if (reg == 2)
{
} else if (reg == 2) {
valve_close();
}
else if (reg == 0)
{
} else if (reg == 0) {
valve_stop();
}
break;
@@ -83,22 +126,30 @@ static int holding_reg_wr(uint16_t addr, uint16_t reg)
case REG_HOLDING_MAX_CLOSING_TIME_S:
valve_set_max_close_time(reg);
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA:
valve_set_end_current_threshold_open(reg);
break;
case REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA:
valve_set_end_current_threshold_close(reg);
break;
case REG_HOLDING_OBSTACLE_THRESHOLD_OPEN_MA:
valve_set_obstacle_threshold_open(reg);
break;
case REG_HOLDING_OBSTACLE_THRESHOLD_CLOSE_MA:
valve_set_obstacle_threshold_close(reg);
break;
case REG_HOLDING_WATCHDOG_TIMEOUT_S:
watchdog_timeout_s = reg;
if (watchdog_timeout_s > 0)
{
if (watchdog_timeout_s > 0) {
LOG_INF("Watchdog enabled with %u s timeout.", watchdog_timeout_s);
reset_watchdog();
}
else
{
} else {
LOG_INF("Watchdog disabled.");
k_timer_stop(&watchdog_timer);
}
break;
case REG_HOLDING_DEVICE_RESET:
if (reg == 1)
{
if (reg == 1) {
LOG_WRN("Modbus reset command received. Rebooting...");
sys_reboot(SYS_REBOOT_WARM);
}
@@ -110,17 +161,26 @@ static int holding_reg_wr(uint16_t addr, uint16_t reg)
return 0;
}
/**
* @brief Callback for reading Modbus input registers.
*
* @param addr Register address.
* @param reg Pointer to store the read value.
* @return 0 on success.
*/
static int input_reg_rd(uint16_t addr, uint16_t *reg)
{
reset_watchdog();
uint32_t uptime_s = k_uptime_get_32() / 1000;
switch (addr)
{
switch (addr) {
case REG_INPUT_VALVE_STATE_MOVEMENT:
*reg = (valve_get_movement() << 8) | (valve_get_state() & 0xFF);
break;
case REG_INPUT_MOTOR_CURRENT_MA:
*reg = valve_get_motor_current();
case REG_INPUT_MOTOR_OPEN_CURRENT_MA:
*reg = (uint16_t)valve_get_opening_current();
break;
case REG_INPUT_MOTOR_CLOSE_CURRENT_MA:
*reg = (uint16_t)valve_get_closing_current();
break;
case REG_INPUT_UPTIME_SECONDS_LOW:
*reg = (uint16_t)(uptime_s & 0xFFFF);
@@ -129,16 +189,16 @@ 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 = 12300;
*reg = (uint16_t)valve_get_vnd_voltage();
break;
case REG_INPUT_FWU_LAST_CHUNK_CRC:
*reg = fwu_get_last_chunk_crc();
break;
case REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR:
*reg = (0 << 8) | 0;
*reg = (APP_VERSION_MAJOR << 8) | APP_VERSION_MINOR;
break;
case REG_INPUT_FIRMWARE_VERSION_PATCH:
*reg = 2;
*reg = APP_PATCHLEVEL;
break;
default:
*reg = 0;
@@ -148,6 +208,7 @@ static int input_reg_rd(uint16_t addr, uint16_t *reg)
}
static struct modbus_user_callbacks mbs_cbs = {
// Modbus server callback functions
.holding_reg_rd = holding_reg_rd,
.holding_reg_wr = holding_reg_wr,
.input_reg_rd = input_reg_rd,
@@ -174,13 +235,11 @@ int modbus_server_init(void)
const struct device *const dev = DEVICE_DT_GET(DT_PARENT(MODBUS_NODE));
uint32_t dtr = 0;
if (!device_is_ready(dev) || usb_enable(NULL))
{
if (!device_is_ready(dev) || usb_enable(NULL)) {
return 0;
}
while (!dtr)
{
while (!dtr) {
uart_line_ctrl_get(dev, UART_LINE_CTRL_DTR, &dtr);
k_sleep(K_MSEC(100));
}
@@ -188,8 +247,7 @@ int modbus_server_init(void)
LOG_INF("Client connected to server on %s", dev->name);
#endif
modbus_iface = modbus_iface_get_by_name(iface_name);
if (modbus_iface < 0)
{
if (modbus_iface < 0) {
return modbus_iface;
}
server_param.server.user_cb = &mbs_cbs;
@@ -207,14 +265,11 @@ int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id)
// Try to reinitialize - this should work for most cases
int ret = modbus_init_server(modbus_iface, server_param);
if (ret == 0)
{
if (ret == 0) {
settings_save_one("modbus/baudrate", &baudrate, sizeof(baudrate));
settings_save_one("modbus/unit_id", &unit_id, sizeof(unit_id));
LOG_INF("Modbus reconfigured: baudrate=%u, unit_id=%u", baudrate, unit_id);
}
else
{
} else {
LOG_ERR("Failed to reconfigure Modbus: %d", ret);
LOG_INF("Modbus reconfiguration requires restart to take effect");
@@ -222,12 +277,19 @@ int modbus_reconfigure(uint32_t baudrate, uint8_t unit_id)
settings_save_one("modbus/baudrate", &baudrate, sizeof(baudrate));
settings_save_one("modbus/unit_id", &unit_id, sizeof(unit_id));
LOG_INF("Settings saved. Type 'reset' to restart the device and apply the change.");
LOG_INF("Settings saved. Type 'reset' to restart the device and apply the "
"change.");
return 0; // Return success since settings are saved
}
return ret;
}
uint32_t modbus_get_baudrate(void) { return server_param.serial.baud; }
uint8_t modbus_get_unit_id(void) { return server_param.server.unit_id; }
uint32_t modbus_get_baudrate(void)
{
return server_param.serial.baud;
}
uint8_t modbus_get_unit_id(void)
{
return server_param.server.unit_id;
}

View File

@@ -1,5 +1,7 @@
config SHELL_MODBUS
bool "Enable Shell Modbus"
default y
default n
depends on SHELL
depends on LIB_MODBUS_SERVER
help
Enable the modnbus shell commands.
Enable the modbus shell commands.

View File

@@ -1,12 +1,30 @@
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include <lib/modbus_server.h>
#include <lib/valve.h>
/**
* @file shell_modbus.c
* @brief Provides shell commands for Modbus and valve configuration.
*
* This file implements a set of commands for the Zephyr shell to allow
* runtime configuration of the Modbus server (baudrate, slave ID) and the
* valve (max opening/closing times). The settings are persisted to non-volatile
* storage.
*/
static int cmd_modbus_set_baud(const struct shell *sh, size_t argc, char **argv)
#include <zephyr/shell/shell.h>
#include <lib/modbus_server.h>
#include <stdio.h>
#include <stdlib.h>
/**
* @brief Shell command to set the Modbus baudrate.
*
* @param sh The shell instance.
* @param argc Argument count.
* @param argv Argument values.
* @return 0 on success, -EINVAL on error.
*/
static int cmd_modbus_setb(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_error(sh, "Usage: set_baud <baudrate>");
shell_error(sh, "Usage: setb <baudrate>");
return -EINVAL;
}
@@ -23,9 +41,13 @@ static int cmd_modbus_set_baud(const struct shell *sh, size_t argc, char **argv)
if (!is_valid) {
char error_msg[128];
int offset = snprintf(error_msg, sizeof(error_msg), "Invalid baudrate. Valid rates are: ");
int offset =
snprintf(error_msg, sizeof(error_msg), "Invalid baudrate. Valid rates are: ");
for (int i = 0; i < ARRAY_SIZE(valid_baud_rates); i++) {
offset += snprintf(error_msg + offset, sizeof(error_msg) - offset, "%u ", valid_baud_rates[i]);
offset += snprintf(error_msg + offset,
sizeof(error_msg) - offset,
"%u ",
valid_baud_rates[i]);
}
shell_error(sh, "%s", error_msg);
return -EINVAL;
@@ -40,10 +62,18 @@ static int cmd_modbus_set_baud(const struct shell *sh, size_t argc, char **argv)
return 0;
}
static int cmd_modbus_set_id(const struct shell *sh, size_t argc, char **argv)
/**
* @brief Shell command to set the Modbus slave ID.
*
* @param sh The shell instance.
* @param argc Argument count.
* @param argv Argument values.
* @return 0 on success, -EINVAL on error.
*/
static int cmd_modbus_setid(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_error(sh, "Usage: set_id <slave_id>");
shell_error(sh, "Usage: setid <slave_id>");
return -EINVAL;
}
@@ -63,57 +93,28 @@ static int cmd_modbus_set_id(const struct shell *sh, size_t argc, char **argv)
return 0;
}
static int cmd_valve_set_open_time(const struct shell *sh, size_t argc, char **argv)
/**
* @brief Shell command to show the current Modbus configuration.
*
* @param sh The shell instance.
* @param argc Argument count.
* @param argv Argument values.
* @return 0 on success.
*/
static int cmd_modbus_show(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_error(sh, "Usage: set_open_time <seconds>");
return -EINVAL;
}
const int label_width = 15;
uint16_t seconds = (uint16_t)strtoul(argv[1], NULL, 10);
valve_set_max_open_time(seconds);
shell_print(sh, "Max opening time set to: %u seconds (and saved)", seconds);
return 0;
}
static int cmd_valve_set_close_time(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_error(sh, "Usage: set_close_time <seconds>");
return -EINVAL;
}
uint16_t seconds = (uint16_t)strtoul(argv[1], NULL, 10);
valve_set_max_close_time(seconds);
shell_print(sh, "Max closing time set to: %u seconds (and saved)", seconds);
return 0;
}
static int cmd_config_show(const struct shell *sh, size_t argc, char **argv)
{
shell_print(sh, "Current Modbus Configuration:");
shell_print(sh, " Baudrate: %u", modbus_get_baudrate());
shell_print(sh, " Slave ID: %u", modbus_get_unit_id());
shell_print(sh, "Current Valve Configuration:");
shell_print(sh, " Max Opening Time: %u s", valve_get_max_open_time());
shell_print(sh, " Max Closing Time: %u s", valve_get_max_close_time());
shell_print(sh, "Modbus Settings:");
shell_print(sh, "%*s %u", label_width, "Baudrate:", modbus_get_baudrate());
shell_print(sh, "%*s %u", label_width, "Slave ID:", modbus_get_unit_id());
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(sub_modbus_cmds,
SHELL_CMD(set_baud, NULL, "Set Modbus baudrate", cmd_modbus_set_baud),
SHELL_CMD(set_id, NULL, "Set Modbus slave ID", cmd_modbus_set_id),
SHELL_SUBCMD_SET_END
);
SHELL_CMD(setb, NULL, "Set Modbus baudrate", cmd_modbus_setb),
SHELL_CMD(setid, NULL, "Set Modbus slave ID", cmd_modbus_setid),
SHELL_CMD(show, NULL, "Show Modbus configuration", cmd_modbus_show),
SHELL_SUBCMD_SET_END);
SHELL_STATIC_SUBCMD_SET_CREATE(sub_valve_cmds,
SHELL_CMD(set_open_time, NULL, "Set max valve opening time", cmd_valve_set_open_time),
SHELL_CMD(set_close_time, NULL, "Set max valve closing time", cmd_valve_set_close_time),
SHELL_SUBCMD_SET_END
);
SHELL_CMD_REGISTER(modbus, &sub_modbus_cmds, "Modbus configuration", NULL);
SHELL_CMD_REGISTER(valve, &sub_valve_cmds, "Valve configuration", NULL);
SHELL_CMD_REGISTER(show_config, NULL, "Show all configurations", cmd_config_show);
SHELL_CMD_REGISTER(modbus, &sub_modbus_cmds, "Modbus commands", NULL);

View File

@@ -1,5 +1,5 @@
config SHELL_SYSTEM
bool "Enable Shell System"
default y
default n
help
Enable the system commands.

View File

@@ -1,6 +1,25 @@
/**
* @file shell_system.c
* @brief Provides basic system-level shell commands.
*
* This file implements essential system commands for the Zephyr shell,
* such as rebooting the device.
*/
#include <zephyr/shell/shell.h>
#include <zephyr/sys/reboot.h>
/**
* @brief Shell command to reset the system.
*
* This command performs a warm reboot of the device after a short delay
* to ensure the shell message is printed.
*
* @param sh The shell instance.
* @param argc Argument count.
* @param argv Argument values.
* @return 0 on success.
*/
static int cmd_reset(const struct shell *sh, size_t argc, char **argv)
{
shell_print(sh, "Rebooting system...");

View File

@@ -0,0 +1 @@
zephyr_library_sources(shell_valve.c)

View File

@@ -0,0 +1,7 @@
config SHELL_VALVE
bool "Shell Valve commands"
default n
depends on SHELL
depends on LIB_VALVE
help
Enable the valve shell commands.

View File

@@ -0,0 +1,185 @@
#include <zephyr/kernel.h>
#include <zephyr/shell/shell.h>
#include <lib/valve.h>
#include <stdlib.h>
static int cmd_valve_set_open_t(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_open_t <seconds>");
return -EINVAL;
}
uint16_t seconds = (uint16_t)atoi(argv[1]);
valve_set_max_open_time(seconds);
shell_print(sh, "Max open time set to %u seconds.", seconds);
return 0;
}
static int cmd_valve_set_close_t(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_close_t <seconds>");
return -EINVAL;
}
uint16_t seconds = (uint16_t)atoi(argv[1]);
valve_set_max_close_time(seconds);
shell_print(sh, "Max close time set to %u seconds.", seconds);
return 0;
}
static int cmd_valve_set_end_curr_open(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_end_curr_open <milliamps>");
return -EINVAL;
}
uint16_t current_ma = (uint16_t)atoi(argv[1]);
valve_set_end_current_threshold_open(current_ma);
shell_print(sh, "End current threshold (open) set to %u mA.", current_ma);
return 0;
}
static int cmd_valve_set_end_curr_close(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_end_curr_close <milliamps>");
return -EINVAL;
}
uint16_t current_ma = (uint16_t)atoi(argv[1]);
valve_set_end_current_threshold_close(current_ma);
shell_print(sh, "End current threshold (close) set to %u mA.", current_ma);
return 0;
}
static int cmd_valve_set_obstacle_open(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_obstacle_open <milliamps>");
return -EINVAL;
}
uint16_t current_ma = (uint16_t)atoi(argv[1]);
valve_set_obstacle_threshold_open(current_ma);
shell_print(sh, "Obstacle threshold (open) set to %u mA.", current_ma);
return 0;
}
static int cmd_valve_set_obstacle_close(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_obstacle_close <milliamps>");
return -EINVAL;
}
uint16_t current_ma = (uint16_t)atoi(argv[1]);
valve_set_obstacle_threshold_close(current_ma);
shell_print(sh, "Obstacle threshold (close) set to %u mA.", current_ma);
return 0;
}
static int cmd_valve_show(const struct shell *sh, size_t argc, char **argv)
{
const int label_width = 30;
shell_print(sh, "Valve Settings:");
shell_print(sh, "%*s %u s", label_width, "Max Open Time:", valve_get_max_open_time());
shell_print(sh, "%*s %u s", label_width, "Max Close Time:", valve_get_max_close_time());
shell_print(sh,
"%*s %u mA",
label_width,
"End Current Threshold (Open):",
valve_get_end_current_threshold_open());
shell_print(sh,
"%*s %u mA",
label_width,
"End Current Threshold (Close):",
valve_get_end_current_threshold_close());
shell_print(sh,
"%*s %u mA",
label_width,
"Obstacle Threshold (Open):",
valve_get_obstacle_threshold_open());
shell_print(sh,
"%*s %u mA",
label_width,
"Obstacle Threshold (Close):",
valve_get_obstacle_threshold_close());
return 0;
}
static int cmd_valve_open(const struct shell *sh, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (valve_get_movement() != VALVE_MOVEMENT_IDLE) {
shell_print(sh, "Valve is already moving.");
return -EBUSY;
}
valve_open();
shell_print(sh, "Valve is opening.");
return 0;
}
static int cmd_valve_close(const struct shell *sh, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (valve_get_movement() != VALVE_MOVEMENT_IDLE) {
shell_print(sh, "Valve is already moving.");
return -EBUSY;
}
valve_close();
shell_print(sh, "Valve is closing.");
return 0;
}
static int cmd_valve_stop(const struct shell *sh, size_t argc, char **argv)
{
ARG_UNUSED(argc);
ARG_UNUSED(argv);
if (valve_get_movement() == VALVE_MOVEMENT_IDLE) {
shell_print(sh, "Valve is already stopped.");
return -EINVAL;
}
valve_stop();
shell_print(sh, "Valve movement stopped.");
return 0;
}
SHELL_STATIC_SUBCMD_SET_CREATE(sub_valve_settings,
SHELL_CMD(open_t, NULL, "Set max open time (seconds)", cmd_valve_set_open_t),
SHELL_CMD(close_t, NULL, "Set max close time (seconds)", cmd_valve_set_close_t),
SHELL_CMD(end_curr_open,
NULL,
"Set end current threshold for opening (mA)",
cmd_valve_set_end_curr_open),
SHELL_CMD(end_curr_close,
NULL,
"Set end current threshold for closing (mA)",
cmd_valve_set_end_curr_close),
SHELL_CMD(obstacle_curr_open,
NULL,
"Set obstacle threshold for opening (mA)",
cmd_valve_set_obstacle_open),
SHELL_CMD(obstacle_curr_close,
NULL,
"Set obstacle threshold for closing (mA)",
cmd_valve_set_obstacle_close),
SHELL_SUBCMD_SET_END);
SHELL_STATIC_SUBCMD_SET_CREATE(valve_cmds,
SHELL_CMD(show, NULL, "Show valve configuration", cmd_valve_show),
SHELL_CMD(set, &sub_valve_settings, "Valve settings commands", NULL),
SHELL_CMD(open, NULL, "Open the valve", cmd_valve_open),
SHELL_CMD(close, NULL, "Close the valve", cmd_valve_close),
SHELL_CMD(stop, NULL, "Stop the valve movement", cmd_valve_stop),
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(valve, &valve_cmds, "Valve commands", NULL);

Some files were not shown because too many files have changed in this diff Show More