docs: Restructure and internationalize documentation

This commit is contained in:
2025-06-30 17:49:22 +02:00
parent 0fd6466d81
commit f9d4a3b971
10 changed files with 311 additions and 6 deletions

75
docs/concept.de.md Normal file
View File

@@ -0,0 +1,75 @@
[🇩🇪 Deutsch](concept.de.md) | [🇬🇧 English](concept.en.md)
# Konzept: Modulares Bewässerungssystem
Dieses Dokument beschreibt das Konzept für ein modulares, smartes Bewässerungssystem, das zentral über Home Assistant gesteuert wird.
## 1. Architekturübersicht
Das System ist in drei logische Ebenen aufgeteilt, um eine hohe Flexibilität und Wartbarkeit zu gewährleisten:
1. **Steuerungs-Ebene (Home Assistant):** Die gesamte Logik, alle Automationen und die Benutzeroberfläche residieren in Home Assistant. Dies ist das "Gehirn" des Systems.
2. **Gateway-Ebene (ESP32):** Ein reiner Protokoll-Übersetzer, der als Brücke zwischen dem Heimnetzwerk (WLAN/Thread) und dem physischen Bus-System der Anlage fungiert. Er enthält keine eigene Steuerungslogik.
3. **Aktor-/Sensor-Ebene (Slave-Nodes):** Robuste, spezialisierte Baugruppen, die über einen Bus angesteuert werden und die eigentlichen Aufgaben ausführen (Ventile schalten, Sensoren auslesen).
![Systemarchitektur](./img/architecture.de.svg)
## 2. Systemkomponenten
* **Wassertank:** Ein IBC-Container dient als Wasserreservoir.
* **Wasserzufuhr:** Ein "Regendieb" am Fallrohr leitet Regenwasser in den Tank.
* **Pumpe:** Eine Pumpe mit integriertem Druckausgleichsbehälter sorgt für den nötigen Wasserdruck.
* **Aktoren:** Motorisierte 12V-Kugelhähne zur Steuerung der Wasserabgänge.
* **Füllstandsensor (präzise):** Ein `QDY30A` mit 4-20mA Ausgang und RS485/MODBUS-Schnittstelle zur kontinuierlichen Messung des Wasserpegels.
* **Füllstandsensoren (Min/Max):** Optionale kapazitive Sensoren (`XKC-Y25-NPN` o.ä.) als redundante Absicherung gegen Leerlauf und Überlauf.
## 3. Gateway
Die zentrale Kommunikationsschnittstelle wird als "dummes" Gateway realisiert.
* **Hardware:** Ein `ESP32C6`-basiertes Board.
* **Funktion:** Das Gateway agiert als transparenter **MODBUS TCP/IP zu MODBUS RTU Konverter**. Es empfängt Befehle aus dem Heimnetzwerk und leitet sie an den RS485-Bus weiter und umgekehrt. Es führt keine eigene Steuerungslogik aus.
* **Anbindung an Home Assistant:** Die Verbindung erfolgt über das Heimnetzwerk, wahlweise per WLAN oder zukünftig eventuell über Thread/Matter. In Home Assistant wird die offizielle MODBUS-Integration genutzt, um das Gateway und die dahinterliegenden Slaves direkt zu adressieren.
## 4. Slave-Nodes
Die Slave-Nodes sind die Arbeitseinheiten im Feld. Um bei der Fertigung kleiner Stückzahlen (z.B. bei JLCPCB) den Aufwand gering zu halten, wird ein universelles Platinen-Design für alle Slave-Typen angestrebt.
* **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 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.
## 5. Bussystem: MODBUS-RTU über RS485
Als Bussystem wird konsequent auf MODBUS-RTU gesetzt.
* **Begründung:** Diese Wahl ist pragmatisch, da der Füllstandsensor bereits MODBUS voraussetzt. So wird für das gesamte System nur ein einziges, einfaches und weit verbreitetes Protokoll benötigt.
* **Physische Schicht:** Die Verkabelung erfolgt über RS485. Es wird handelsübliches Cat-7-Ethernetkabel mit RJ45-Steckern verwendet:
* 1 Adernpaar (verdrillt) für die Bus-Signale `A` und `B`.
* 3 Adernpaare parallel für `+12V` und `GND` zur Stromversorgung der Slaves.
## 6. Software
* **Betriebssystem (Slaves):** `Zephyr OS`. Es ist ein modernes, leistungsfähiges Echtzeitbetriebssystem, das eine saubere und wartbare Firmware-Struktur ermöglicht.
* **Logik-Implementierung:** Die gesamte Steuerungslogik (z.B. "Wenn Füllstand < 20% und Wochentag = Montag, dann schalte Ventil 3 für 10 Minuten ein") wird ausschliesslich in **Home Assistant** über dessen Automatisierungs-Engine abgebildet.
### 6.1. Firmware-Update der Slaves (OTA)
Die Firmware der Slaves kann im laufenden Betrieb über den Bus aktualisiert werden, ohne dass ein direkter physischer Zugriff nötig ist.
* **Konzept:** Es wird der `MCUBoot`-Bootloader genutzt. Dieser ist vom Kommunikationsprotokoll entkoppelt.
* **Ablauf:**
1. Ein Skript in Home Assistant liest die neue Firmware-Datei (`firmware.bin`).
2. Das Skript zerlegt die Datei in kleine Datenpakete und sendet diese nacheinander per MODBUS-Befehl an den Ziel-Slave.
3. Die laufende Applikation auf dem Slave empfängt diese Pakete und schreibt sie direkt in den sekundären Flash-Speicher ("Update-Slot").
4. Nach erfolgreicher Übertragung wird der Slave per Befehl neu gestartet.
5. `MCUBoot` prüft die Signatur des neuen Images, kopiert es in den primären Slot und startet es.
* **Sicherheit:** Die neue Firmware muss sich nach dem Start selbst als "funktionstüchtig" markieren. Tut sie dies nicht (z.B. wegen eines Absturzes), wird beim nächsten Neustart durch den Watchdog automatisch die vorherige, stabile Firmware-Version von `MCUBoot` wiederhergestellt.
## 7. Sicherheits- und Robustheitskonzepte
* **Fail-Safe-Verhalten:** Jeder Slave-Node implementiert einen Watchdog. Wenn über eine definierte Zeit (z.B. 15 Sekunden) keine gültige MODBUS-Abfrage vom Gateway eintrifft, geht der Slave in einen sicheren Zustand über: Alle Ventile werden geschlossen und Relais (z.B. für die Pumpe) werden ausgeschaltet.
* **Elektrische Schutzschaltungen:** Alle externen Schnittstellen werden geschützt. Die RS485-Busleitungen (`A`/`B`) werden auf jeder Platine mit TVS-Dioden gegen Überspannungen gesichert. Eingänge und Ausgänge erhalten einen Basis-ESD-Schutz.
* **Stromversorgung:** Die 12V-Busspannung wird auf jedem Slave-Node mit einem effizienten `TPS5430DDAR` Step-Down-Wandler auf die benötigten 3.3V für den Mikrocontroller und die Bustreiber geregelt.

