From 33f2a15cf30fdfcc93d7ba3d98f1ce526cd3b77d Mon Sep 17 00:00:00 2001 From: Eduard Iten Date: Tue, 1 Jul 2025 22:56:30 +0200 Subject: [PATCH] docs(slave_node): Add Doxygen comments to main.c - Add Doxygen-compliant comments to functions, enums, and state variables in `main.c`. - This provides a foundation for automatically generating source code documentation. - Remove the separate, now redundant, `firmware-manual.de.md` file. --- docs/firmware-manual.de.md | 82 --------------- software/apps/slave_node/src/main.c | 152 +++++++++++++++++++++++----- 2 files changed, 127 insertions(+), 107 deletions(-) delete mode 100644 docs/firmware-manual.de.md diff --git a/docs/firmware-manual.de.md b/docs/firmware-manual.de.md deleted file mode 100644 index 6e72b3b..0000000 --- a/docs/firmware-manual.de.md +++ /dev/null @@ -1,82 +0,0 @@ -# Firmware-Handbuch (Slave Node) - -**Status: Work in Progress (WIP)** - -Dieses Dokument beschreibt den aktuellen Funktionsumfang der Firmware für den `slave_node` des Bewässerungssystems. - -## Inhaltsverzeichnis -- [1. Übersicht](#1-übersicht) -- [2. Implementierte Features](#2-implementierte-features) - - [2.1. Modbus RTU Server](#21-modbus-rtu-server) - - [2.2. Ventilsteuerung (Simuliert)](#22-ventilsteuerung-simuliert) - - [2.3. Digitale Ein- und Ausgänge](#23-digitale-ein--und-ausgänge) - - [2.4. Systemfunktionen](#24-systemfunktionen) - - [2.5. Fail-Safe Watchdog](#25-fail-safe-watchdog) - - [2.6. Simulierter Firmware-Update-Prozess](#26-simulierter-firmware-update-prozess) - - [2.7. Persistente Einstellungen](#27-persistente-einstellungen) -- [3. Kommandozeilen-Interface (Shell)](#3-kommandozeilen-interface-shell) - -## 1. Übersicht - -Die Firmware macht aus einem Standard-Mikrocontroller (wie dem Bluepill STM32F103) einen intelligenten Modbus-Slave, der zur Steuerung eines motorisierten Ventils und zur Überwachung einfacher digitaler Sensoren dient. - -## 2. Implementierte Features - -### 2.1. Modbus RTU Server - -- **Schnittstelle:** Läuft auf der Standard-UART-Schnittstelle des Boards. -- **Konfiguration:** Baudrate und Slave-ID können über die Shell zur Laufzeit geändert und persistent gespeichert werden. -- **Register-Map:** Implementiert die in `docs/modbus-registers.de.md` definierte Register-Map. - -### 2.2. Ventilsteuerung (Simuliert) - -- **Zustandsmaschine:** Eine virtuelle Ventilsteuerung simuliert die Zustände "Geöffnet", "Geschlossen" und die Bewegungen "Öffnet" und "Schließt". -- **Zeitbasierte Bewegung:** Die Dauer für einen vollständigen Öffnungs- oder Schließvorgang kann über Modbus-Register (`MAX_OEFFNUNGSZEIT_S`, `MAX_SCHLIESSZEIT_S`) konfiguriert werden. -- **Befehle:** Das Ventil kann über das `VENTIL_BEFEHL`-Register gestartet, gestoppt und in die jeweilige Richtung bewegt werden. - -### 2.3. Digitale Ein- und Ausgänge - -- **Ausgänge:** Der Zustand von zwei digitalen Ausgängen kann über das `DIGITAL_AUSGAENGE_ZUSTAND`-Register gelesen und geschrieben werden. In der aktuellen Implementierung ist dies rein virtuell und wird nur geloggt. -- **Eingänge:** Der Zustand von zwei digitalen Eingängen wird im `DIGITAL_EINGAENGE_ZUSTAND`-Register abgebildet. Diese sind für den Anschluss von Tastern oder Sensoren vorgesehen. -- **Taster-Events:** Das `TASTER_EVENTS`-Register speichert Bit-Flags für "gedrückt"-Events und wird beim Lesen automatisch zurückgesetzt (Clear-on-Read). - -### 2.4. Systemfunktionen - -- **Uptime:** Die Firmware zählt die Sekunden seit dem letzten Start und stellt sie über zwei 16-Bit-Register zur Verfügung. -- **Firmware-Version:** Die Version (`vX.Y.Z`) ist fest einkompiliert und kann über die entsprechenden Register ausgelesen werden. -- **Gerätestatus:** Ein einfaches Statusregister (`DEVICE_STATUS`) zeigt den allgemeinen Zustand des Geräts an (`0` = OK). - -### 2.5. Fail-Safe Watchdog - -- **Funktionsweise:** Ein Timer wird bei jeder erfolgreichen Modbus-Kommunikation zurückgesetzt. -- **Timeout:** Läuft der Timer ab (Timeout konfigurierbar über `WATCHDOG_TIMEOUT_S`), wird als Sicherheitsmaßnahme automatisch der Befehl zum Schließen des Ventils ausgelöst. -- **Aktivierung:** Das Schreiben eines Wertes `> 0` in das Register aktiviert den Watchdog. Das Schreiben einer `0` deaktiviert ihn. - -### 2.6. Simulierter Firmware-Update-Prozess - -- **Protokoll:** Der in der Modbus-Dokumentation beschriebene, zustandslose Update-Prozess ist vollständig implementiert. -- **Datenempfang:** Das Gerät kann Firmware-Chunks (max. 256 Bytes) im `FWU_DATA_BUFFER` empfangen. -- **CRC-Verifizierung:** Nach dem Empfang eines Chunks berechnet der Slave dessen CRC16 und stellt das Ergebnis im `FWU_LAST_CHUNK_CRC`-Register bereit, damit der Client die Übertragung überprüfen kann. -- **Flash-Schreiben (Simuliert):** Der Befehl zum Schreiben eines verifizierten Chunks ins Flash wird aktuell nur geloggt. Es finden keine echten Schreiboperationen statt. -- **Finalisierung (Simuliert):** Der Befehl zum Abschluss des Updates und Neustart wird ebenfalls nur geloggt. - -### 2.7. Persistente Einstellungen - -- **Technologie:** Das Zephyr Settings Subsystem wird mit NVS (Non-Volatile Storage) auf einer dedizierten Flash-Partition verwendet. -- **Gespeicherte Werte:** - - Modbus Baudrate - - Modbus Slave ID - - Maximale Öffnungszeit des Ventils - - Maximale Schließzeit des Ventils - -## 3. Kommandozeilen-Interface (Shell) - -Die Firmware bietet eine Shell über die RTT-Schnittstelle für Debugging und Konfiguration. - -- **`modbus get`**: Zeigt die aktuelle Modbus-Konfiguration (Baudrate, Slave ID). -- **`modbus set baudrate `**: Setzt die Baudrate. -- **`modbus set slave_id `**: Setzt die Slave ID. -- **`valve show_config`**: Zeigt die aktuelle Ventil-Konfiguration. -- **`valve set_open_time `**: Setzt die maximale Öffnungszeit. -- **`valve set_close_time `**: Setzt die maximale Schließzeit. -- **`reset`**: Führt einen Neustart des Geräts durch. \ No newline at end of file diff --git a/software/apps/slave_node/src/main.c b/software/apps/slave_node/src/main.c index b5572aa..dc3789c 100644 --- a/software/apps/slave_node/src/main.c +++ b/software/apps/slave_node/src/main.c @@ -1,8 +1,13 @@ -/* - * Copyright (c) 2020 PHYTEC Messtechnik GmbH - * Copyright (c) 2022 Nordic Semiconductor ASA +/** + * @file main.c + * @brief Main application for the Irrigation System Slave Node. * - * SPDX-License-Identifier: Apache-2.0 + * This file contains the main logic for the Modbus RTU slave node, including + * valve control, digital I/O, system status, and a simulated firmware update process. + * + * @copyright Copyright (c) 2020 PHYTEC Messtechnik GmbH + * @copyright Copyright (c) 2022 Nordic Semiconductor ASA + * @license SPDX-License-Identifier: Apache-2.0 */ #include @@ -23,49 +28,72 @@ LOG_MODULE_REGISTER(mbs_sample, LOG_LEVEL_INF); #define APP_VERSION_MINOR 0 #define APP_VERSION_PATCH 0 -/* Register Definitions from Documentation */ +/** + * @brief Modbus Input Register Addresses. + */ enum { - REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000, REG_INPUT_MOTOR_CURRENT_MA = 0x0001, - REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020, REG_INPUT_BUTTON_EVENTS = 0x0021, - REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0, REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1, - REG_INPUT_DEVICE_STATUS = 0x00F2, REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3, - REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4, REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100, -}; -enum { - REG_HOLDING_VALVE_COMMAND = 0x0000, REG_HOLDING_MAX_OPENING_TIME_S = 0x0001, - REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002, REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010, - REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0, REG_HOLDING_FWU_COMMAND = 0x0100, - REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101, REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102, - REG_HOLDING_FWU_CHUNK_SIZE = 0x0103, REG_HOLDING_FWU_DATA_BUFFER = 0x0180, + /* Valve Control & Status */ + REG_INPUT_VALVE_STATE_MOVEMENT = 0x0000, + REG_INPUT_MOTOR_CURRENT_MA = 0x0001, + /* Digital Inputs */ + REG_INPUT_DIGITAL_INPUTS_STATE = 0x0020, + REG_INPUT_BUTTON_EVENTS = 0x0021, + /* System Config & Status */ + REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR = 0x00F0, + REG_INPUT_FIRMWARE_VERSION_PATCH = 0x00F1, + REG_INPUT_DEVICE_STATUS = 0x00F2, + REG_INPUT_UPTIME_SECONDS_LOW = 0x00F3, + REG_INPUT_UPTIME_SECONDS_HIGH = 0x00F4, + /* Firmware Update */ + REG_INPUT_FWU_LAST_CHUNK_CRC = 0x0100, }; -/* Valve Simulation */ +/** + * @brief Modbus Holding Register Addresses. + */ +enum { + /* Valve Control */ + REG_HOLDING_VALVE_COMMAND = 0x0000, + REG_HOLDING_MAX_OPENING_TIME_S = 0x0001, + REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002, + /* Digital Outputs */ + REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010, + /* System Config */ + REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0, + /* Firmware Update */ + REG_HOLDING_FWU_COMMAND = 0x0100, + REG_HOLDING_FWU_CHUNK_OFFSET_LOW = 0x0101, + REG_HOLDING_FWU_CHUNK_OFFSET_HIGH = 0x0102, + REG_HOLDING_FWU_CHUNK_SIZE = 0x0103, + REG_HOLDING_FWU_DATA_BUFFER = 0x0180, +}; + +/** @brief Represents the physical state of the valve. */ enum valve_state { VALVE_STATE_CLOSED, VALVE_STATE_OPEN }; +/** @brief Represents the current movement of the valve. */ enum valve_movement { VALVE_MOVEMENT_IDLE, VALVE_MOVEMENT_OPENING, VALVE_MOVEMENT_CLOSING, VALVE_MOVEMENT_ERROR }; + +/* --- State Variables --- */ static enum valve_state current_state = VALVE_STATE_CLOSED; static enum valve_movement current_movement = VALVE_MOVEMENT_IDLE; static uint16_t max_opening_time_s = 60; static uint16_t max_closing_time_s = 60; static struct k_work_delayable valve_work; -/* Digital I/O State */ static uint16_t digital_outputs_state = 0; static uint16_t digital_inputs_state = 0; static uint16_t button_events = 0; -/* System State & Watchdog */ -static uint16_t device_status = 0; // 0 = OK +static uint16_t device_status = 0; static uint16_t watchdog_timeout_s = 0; static struct k_timer watchdog_timer; -/* Firmware Update State */ #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; -/* Modbus Configuration */ static int modbus_iface; static struct modbus_iface_param server_param = { .mode = MODBUS_MODE_RTU, @@ -73,6 +101,12 @@ static struct modbus_iface_param server_param = { .serial = { .baud = 19200, .parity = UART_CFG_PARITY_NONE }, }; +/** + * @brief Triggers the closing of the valve. + * + * If the valve is currently open, this function initiates the closing movement + * and schedules the completion via the valve_work handler. + */ static void close_valve_action(void) { if (current_state == VALVE_STATE_OPEN) { @@ -81,12 +115,26 @@ static void close_valve_action(void) } } +/** + * @brief Watchdog timer expiration handler. + * + * This function is called by the kernel when the Modbus watchdog timer expires. + * It triggers the fail-safe action of closing the valve. + * + * @param timer_id Pointer to the timer instance that expired. + */ static void watchdog_timer_handler(struct k_timer *timer_id) { LOG_WRN("Modbus watchdog expired! Closing valve as a fail-safe."); close_valve_action(); } +/** + * @brief Resets the Modbus communication watchdog timer. + * + * This function should be called on any valid Modbus communication to prevent + * the fail-safe timeout from occurring. + */ static inline void reset_watchdog(void) { if (watchdog_timeout_s > 0) { @@ -94,6 +142,14 @@ static inline void reset_watchdog(void) } } +/** + * @brief Work handler for completing valve movement. + * + * This function is called from a workqueue to finalize the state of the valve + * after a time-based movement simulation has completed. + * + * @param work Pointer to the work item. + */ static void valve_work_handler(struct k_work *work) { if (current_movement == VALVE_MOVEMENT_OPENING) { @@ -105,6 +161,12 @@ static void valve_work_handler(struct k_work *work) current_movement = VALVE_MOVEMENT_IDLE; } +/** + * @brief Modbus callback for reading holding registers. + * @param addr Register address. + * @param reg Pointer to store the read value. + * @return 0 on success, negative error code otherwise. + */ static int holding_reg_rd(uint16_t addr, uint16_t *reg) { reset_watchdog(); @@ -118,6 +180,12 @@ static int holding_reg_rd(uint16_t addr, uint16_t *reg) return 0; } +/** + * @brief Modbus callback for writing holding registers. + * @param addr Register address. + * @param reg Value to write. + * @return 0 on success, negative error code otherwise. + */ static int holding_reg_wr(uint16_t addr, uint16_t reg) { reset_watchdog(); @@ -181,6 +249,12 @@ static int holding_reg_wr(uint16_t addr, uint16_t reg) return 0; } +/** + * @brief Modbus callback for reading input registers. + * @param addr Register address. + * @param reg Pointer to store the read value. + * @return 0 on success, negative error code otherwise. + */ static int input_reg_rd(uint16_t addr, uint16_t *reg) { reset_watchdog(); @@ -216,7 +290,27 @@ void valve_set_max_open_time(uint16_t seconds) { max_opening_time_s = seconds; s void valve_set_max_close_time(uint16_t seconds) { max_closing_time_s = seconds; settings_save_one("valve/max_close_time", &max_closing_time_s, sizeof(max_closing_time_s)); } uint16_t valve_get_max_open_time(void) { return max_opening_time_s; } uint16_t valve_get_max_close_time(void) { return max_closing_time_s; } -static int settings_load_cb(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { /* ... */ return -ENOENT; } +static int settings_load_cb(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { + const char *next; + int rc; + if (settings_name_steq(name, "baudrate", &next) && !next) { + rc = read_cb(cb_arg, &server_param.serial.baud, sizeof(server_param.serial.baud)); + return (rc < 0) ? rc : 0; + } + if (settings_name_steq(name, "unit_id", &next) && !next) { + rc = read_cb(cb_arg, &server_param.server.unit_id, sizeof(server_param.server.unit_id)); + return (rc < 0) ? rc : 0; + } + if (settings_name_steq(name, "max_open_time", &next) && !next) { + rc = read_cb(cb_arg, &max_opening_time_s, sizeof(max_opening_time_s)); + return (rc < 0) ? rc : 0; + } + if (settings_name_steq(name, "max_close_time", &next) && !next) { + rc = read_cb(cb_arg, &max_closing_time_s, sizeof(max_closing_time_s)); + return (rc < 0) ? rc : 0; + } + return -ENOENT; +} SETTINGS_STATIC_HANDLER_DEFINE(modbus, "modbus", NULL, settings_load_cb, NULL, NULL); SETTINGS_STATIC_HANDLER_DEFINE(valve, "valve", NULL, settings_load_cb, NULL, NULL); @@ -229,6 +323,14 @@ static int init_modbus_server(void) return modbus_init_server(modbus_iface, server_param); } +/** + * @brief Main entry point for the application. + * + * Initializes all subsystems including Modbus, settings, and timers, + * then enters an idle state. + * + * @return 0 on success, negative error code otherwise. + */ int main(void) { LOG_INF("Starting Irrigation System Slave Node"); @@ -247,4 +349,4 @@ int main(void) LOG_INF("Irrigation System Slave Node started successfully"); return 0; -} +} \ No newline at end of file