75
docs/concept.en.md Normal file
View File

@@ -0,0 +1,75 @@
[🇩🇪 Deutsch](concept.de.md) | [🇬🇧 English](concept.en.md)
# Concept: Modular Irrigation System
This document describes the concept for a modular, smart irrigation system that is centrally controlled via Home Assistant.
## 1. Architecture Overview
The system is divided into three logical layers to ensure high flexibility and maintainability:
1. **Control Layer (Home Assistant):** All logic, automations, and the user interface reside in Home Assistant. This is the "brain" of the system.
2. **Gateway Layer (ESP32):** A pure protocol translator that acts as a bridge between the home network (WLAN/Thread) and the physical bus system of the plant. It contains no control logic of its own.
3. **Actor/Sensor Layer (Slave Nodes):** Robust, specialized modules that are controlled via a bus and perform the actual tasks (switching valves, reading sensors).
![System Architecture](./img/architecture.en.svg)
## 2. System Components
* **Water Tank:** An IBC container serves as a water reservoir.
* **Water Supply:** A "rain thief" on the downpipe directs rainwater into the tank.
* **Pump:** A pump with an integrated pressure expansion tank provides the necessary water pressure.
* **Actuators:** Motorized 12V ball valves to control the water outlets.
* **Level Sensor (precise):** A `QDY30A` with 4-20mA output and RS485/MODBUS interface for continuous measurement of the water level.
* **Level Sensors (Min/Max):** Optional capacitive sensors (`XKC-Y25-NPN` or similar) as redundant protection against running dry and overflowing.
## 3. Gateway
The central communication interface is implemented as a "dumb" gateway.
* **Hardware:** An `ESP32C6`-based board.
* **Function:** The gateway acts as a transparent **MODBUS TCP/IP to MODBUS RTU converter**. It receives commands from the home network and forwards them to the RS485 bus and vice versa. It does not execute its own control logic.
* **Connection to Home Assistant:** The connection is made via the home network, either via WLAN or in the future possibly via Thread/Matter. In Home Assistant, the official MODBUS integration is used to address the gateway and the slaves behind it directly.
## 4. Slave Nodes
The slave nodes are the working units in the field. To keep the effort low for small series production (e.g. at JLCPCB), a universal board design for all slave types is sought.
* **Microcontroller:** An `STM32G431PB`. Although powerful, it offers all the necessary peripherals (multiple UARTs, ADCs, CAN) and enables a uniform hardware and software design.
* **Peripherals per Node:**
* **Two High-Side Outputs (+12V):** Realized via a `VND7050AJ`. Perfect for controlling the 12V motor valves (`Open`/`Close`). The `Sense` line of the driver is read out via an AD converter to realize an end position detection without physical limit switches by measuring the motor current (motor current at standstill ≈ 0).
* **Two Low-Side Outputs (0V):** Outputs switched via N-channel MOSFETs. Can be used to control 12V LEDs in buttons or to switch the solid-state relay for the pump.
* **Two digital inputs:** Direct, protected inputs on the controller for connecting buttons or the capacitive NPN sensors.
## 5. Bus System: MODBUS-RTU via RS485
MODBUS-RTU is consistently used as the bus system.
* **Reasoning:** This choice is pragmatic, as the level sensor already requires MODBUS. This means that only a single, simple and widespread protocol is required for the entire system.
* **Physical Layer:** The cabling is done via RS485. Commercially available Cat-7 Ethernet cable with RJ45 plugs is used:
* 1 twisted pair for the bus signals `A` and `B`.
* 3 pairs of wires in parallel for `+12V` and `GND` to supply power to the slaves.
## 6. Software
* **Operating System (Slaves):** `Zephyr OS`. It is a modern, powerful real-time operating system that enables a clean and maintainable firmware structure.
* **Logic Implementation:** The entire control logic (e.g. "If level < 20% and day of the week = Monday, then switch on valve 3 for 10 minutes") is mapped exclusively in **Home Assistant** via its automation engine.
### 6.1. Firmware Update of the Slaves (OTA)
The firmware of the slaves can be updated during operation via the bus without the need for direct physical access.
* **Concept:** The `MCUBoot` bootloader is used. This is decoupled from the communication protocol.
* **Procedure:**
1. A script in Home Assistant reads the new firmware file (`firmware.bin`).
2. The script breaks the file down into small data packets and sends them one after the other to the target slave via MODBUS command.
3. The running application on the slave receives these packets and writes them directly to the secondary flash memory ("update slot").
4. After successful transmission, the slave is restarted by command.
5. `MCUBoot` checks the signature of the new image, copies it to the primary slot and starts it.
* **Security:** The new firmware must mark itself as "functional" after starting. If it does not do this (e.g. due to a crash), the previous, stable firmware version is automatically restored by `MCUBoot` on the next restart by the watchdog.
## 7. Safety and Robustness Concepts
* **Fail-Safe Behavior:** Each slave node implements a watchdog. If no valid MODBUS query is received from the gateway over a defined period of time (e.g. 15 seconds), the slave goes into a safe state: all valves are closed and relays (e.g. for the pump) are switched off.
* **Electrical Protection Circuits:** All external interfaces are protected. The RS485 bus lines (`A`/`B`) are protected against overvoltages with TVS diodes on each board. Inputs and outputs receive basic ESD protection.
* **Power Supply:** The 12V bus voltage is regulated on each slave node with an efficient `TPS5430DDAR` step-down converter to the required 3.3V for the microcontroller and the bus drivers.

View File

@@ -0,0 +1,86 @@
<svg width="650" height="500" viewBox="0 0 650 500" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
.box {
fill: #f0f7ff;
stroke: #0d47a1;
stroke-width: 1.5;
rx: 8;
}
.label-main {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: 500;
text-anchor: middle;
fill: #111;
}
.label-sub {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 12px;
text-anchor: middle;
fill: #444;
}
.label-arrow {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 13px;
text-anchor: middle;
fill: #333;
}
.arrow-line {
stroke: #333;
stroke-width: 2;
}
.arrow-head {
fill: #333;
}
.bus-line {
stroke: #212121;
stroke-width: 4;
}
.bus-connector {
stroke: #212121;
stroke-width: 2;
}
</style>
<marker id="arrowhead" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#333" />
</marker>
</defs>
<text x="325" y="30" class="label-main" font-size="20">Systemarchitektur</text>
<rect x="225" y="60" width="200" height="70" class="box"/>
<text x="325" y="90" class="label-main">Home Assistant</text>
<text x="325" y="110" class="label-sub">(Logik &amp; UI)</text>
<line x1="325" y1="130" x2="325" y2="170" class="arrow-line" marker-end="url(#arrowhead)"/>
<text x="445" y="155" class="label-arrow">WLAN / Thread</text>
<text x="445" y="170" class="label-arrow" font-size="11">(MODBUS TCP/IP)</text>
<rect x="225" y="180" width="200" height="70" class="box"/>
<text x="325" y="210" class="label-main">Gateway (ESP32C6)</text>
<text x="325" y="230" class="label-sub">(Protokoll-Übersetzer)</text>
<line x1="325" y1="250" x2="325" y2="300" class="bus-line"/>
<line x1="50" y1="300" x2="600" y2="300" class="bus-line"/>
<text x="500" y="285" class="label-arrow">RS485 Bus (MODBUS RTU)</text>
<line x1="125" y1="300" x2="125" y2="340" class="bus-connector"/>
<rect x="50" y="340" width="150" height="60" class="box"/>
<text x="125" y="365" class="label-main">Slave-Node</text>
<text x="125" y="385" class="label-sub">(Ventil, Taster)</text>
<line x1="325" y1="300" x2="325" y2="340" class="bus-connector"/>
<rect x="250" y="340" width="150" height="60" class="box"/>
<text x="325" y="365" class="label-main">Slave-Node</text>
<text x="325" y="385" class="label-sub">(Pumpe, Sensoren)</text>
<line x1="525" y1="300" x2="525" y2="340" class="bus-connector"/>
<rect x="450" y="340" width="150" height="60" class="box"/>
<text x="525" y="365" class="label-main">Füllstandsensor</text>
<text x="525" y="385" class="label-sub">(QDY30A)</text>
<text x="325" y="440" class="label-sub" font-size="20">...</text>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,78 @@
<svg xmlns="http://www.w3.org/2000/svg" width="203.36" height="143.44" version="1.1" viewBox="0 0 203.36 143.44">
<g transform="translate(-2.88 -78.28)">
<g stroke-width=".26458">
<g fill="#fff" stroke="#000">
<rect x="3.14" y="78.54" width="88.9" height="21.16" rx="2" ry="2"/>
<rect x="3.14" y="105.4" width="88.9" height="21.16" rx="2" ry="2"/>
<rect x="3.14" y="132.26" width="88.9" height="21.16" rx="2" ry="2"/>
</g>
<text x="47.59" y="86.9" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="86.9" text-anchor="middle">Control Layer</tspan>
<tspan x="47.59" y="93.45" text-anchor="middle">(Home Assistant)</tspan>
</text>
<text x="47.59" y="113.76" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="113.76" text-anchor="middle">Gateway Layer</tspan>
<tspan x="47.59" y="120.31" text-anchor="middle">(ESP32)</tspan>
</text>
<text x="47.59" y="140.62" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="140.62" text-anchor="middle">Actor/Sensor Layer</tspan>
<tspan x="47.59" y="147.17" text-anchor="middle">(Slaves)</tspan>
</text>
</g>
<path d="m47.59 99.7v5.7m-2.82-2.88h5.64" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.0583"/>
<path d="m47.59 126.56v5.7m-2.82-2.88h5.64" fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.0583"/>
<g stroke-width=".26458">
<g transform="translate(100.58 -2.64)">
<g fill="#fff" stroke="#000">
<rect x="3.14" y="108.04" width="88.9" height="21.16" rx="2" ry="2"/>
<rect x="3.14" y="134.9" width="88.9" height="21.16" rx="2" ry="2"/>
<rect x="3.14" y="161.76" width="88.9" height="21.16" rx="2" ry="2"/>
<rect x="3.14" y="188.62" width="88.9" height="21.16" rx="2" ry="2"/>
</g>
<text x="47.59" y="118.94" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="118.94" text-anchor="middle">Level Sensor</tspan>
<tspan x="47.59" y="125.49" font-size="4.23" text-anchor="middle">(MODBUS Slave)</tspan>
</text>
<text x="47.59" y="145.8" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="145.8" text-anchor="middle">Valve Control 1</tspan>
<tspan x="47.59" y="152.35" font-size="4.23" text-anchor="middle">(MODBUS Slave)</tspan>
</text>
<text x="47.59" y="172.66" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="172.66" text-anchor="middle">Valve Control n</tspan>
<tspan x="47.59" y="179.21" font-size="4.23" text-anchor="middle">(MODBUS Slave)</tspan>
</text>
<text x="47.59" y="199.52" font-family="sans-serif" font-size="5.64" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="47.59" y="199.52" text-anchor="middle">Pump</tspan>
<tspan x="47.59" y="206.07" font-size="4.23" text-anchor="middle">(switched via Slave)</tspan>
</text>
</g>
<path d="m148.17 132.26v-18.52m-2.82 2.82 2.82-2.82 2.82 2.82" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width=".529"/>
<path d="m148.17 159.12v-18.52m-2.82 2.82 2.82-2.82 2.82 2.82" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width=".529"/>
<path d="m148.17 185.98v-18.52m-2.82 2.82 2.82-2.82 2.82 2.82" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width=".529"/>
<path d="m148.17 212.84v-18.52m-2.82 2.82 2.82-2.82 2.82 2.82" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width=".529"/>
</g>
<g fill="none" stroke="#000" stroke-width=".52917">
<path d="m92.04 89.12h10.58"/>
<path d="m92.04 116h10.58"/>
</g>
<path d="m102.62 89.12v-5.29h45.55v26.45" fill="none" stroke="#000" stroke-width=".52917"/>
<path d="m102.62 116h45.55v-23.8h-45.55z" fill="#fff" stroke="#000" stroke-width=".26458"/>
<text x="125.4" y="100.3" font-family="sans-serif" font-size="4.23" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="125.4" y="100.3" text-anchor="middle">Home Network</tspan>
<tspan x="125.4" y="105.11" text-anchor="middle">(WLAN / Thread)</tspan>
</text>
<text x="125.4" y="114.09" font-family="sans-serif" font-size="4.23" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="125.4" y="114.09" text-anchor="middle">MODBUS TCP/IP</tspan>
</text>
<path d="m102.62 142.84h45.55v-23.8h-45.55z" fill="#fff" stroke="#000" stroke-width=".26458"/>
<text x="125.4" y="127.15" font-family="sans-serif" font-size="4.23" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="125.4" y="127.15" text-anchor="middle">Gateway</tspan>
<tspan x="125.4" y="131.96" text-anchor="middle">(Protocol Translator)</tspan>
</text>
<text x="125.4" y="140.94" font-family="sans-serif" font-size="4.23" letter-spacing="0" stroke-width=".26458" text-align="center" word-spacing="0">
<tspan x="125.4" y="140.94" text-anchor="middle">MODBUS RTU</tspan>
<tspan x="125.4" y="145.75" text-anchor="middle">(RS485 Bus)</tspan>
</text>
<path d="m92.04 142.84h10.58"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,85 @@
[🇩🇪 Deutsch](modbus-registers.de.md) | [🇬🇧 English](modbus-registers.en.md)
# MODBUS Register Map Definition v1.0
## 1. Einleitung
Dieses Dokument definiert die MODBUS-Register für die universellen Slave-Nodes des Bewässerungssystems.
### 1.1. Adressierungs-Philosophie
Alle Register sind in einer einzigen, durchgehenden Liste pro Register-Typ (`Input` oder `Holding`) definiert. Eine Spalte "Zugehörigkeit" ordnet die Funktion logisch zu. Die Adressen sind in Blöcken gruppiert, um Raum für zukünftige Erweiterungen zu lassen und die Lesbarkeit zu erhöhen.
* **`0x0000 - 0x000F`**: Ventilsteuerung & Status
* **`0x0010 - 0x001F`**: Digitale Ausgänge (LEDs / Relais)
* **`0x0020 - 0x002F`**: Digitale Eingänge (Taster / Sensoren)
* **`0x00F0 - 0x00FF`**: Allgemeine Gerätekonfiguration & Status
* **`0x0100 - 0x01FF`**: Firmware-Update-Mechanismus
### 1.2. Verwendete Funktionscodes
* **`0x03` (Read Holding Registers):** Zum Lesen von `4xxxx` Registern.
* **`0x04` (Read Input Registers):** Zum Lesen von `3xxxx` Registern.
* **`0x06` (Write Single Register):** Zum Schreiben eines einzelnen `4xxxx` Registers.
* **`0x10` (Write Multiple Registers):** Zum Schreiben mehrerer `4xxxx` Register am Stück.
## 2. Input Registers (3xxxx, Read-Only)
| 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. |
| **0x0100** | `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. |
| **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`. |
## 4. Detaillierter Firmware-Update-Prozess
Dieser Prozess ist zustandslos und robust gegen Übertragungsfehler.
1. **Client:** Wählt einen Chunk (max. 256 Bytes) aus der Firmware-Datei und berechnet dessen CRC16.
2. **Client:** Schreibt den Ziel-Offset (z.B. `0`) in `FWU_CHUNK_OFFSET_...` und die Grösse in `FWU_CHUNK_SIZE`.
3. **Client:** Schreibt die Chunk-Daten in den `FWU_DATA_BUFFER` (ab Adresse `0x0180`).
4. **Slave:** Empfängt die Daten, legt sie im RAM-Puffer ab und berechnet den CRC. Das Ergebnis wird in `FWU_LAST_CHUNK_CRC` (`30256`) bereitgestellt.
5. **Client:** Liest `FWU_LAST_CHUNK_CRC` und vergleicht den Wert mit dem selbst berechneten CRC.
* **Fehler:** Gehe zurück zu Schritt 3, um den gleichen Chunk erneut zu senden.
* **Erfolg:** Fährt mit dem nächsten Schritt fort.
6. **Client:** Schreibt den Befehl `1` ("Verify Chunk") in `FWU_COMMAND` (`40256`).
7. **Slave:** Empfängt den Befehl, nimmt den verifizierten Chunk aus dem RAM-Puffer und schreibt ihn an die richtige Stelle im Flash-Speicher.
8. **Client:** Fährt mit dem nächsten Chunk fort (zurück zu Schritt 1 mit neuem Offset und Daten).
9. **Letzter Chunk:** Nachdem der letzte Chunk übertragen und mit Befehl `1` ins Flash geschrieben wurde, schreibt der Client den Befehl `2` ("Finalize Update") in `FWU_COMMAND`.
10. **Slave:** Führt Abschlussprüfungen durch und startet neu, damit MCUBoot die Installation durchführt.
## Anhang: QDY30A Füllstandsensor Register
Diese Register gehören zum externen Füllstandsensor und können auf dem Bus ebenfalls adressiert werden. Es handelt sich laut Hersteller um Holding Registers (`4xxxx`), die mit Funktionscode `0x03` gelesen werden.
| Adresse (hex) | Name | R/W | Beschreibung |
| :------------ | :------------------------- | :-- | :---------------------------------------------------------------------------------------------------------------------------------------- |
| **0x0000** | `NODE_ADRESSE` | 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. |

View File

@@ -0,0 +1,85 @@
[🇩🇪 Deutsch](modbus-registers.de.md) | [🇬🇧 English](modbus-registers.en.md)
# MODBUS Register Map Definition v1.0
## 1. Introduction
This document defines the MODBUS registers for the universal slave nodes of the irrigation system.
### 1.1. Addressing Philosophy
All registers are defined in a single, continuous list per register type (`Input` or `Holding`). A "Category" column logically assigns the function. The addresses are grouped in blocks to leave room for future extensions and to increase readability.
* **`0x0000 - 0x000F`**: Valve control & status
* **`0x0010 - 0x001F`**: Digital outputs (LEDs / relays)
* **`0x0020 - 0x002F`**: Digital inputs (buttons / sensors)
* **`0x00F0 - 0x00FF`**: General device configuration & status
* **`0x0100 - 0x01FF`**: Firmware update mechanism
### 1.2. Used Function Codes
* **`0x03` (Read Holding Registers):** For reading `4xxxx` registers.
* **`0x04` (Read Input Registers):** For reading `3xxxx` registers.
* **`0x06` (Write Single Register):** For writing a single `4xxxx` register.
* **`0x10` (Write Multiple Registers):** For writing multiple `4xxxx` registers at once.
## 2. Input Registers (3xxxx, Read-Only)
| Address (hex) | Name | Category | Description |
| :--- | :--- | :--- | :--- |
| **0x0000** | `VALVE_STATE_MOVEMENT` | Valve | Combined status register. **High-Byte**: Movement (`0`=Idle, `1`=Opening, `2`=Closing, `3`=Error). **Low-Byte**: State (`0`=Closed, `1`=Open). |
| **0x0001** | `MOTOR_CURRENT_MA` | Valve | Current motor current in milliamperes (mA). |
| **0x0020** | `DIGITAL_INPUTS_STATE` | Inputs | Bitmask of the digital inputs. Bit 0: Input 1, Bit 1: Input 2. `1`=Active. |
| **0x0021** | `BUTTON_EVENTS` | Inputs | Event flags for buttons (Clear-on-Read). Bit 0: Button 1 pressed. Bit 1: Button 2 pressed. |
| **0x00F0** | `FIRMWARE_VERSION_MAJOR_MINOR` | System | e.g. `0x0102` for v1.2. |
| **0x00F1** | `FIRMWARE_VERSION_PATCH` | System | e.g. `3` for v1.2.3. |
| **0x00F2** | `DEVICE_STATUS` | System | `0`=OK, `1`=General error. |
| **0x00F3** | `UPTIME_SECONDS_LOW` | System | Lower 16 bits of the uptime in seconds. |
| **0x00F4** | `UPTIME_SECONDS_HIGH` | System | Upper 16 bits of the uptime. |
| **0x0100** | `FWU_LAST_CHUNK_CRC` | Firmware-Update | Contains the CRC16 of the last data chunk received in the buffer. |
## 3. Holding Registers (4xxxx, Read/Write)
| Address (hex) | Name | Category | Description |
| :--- | :--- | :--- | :--- |
| **0x0000** | `VALVE_COMMAND` | Valve | `1`=Open, `2`=Close, `0`=Stop movement. |
| **0x0001** | `MAX_OPENING_TIME_S` | Valve | Safety timeout in seconds for the opening process. |
| **0x0002** | `MAX_CLOSING_TIME_S` | Valve | Safety timeout in seconds for the closing process. |
| **0x0010** | `DIGITAL_OUTPUTS_STATE` | Outputs | Bitmask for reading and writing the outputs. Bit 0: Output 1, Bit 1: Output 2. `1`=ON, `0`=OFF. |
| **0x00F0** | `WATCHDOG_TIMEOUT_S` | System | Timeout of the fail-safe watchdog in seconds. `0`=Disabled. |
| **0x0100** | `FWU_COMMAND` | Firmware-Update | `1`: **Verify Chunk**: The last transmitted chunk was found to be valid by the client. The slave should now write it to flash. `2`: **Finalize Update**: All chunks have been transmitted. Finalize installation and restart. |
| **0x0101** | `FWU_CHUNK_OFFSET_LOW` | Firmware-Update | Lower 16 bits of the 32-bit offset to which the next chunk is to be written. |
| **0x0102** | `FWU_CHUNK_OFFSET_HIGH` | Firmware-Update | Upper 16 bits of the 32-bit offset. |
| **0x0103** | `FWU_CHUNK_SIZE` | Firmware-Update | Size of the next chunk in bytes (max. 256). |
| **0x0180** | `FWU_DATA_BUFFER` | Firmware-Update | **Start address** of a 128x16-bit buffer (256 bytes). Corresponds to registers `40384` to `40511`. |
## 4. Detailed Firmware Update Process
This process is stateless and robust against transmission errors.
1. **Client:** Selects a chunk (max. 256 bytes) from the firmware file and calculates its CRC16.
2. **Client:** Writes the target offset (e.g. `0`) to `FWU_CHUNK_OFFSET_...` and the size to `FWU_CHUNK_SIZE`.
3. **Client:** Writes the chunk data to the `FWU_DATA_BUFFER` (from address `0x0180`).
4. **Slave:** Receives the data, places it in the RAM buffer and calculates the CRC. The result is provided in `FWU_LAST_CHUNK_CRC` (`30256`).
5. **Client:** Reads `FWU_LAST_CHUNK_CRC` and compares the value with the self-calculated CRC.
* **Error:** Go back to step 3 to send the same chunk again.
* **Success:** Continues with the next step.
6. **Client:** Writes the command `1` ("Verify Chunk") to `FWU_COMMAND` (`40256`).
7. **Slave:** Receives the command, takes the verified chunk from the RAM buffer and writes it to the correct location in the flash memory.
8. **Client:** Continues with the next chunk (back to step 1 with new offset and data).
9. **Last Chunk:** After the last chunk has been transferred and written to flash with command `1`, the client writes the command `2` ("Finalize Update") to `FWU_COMMAND`.
10. **Slave:** Performs final checks and restarts so that MCUBoot can perform the installation.
## Appendix: QDY30A Level Sensor Registers
These registers belong to the external level sensor and can also be addressed on the bus. According to the manufacturer, these are Holding Registers (`4xxxx`) that are read with function code `0x03`.
| Address (hex) | Name | R/W | Description |
| :--- | :--- | :-- | :--- |
| **0x0000** | `NODE_ADDRESS` | R/W | Device address of the sensor (1-255). |
| **0x0001** | `BAUDRATE` | R/W | `0`=1200, `1`=2400, `2`=4800, `3`=9600, `4`=19200, `5`=38400, `6`=57600, `7`=115200. |
| **0x0002** | `UNIT` | R/W | `0`=None, `1`=cm, `2`=mm, `3`=MPa, `4`=Pa, `5`=kPa. |
| **0x0003** | `DECIMAL_PLACES` | R/W | Number of decimal places for the measured value (0-3). |
| **0x0004** | `CURRENT_MEASUREMENT` | R | The scaled measured value as a signed 16-bit integer. |
| **0x0005** | `MEASURING_RANGE_ZERO_POINT` | R/W | Raw value for the zero point of the scale. |
| **0x0006** | `MEASURING_RANGE_END_POINT` | R/W | Raw value for the end point of the scale. |

33
docs/planning.de.md Normal file
View File

@@ -0,0 +1,33 @@
[🇩🇪 Deutsch](planning.de.md) | [🇬🇧 English](planning.en.md)
# Projektplan: Modulares Bewässerungssystem
| Abgehakt | Aufgabe | Datum | Bemerkungen |
| :---: | :--- | :--- | :--- |
| ✅ | **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. |
| ☐ | **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 | | 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. |
| ☐ | **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. |
| ☐ | **Phase 3: Hardware-Design und Prototypenbau** | | **Ziel:** Von der Entwicklung auf dem Eval-Board zum massgeschneiderten PCB. |
| ☐ | 3.1 Schaltplan und PCB-Layout für Slave-Node entwerfen | | Basierend auf den Erfahrungen mit dem Eval-Board. |
| ☐ | 3.2 Prototypen-Platinen bestellen und bestücken | | Z.B. bei JLCPCB. THT-Komponenten (Stecker etc.) selbst löten. |
| ☐ | 3.3 Hardware-Inbetriebnahme des ersten Prototyps | | Spannungen prüfen, Firmware aufspielen und die Tests aus Phase 2 wiederholen, um die Hardware zu validieren. |
| ☐ | 3.4 Flash-Schreibroutine für Firmware-Update implementieren | | Den in Schritt 2.2 im RAM validierten Prozess nun auf den echten Flash-Speicher anwenden. |
| ☐ | **Phase 4: Gateway Entwicklung (ESP32 Eval-Board)** | | **Ziel:** Die Brücke von der RS485-Welt ins Heimnetzwerk bauen. |
| ☐ | 4.1 Gateway-Firmware (ESPHome) erstellen | | Einfaches MODBUS TCP zu RTU Gateway auf dem ESP32C6 Eval-Board aufsetzen. |
| ☐ | 4.2 Gateway mit Slave-Node Prototyp verbinden und testen | | Test der Kette: PC (als MODBUS TCP Client) -> WLAN -> Gateway -> RS485 -> Slave. |
| ☐ | **Phase 5: System-Integration in Home Assistant** | | **Ziel:** Das System "smart" machen. |
| ☐ | 5.1 MODBUS-Integration in Home Assistant konfigurieren | | Anlegen der Sensoren und Entitäten für den Slave-Node in der `configuration.yaml` oder über die UI. |
| ☐ | 5.2 Dashboards und Automationen in HA erstellen | | Visualisierung der Zustände (Ventil, Pumpe etc.) und Erstellen der eigentlichen Bewässerungs-Logik. |
| ☐ | 5.3 Python-Skript für Firmware-Update in HA entwickeln | | Implementierung des Chunk-basierten Uploads als Skript, das aus HA heraus aufgerufen werden kann. |
| ☐ | **Phase 6: Aufbau und Inbetriebnahme** | | **Ziel:** Das fertige System installieren. |
| ☐ | 6.1 Alle benötigten Slave-Nodes aufbauen und testen | | Jeden Slave einzeln mit dem PC via USB-Adapter testen und die MODBUS-Adresse konfigurieren. |
| ☐ | 6.2 System final installieren und verkabeln | | Einbau der Komponenten in den Schuppen, Verkabelung des RS485-Busses. |
| ☐ | 6.3 Gesamtsystemtest und Kalibrierung | | Füllstandsensor kalibrieren, Laufzeiten der Ventile prüfen, Fail-Safe-Verhalten testen. |

33
docs/planning.en.md Normal file
View File

@@ -0,0 +1,33 @@
[🇩🇪 Deutsch](planning.de.md) | [🇬🇧 English](planning.en.md)
# Project Plan: Modular Irrigation System
| Done | Task | Date | Remarks |
| :---: | :--- | :--- | :--- |
| ✅ | **Phase 0: Planning & Definition** | | |
| ✅ | Create and finalize concept | 2025-06-30 | Architecture, components and basic architecture are defined. |
| ✅ | Define MODBUS Register Map | 2025-06-30 | The "API" of the slaves is defined and forms the basis for software development. |
| ☐ | **Phase 1: Slave Node Prototype (STM32 Eval-Board)** | | **Goal:** A single slave is brought to life on the eval board. |
| ☐ | 1.1 Set up development environment for STM32/Zephyr | | Install toolchain, VS Code, Zephyr-SDK, MCUBoot etc. and get a "Hello World" running. |
| ☐ | 1.2 Create basic firmware for slave node | | Implement hardware abstraction (GPIOs, ADC, UART for RS485). |
| ☐ | 1.3 Implement MODBUS-RTU stack on the slave | | Based on the defined register map. Initially only read functions (status, version). |
| ☐ | 1.4 Implement core logic (e.g. valve control) | | Implementation of the `VALVE_STATE_MOVEMENT` logic, current measurement for end positions etc. |
| ☐ | **Phase 2: Verification of the Slave Firmware** | | **Goal:** Prove that the slave adheres exactly to the MODBUS specification. |
| ☐ | 2.1 Test slave node with PC via USB-MODBUS adapter | | **Critical milestone.** Read & write the registers with tools like "QModMaster" or a Python script. The slave firmware is thus validated independently of the gateway. |
| ☐ | 2.2 Test firmware update mechanism | | Test the complete update process (chunking, CRC check) with a script from the PC. The slave initially only writes the firmware to an unused RAM area. |
| ☐ | **Phase 3: Hardware Design and Prototype Construction** | | **Goal:** From development on the eval board to a customized PCB. |
| ☐ | 3.1 Design schematic and PCB layout for slave node | | Based on the experience with the eval board. |
| ☐ | 3.2 Order and assemble prototype boards | | E.g. at JLCPCB. Solder THT components (connectors etc.) yourself. |
| ☐ | 3.3 Hardware commissioning of the first prototype | | Check voltages, upload firmware and repeat the tests from phase 2 to validate the hardware. |
| ☐ | 3.4 Implement flash write routine for firmware update | | Apply the process validated in RAM in step 2.2 to the real flash memory. |
| ☐ | **Phase 4: Gateway Development (ESP32 Eval-Board)** | | **Goal:** Build the bridge from the RS485 world to the home network. |
| ☐ | 4.1 Create gateway firmware (ESPHome) | | Set up a simple MODBUS TCP to RTU gateway on the ESP32C6 eval board. |
| ☐ | 4.2 Connect and test gateway with slave node prototype | | Test the chain: PC (as MODBUS TCP client) -> WLAN -> Gateway -> RS485 -> Slave. |
| ☐ | **Phase 5: System Integration in Home Assistant** | | **Goal:** Make the system "smart". |
| ☐ | 5.1 Configure MODBUS integration in Home Assistant | | Create the sensors and entities for the slave node in `configuration.yaml` or via the UI. |
| ☐ | 5.2 Create dashboards and automations in HA | | Visualization of the states (valve, pump etc.) and creation of the actual irrigation logic. |
| ☐ | 5.3 Develop Python script for firmware update in HA | | Implementation of the chunk-based upload as a script that can be called from HA. |
| ☐ | **Phase 6: Setup and Commissioning** | | **Goal:** Install the finished system. |
| ☐ | 6.1 Build and test all required slave nodes | | Test each slave individually with the PC via USB adapter and configure the MODBUS address. |
| ☐ | 6.2 Final installation and cabling of the system | | Installation of the components in the shed, cabling of the RS485 bus. |
| ☐ | 6.3 Overall system test and calibration | | Calibrate level sensor, check valve running times, test fail-safe behavior. |