Compare commits
122 Commits
ade514cbf3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c4a728c17 | |||
| d92a1d9533 | |||
| 9325fa20c8 | |||
| 08c47f00f8 | |||
| 1cba00df8c | |||
| 35bd208cc0 | |||
| 48cfcd5d4c | |||
| d76b897eb2 | |||
| 0713f8255e | |||
| fc089e5a33 | |||
| ef966cb078 | |||
| 6f304efb57 | |||
| e1ae96506d | |||
| 6cb17be451 | |||
| 54e991294b | |||
| 0227e54198 | |||
| c3c23efc95 | |||
| 4466b677a6 | |||
| dcbd02ad7a | |||
| 8467b3e347 | |||
| fc0add8583 | |||
| 66cdc3ae27 | |||
| 32bb77926f | |||
| 4df0181d7f | |||
| d6fb501594 | |||
|
|
76d0d0647c | ||
| 3de42a46c2 | |||
| ddaaa8988d | |||
| b937c52bcc | |||
| 3c2235733b | |||
| a3e8d5c168 | |||
| 5fd904de9e | |||
| 92bb171e85 | |||
| bd8a7a766c | |||
| 8f89713866 | |||
| bf29061db6 | |||
| c1622bb01c | |||
| 222ffea568 | |||
| a9a0626913 | |||
| b11f844415 | |||
| 2e8a86bc54 | |||
| 224adccf6b | |||
| 9b7159d5a4 | |||
| bc327acc41 | |||
| c9b0f38576 | |||
| edf0fb2563 | |||
| 537d76ef5d | |||
| 45d011952f | |||
| bb25134b6c | |||
| 9f96384aa5 | |||
| b543579393 | |||
| 69cf7e9511 | |||
| 8df7aef51b | |||
| f6ee0a5122 | |||
| 6c1ff0c4df | |||
| 3f0d5a76c6 | |||
| 10a770de59 | |||
| 1b0519aadf | |||
| e429a0874d | |||
| 3a05c80b25 | |||
| 5208f1370d | |||
| a59e8518cc | |||
| 2a2890b675 | |||
| 38fd3a6aac | |||
| c3df6565b7 | |||
| 140d2baa24 | |||
| 711341f362 | |||
| a5da0a61dd | |||
| b54c73edb1 | |||
| 2418d4e218 | |||
| 2b4890f052 | |||
| 85d493f24a | |||
| f486d4c4ab | |||
| 6cfd4b8b4d | |||
| 0088030d66 | |||
| 4d828b41f1 | |||
| 95f435923f | |||
| 33f2a15cf3 | |||
| c4e87a3125 | |||
| 773027f6b0 | |||
| 461cce7a48 | |||
| 23b88ada83 | |||
| c2916662e2 | |||
| 24087f5622 | |||
| 95fd88e93e | |||
| 21797d8507 | |||
| 6dcb11ae0c | |||
| 38d6dbe95a | |||
| b100a8acf7 | |||
| 269e9e88a1 | |||
| 8cab3eecc1 | |||
| 6a9e4773ea | |||
| b836f9a2f4 | |||
| 032ddf2cc0 | |||
| 1067796df4 | |||
| 6f81e84541 | |||
| 0d3696bf93 | |||
| b005fd5c11 | |||
| 6c15b7021f | |||
| 842b204d36 | |||
| 5ce96a662d | |||
| fbeaa916b9 | |||
| 423e3947ab | |||
| 3fd816daca | |||
| 7597f5ddc0 | |||
| 5971fde145 | |||
| f008dfb6f0 | |||
| f9d4a3b971 | |||
| 0fd6466d81 | |||
| c23d1f08c0 | |||
| 1dc78c948a | |||
| c952b9f897 | |||
| 97637a8315 | |||
| 1b066d343b | |||
| d36f3b85b4 | |||
| a4aeaa5f91 | |||
| 0b5ad510fe | |||
| e9fc370094 | |||
| 852c5c72be | |||
| 9d017f9f8d | |||
| f995b028e9 | |||
| 1a85b40444 |
7
.gemini_commit_message.txt
Normal file
7
.gemini_commit_message.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
feat(modbus): Implement persistent and improved reconfiguration for Modbus server
|
||||||
|
|
||||||
|
This commit enhances the Modbus server's configuration handling by:
|
||||||
|
|
||||||
|
- Loading saved baudrate and unit ID settings during initialization, ensuring persistence across reboots.
|
||||||
|
- Providing improved feedback during `modbus_reconfigure`, including logging for successful changes and informing the user when a device restart is required for changes to take effect.
|
||||||
|
- Saving new configuration settings even if immediate reinitialization fails, allowing them to be applied on the next boot.
|
||||||
64
.gitignore
vendored
64
.gitignore
vendored
@@ -1 +1,65 @@
|
|||||||
**/build
|
**/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
3
.gitmodules
vendored
Normal 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
|
||||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"fwu.h": "c"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
.vscode/tasks.json
vendored
Normal file
14
.vscode/tasks.json
vendored
Normal 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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
22
README.de.md
Normal file
22
README.de.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<img src="./docs/img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
🇩🇪 Deutsch | [🇬🇧 English](README.md) | [🇫🇷 Français](README.fr.md) | [🇪🇸 Español](README.es.md)
|
||||||
|
|
||||||
|
# Modulares Bewässerungssystem
|
||||||
|
|
||||||
|
Dieses Projekt realisiert ein smartes, modulares Bewässerungssystem, das über Home Assistant gesteuert wird.
|
||||||
|
|
||||||
|
## Dokumentation
|
||||||
|
|
||||||
|
Die detaillierte Dokumentation befindet sich im Verzeichnis [`docs/`](./docs/):
|
||||||
|
|
||||||
|
* **[Konzept](./docs/concept.de.md)**: Beschreibt die Systemarchitektur, die verwendeten Komponenten und die grundlegenden Design-Entscheidungen.
|
||||||
|
* **[MODBUS Register](./docs/modbus-registers.de.md)**: Definiert die Register-Map für die Kommunikation mit den Slave-Nodes.
|
||||||
|
* **[Projektplan](./docs/planning.de.md)**: Enthält den Entwicklungs- und Implementierungsplan.
|
||||||
|
* **[Firmware-Handbuch](./docs/firmware-manual.de.md)**: Beschreibt den Funktionsumfang und die Bedienung der Slave-Node-Firmware.
|
||||||
|
* **[Modbus Test-Tool](./software/tools/modbus_tool/README.de.md)**: Anleitung für das Python-basierte Kommandozeilen-Tool zum Testen der Slaves.
|
||||||
|
|
||||||
|
## Schnellstart
|
||||||
|
|
||||||
|
* **Hardware**: Die KiCad-Dateien für die Hardware befinden sich im Verzeichnis [`hardware/`](./hardware/).
|
||||||
|
* **Software**: Die Zephyr-basierte Firmware für die Slave-Nodes befindet sich im Verzeichnis [`software/`](./software/).
|
||||||
20
README.es.md
Normal file
20
README.es.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<img src="./docs/img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](README.de.md) | [🇬🇧 English](README.md) | [🇫🇷 Français](README.fr.md) | 🇪🇸 Español
|
||||||
|
|
||||||
|
# Sistema de riego modular
|
||||||
|
|
||||||
|
Este proyecto implementa un sistema de riego inteligente y modular controlado a través de Home Assistant.
|
||||||
|
|
||||||
|
## Documentación
|
||||||
|
|
||||||
|
La documentación detallada se puede encontrar en el directorio [`docs/`](./docs/):
|
||||||
|
|
||||||
|
* **[Concepto](./docs/concept.es.md)**: Describe la arquitectura del sistema, los componentes utilizados y las decisiones de diseño básicas.
|
||||||
|
* **[Registros MODBUS](./docs/modbus-registers.es.md)**: Define el mapa de registros para la comunicación con los nodos esclavos.
|
||||||
|
* **[Plan del proyecto](./docs/planning.es.md)**: Contiene el plan de desarrollo e implementación.
|
||||||
|
|
||||||
|
## Inicio rápido
|
||||||
|
|
||||||
|
* **Hardware**: Los archivos KiCad para el hardware se encuentran en el directorio [`hardware/`](./hardware/).
|
||||||
|
* **Software**: El firmware basado en Zephyr para los nodos esclavos se encuentra en el directorio [`software/`](./software/).
|
||||||
20
README.fr.md
Normal file
20
README.fr.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<img src="./docs/img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](README.de.md) | [🇬🇧 English](README.md) | 🇫🇷 Français | [🇪🇸 Español](README.es.md)
|
||||||
|
|
||||||
|
# Système d'irrigation modulaire
|
||||||
|
|
||||||
|
Ce projet met en œuvre un système d'irrigation intelligent et modulaire contrôlé via Home Assistant.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
La documentation détaillée se trouve dans le répertoire [`docs/`](./docs/) :
|
||||||
|
|
||||||
|
* **[Concept](./docs/concept.fr.md)** : Décrit l'architecture du système, les composants utilisés et les décisions de conception de base.
|
||||||
|
* **[Registres MODBUS](./docs/modbus-registers.fr.md)** : Définit la carte des registres pour la communication avec les nœuds esclaves.
|
||||||
|
* **[Plan du projet](./docs/planning.fr.md)** : Contient le plan de développement et de mise en œuvre.
|
||||||
|
|
||||||
|
## Démarrage rapide
|
||||||
|
|
||||||
|
* **Matériel** : Les fichiers KiCad pour le matériel se trouvent dans le répertoire [`hardware/`](./hardware/).
|
||||||
|
* **Logiciel** : Le firmware basé sur Zephyr pour les nœuds esclaves se trouve dans le répertoire [`software/`](./software/).
|
||||||
22
README.md
22
README.md
@@ -1,2 +1,20 @@
|
|||||||
# Home assistant irrigation system
|
<img src="./docs/img/logo.png" alt="Logo" width="100"/>
|
||||||
This is a home assistant irrigation system
|
|
||||||
|
[🇩🇪 Deutsch](README.de.md) | 🇬🇧 English | [🇫🇷 Français](README.fr.md) | [🇪🇸 Español](README.es.md)
|
||||||
|
|
||||||
|
# Modular Irrigation System
|
||||||
|
|
||||||
|
This project implements a smart, modular irrigation system controlled via Home Assistant.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The detailed documentation can be found in the [`docs/`](./docs/) directory:
|
||||||
|
|
||||||
|
* **[Concept](./docs/concept.en.md)**: Describes the system architecture, the components used, and the basic design decisions.
|
||||||
|
* **[MODBUS Registers](./docs/modbus-registers.en.md)**: Defines the register map for communication with the slave nodes.
|
||||||
|
* **[Project Plan](./docs/planning.en.md)**: Contains the development and implementation plan.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
* **Hardware**: The KiCad files for the hardware are located in the [`hardware/`](./hardware/) directory.
|
||||||
|
* **Software**: The Zephyr-based firmware for the slave nodes is located in the [`software/`](./software/) directory.
|
||||||
|
|||||||
77
docs/concept.de.md
Normal file
77
docs/concept.de.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
🇩🇪 Deutsch | [🇬🇧 English](concept.en.md) | [🇫🇷 Français](concept.fr.md) | [🇪🇸 Español](concept.es.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).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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). 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
77
docs/concept.en.md
Normal file
77
docs/concept.en.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](concept.de.md) | 🇬🇧 English | [🇫🇷 Français](concept.fr.md) | [🇪🇸 Español](concept.es.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).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 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.
|
||||||
77
docs/concept.es.md
Normal file
77
docs/concept.es.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](concept.de.md) | [🇬🇧 English](concept.en.md) | [🇫🇷 Français](concept.fr.md) | 🇪🇸 Español
|
||||||
|
|
||||||
|
# Concepto: Sistema de riego modular
|
||||||
|
|
||||||
|
Este documento describe el concepto de un sistema de riego modular e inteligente, controlado de forma centralizada a través de Home Assistant.
|
||||||
|
|
||||||
|
## 1. Descripción general de la arquitectura
|
||||||
|
|
||||||
|
El sistema se divide en tres capas lógicas para garantizar una alta flexibilidad y mantenibilidad:
|
||||||
|
|
||||||
|
1. **Capa de control (Home Assistant):** Toda la lógica, las automatizaciones y la interfaz de usuario residen en Home Assistant. Este es el "cerebro" del sistema.
|
||||||
|
2. **Capa de puerta de enlace (ESP32):** Un puro traductor de protocolos que actúa como puente entre la red doméstica (WLAN/Thread) y el sistema de bus físico de la planta. No contiene lógica de control propia.
|
||||||
|
3. **Capa de actuadores/sensores (nodos esclavos):** Módulos robustos y especializados que se controlan a través de un bus y realizan las tareas reales (conmutación de válvulas, lectura de sensores).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 2. Componentes del sistema
|
||||||
|
|
||||||
|
* **Depósito de agua:** Un contenedor IBC sirve como depósito de agua.
|
||||||
|
* **Suministro de agua:** Un "ladrón de lluvia" en el bajante dirige el agua de lluvia al depósito.
|
||||||
|
* **Bomba:** Una bomba con un tanque de expansión de presión integrado proporciona la presión de agua necesaria.
|
||||||
|
* **Actuadores:** Válvulas de bola motorizadas de 12V para controlar las salidas de agua.
|
||||||
|
* **Sensor de nivel (preciso):** Un `QDY30A` con salida de 4-20mA e interfaz RS485/MODBUS para la medición continua del nivel del agua.
|
||||||
|
* **Sensores de nivel (Min/Max):** Sensores capacitivos opcionales (`XKC-Y25-NPN` o similar) como protección redundante contra el funcionamiento en seco y el desbordamiento.
|
||||||
|
|
||||||
|
## 3. Puerta de enlace
|
||||||
|
|
||||||
|
La interfaz de comunicación central se implementa como una puerta de enlace "tonta".
|
||||||
|
|
||||||
|
* **Hardware:** Una placa basada en `ESP32C6`.
|
||||||
|
* **Función:** La puerta de enlace actúa como un **convertidor transparente de MODBUS TCP/IP a MODBUS RTU**. Recibe comandos de la red doméstica y los reenvía al bus RS485 y viceversa. No ejecuta su propia lógica de control.
|
||||||
|
* **Conexión a Home Assistant:** La conexión se realiza a través de la red doméstica, ya sea a través de WLAN o en el futuro posiblemente a través de Thread/Matter. En Home Assistant, la integración oficial de MODBUS se utiliza para direccionar directamente la puerta de enlace y los esclavos detrás de ella.
|
||||||
|
|
||||||
|
## 4. Nodos esclavos
|
||||||
|
|
||||||
|
Los nodos esclavos son las unidades de trabajo en el campo. Para mantener bajo el esfuerzo en la producción de series pequeñas (por ejemplo, en JLCPCB), se busca un diseño de placa universal para todos los tipos de esclavos.
|
||||||
|
|
||||||
|
* **Microcontrolador:** Un `STM32G431PB`. Aunque potente, ofrece todos los periféricos necesarios (múltiples UART, ADC, CAN) y permite un diseño de hardware y software uniforme.
|
||||||
|
* **Periféricos por nodo:**
|
||||||
|
* **Dos salidas de lado alto (+12V):** Realizadas a través de un `VND7050AJ`. Perfecto para controlar las válvulas de motor de 12V (`Abrir`/`Cerrar`). La línea `Sense` del controlador se lee a través de un convertidor AD para realizar una detección de posición final sin interruptores de límite físicos midiendo la corriente del motor (corriente del motor en reposo ≈ 0).
|
||||||
|
* **Dos salidas de lado bajo (0V):** Salidas conmutadas a través de MOSFET de canal N. Se pueden utilizar para controlar LED de 12V en botones o para conmutar el relé de estado sólido para la bomba.
|
||||||
|
* **Dos entradas digitales:** Entradas directas y protegidas en el controlador para conectar botones o los sensores NPN capacitivos.
|
||||||
|
|
||||||
|
## 5. Sistema de bus: MODBUS-RTU a través de RS485
|
||||||
|
|
||||||
|
MODBUS-RTU se utiliza de forma consistente como sistema de bus.
|
||||||
|
|
||||||
|
* **Justificación:** Esta elección es pragmática, ya que el sensor de nivel ya requiere MODBUS. Esto significa que solo se requiere un único protocolo simple y extendido para todo el sistema.
|
||||||
|
* **Capa física:** El cableado se realiza a través de RS485. Se utiliza un cable Ethernet Cat-7 disponible en el mercado con conectores RJ45:
|
||||||
|
* 1 par trenzado para las señales de bus `A` y `B`.
|
||||||
|
* 3 pares de cables en paralelo para `+12V` y `GND` para suministrar energía a los esclavos.
|
||||||
|
|
||||||
|
## 6. Software
|
||||||
|
|
||||||
|
* **Sistema operativo (esclavos):** `Zephyr OS`. Es un sistema operativo en tiempo real moderno y potente que permite una estructura de firmware limpia y mantenible.
|
||||||
|
* **Implementación de la lógica:** Toda la lógica de control (por ejemplo, "Si el nivel < 20% y el día de la semana = lunes, entonces enciende la válvula 3 durante 10 minutos") se asigna exclusivamente en **Home Assistant** a través de su motor de automatización.
|
||||||
|
|
||||||
|
### 6.1. Actualización del firmware de los esclavos (OTA)
|
||||||
|
|
||||||
|
El firmware de los esclavos se puede actualizar durante el funcionamiento a través del bus sin necesidad de acceso físico directo.
|
||||||
|
|
||||||
|
* **Concepto:** Se utiliza el cargador de arranque `MCUBoot`. Este está desacoplado del protocolo de comunicación.
|
||||||
|
* **Procedimiento:**
|
||||||
|
1. Un script en Home Assistant lee el nuevo archivo de firmware (`firmware.bin`).
|
||||||
|
2. El script divide el archivo en pequeños paquetes de datos y los envía uno tras otro al esclavo de destino mediante un comando MODBUS.
|
||||||
|
3. La aplicación en ejecución en el esclavo recibe estos paquetes y los escribe directamente en la memoria flash secundaria ("ranura de actualización").
|
||||||
|
4. Después de una transmisión exitosa, el esclavo se reinicia por comando.
|
||||||
|
5. `MCUBoot` comprueba la firma de la nueva imagen, la copia en la ranura principal y la inicia.
|
||||||
|
* **Seguridad:** El nuevo firmware debe marcarse a sí mismo como "funcional" después de iniciarse. Si no lo hace (por ejemplo, debido a un bloqueo), la versión de firmware anterior y estable es restaurada automáticamente por `MCUBoot` en el siguiente reinicio por el watchdog.
|
||||||
|
|
||||||
|
## 7. Conceptos de seguridad y robustez
|
||||||
|
|
||||||
|
* **Comportamiento a prueba de fallos:** Cada nodo esclavo implementa un watchdog. Si no se recibe ninguna consulta MODBUS válida de la puerta de enlace durante un período de tiempo definido (por ejemplo, 15 segundos), el esclavo pasa a un estado seguro: todas las válvulas se cierran y los relés (por ejemplo, para la bomba) se apagan.
|
||||||
|
* **Circuitos de protección eléctrica:** Todas las interfaces externas están protegidas. Las líneas de bus RS485 (`A`/`B`) están protegidas contra sobretensiones con diodos TVS en cada placa. Las entradas y salidas reciben protección ESD básica.
|
||||||
|
* **Fuente de alimentación:** La tensión del bus de 12V se regula en cada nodo esclavo con un convertidor reductor `TPS5430DDAR` eficiente a los 3.3V necesarios para el microcontrolador y los controladores del bus.
|
||||||
77
docs/concept.fr.md
Normal file
77
docs/concept.fr.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](concept.de.md) | [🇬🇧 English](concept.en.md) | 🇫🇷 Français | [🇪🇸 Español](concept.es.md)
|
||||||
|
|
||||||
|
# Concept : Système d'irrigation modulaire
|
||||||
|
|
||||||
|
Ce document décrit le concept d'un système d'irrigation modulaire et intelligent, contrôlé de manière centralisée via Home Assistant.
|
||||||
|
|
||||||
|
## 1. Aperçu de l'architecture
|
||||||
|
|
||||||
|
Le système est divisé en trois couches logiques pour garantir une flexibilité et une maintenabilité élevées :
|
||||||
|
|
||||||
|
1. **Couche de contrôle (Home Assistant) :** Toute la logique, les automatisations et l'interface utilisateur résident dans Home Assistant. C'est le "cerveau" du système.
|
||||||
|
2. **Couche de passerelle (ESP32) :** Un simple traducteur de protocole qui sert de pont entre le réseau domestique (WLAN/Thread) et le système de bus physique de l'installation. Il ne contient aucune logique de contrôle propre.
|
||||||
|
3. **Couche d'acteurs/capteurs (nœuds esclaves) :** Des modules robustes et spécialisés, commandés via un bus, qui exécutent les tâches réelles (commutation de vannes, lecture de capteurs).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 2. Composants du système
|
||||||
|
|
||||||
|
* **Réservoir d'eau :** Un conteneur IBC sert de réservoir d'eau.
|
||||||
|
* **Alimentation en eau :** Un "récupérateur d'eau de pluie" sur le tuyau de descente dirige l'eau de pluie dans le réservoir.
|
||||||
|
* **Pompe :** Une pompe avec un vase d'expansion intégré assure la pression d'eau nécessaire.
|
||||||
|
* **Actionneurs :** Des vannes à boisseau sphérique motorisées de 12V pour contrôler les sorties d'eau.
|
||||||
|
* **Capteur de niveau (précis) :** Un `QDY30A` avec une sortie 4-20mA et une interface RS485/MODBUS pour la mesure continue du niveau d'eau.
|
||||||
|
* **Capteurs de niveau (Min/Max) :** Des capteurs capacitifs optionnels (`XKC-Y25-NPN` ou similaire) comme protection redondante contre le fonctionnement à sec et le débordement.
|
||||||
|
|
||||||
|
## 3. Passerelle
|
||||||
|
|
||||||
|
L'interface de communication centrale est réalisée sous la forme d'une passerelle "stupide".
|
||||||
|
|
||||||
|
* **Matériel :** Une carte basée sur `ESP32C6`.
|
||||||
|
* **Fonction :** La passerelle agit comme un **convertisseur transparent MODBUS TCP/IP vers MODBUS RTU**. Elle reçoit les commandes du réseau domestique et les transmet au bus RS485 et vice versa. Elle n'exécute aucune logique de contrôle propre.
|
||||||
|
* **Connexion à Home Assistant :** La connexion se fait via le réseau domestique, soit par WLAN, soit à l'avenir éventuellement par Thread/Matter. Dans Home Assistant, l'intégration MODBUS officielle est utilisée pour adresser directement la passerelle et les esclaves qui se trouvent derrière.
|
||||||
|
|
||||||
|
## 4. Nœuds esclaves
|
||||||
|
|
||||||
|
Les nœuds esclaves sont les unités de travail sur le terrain. Pour réduire les efforts lors de la production en petites séries (par ex. chez JLCPCB), un design de platine universel pour tous les types d'esclaves est visé.
|
||||||
|
|
||||||
|
* **Microcontrôleur :** Un `STM32G431PB`. Bien que puissant, il offre toutes les périphériques nécessaires (plusieurs UART, ADC, CAN) et permet une conception matérielle et logicielle uniforme.
|
||||||
|
* **Périphériques par nœud :**
|
||||||
|
* **Deux sorties High-Side (+12V) :** Réalisées via un `VND7050AJ`. Parfait pour commander les vannes motorisées 12V (`Ouvrir`/`Fermer`). La ligne `Sense` du pilote est lue via un convertisseur AN pour réaliser une détection de fin de course sans interrupteurs de fin de course physiques en mesurant le courant du moteur (courant du moteur à l'arrêt ≈ 0).
|
||||||
|
* **Deux sorties Low-Side (0V) :** Sorties commutées via des MOSFET à canal N. Utilisables pour commander des LED 12V dans des boutons-poussoirs ou pour commuter le relais à semi-conducteurs pour la pompe.
|
||||||
|
* **Deux entrées numériques :** Entrées directes et protégées sur le contrôleur pour connecter des boutons-poussoirs ou les capteurs NPN capacitifs.
|
||||||
|
|
||||||
|
## 5. Système de bus : MODBUS-RTU via RS485
|
||||||
|
|
||||||
|
Le système de bus repose systématiquement sur MODBUS-RTU.
|
||||||
|
|
||||||
|
* **Justification :** Ce choix est pragmatique, car le capteur de niveau requiert déjà MODBUS. Ainsi, un seul protocole simple et largement répandu est nécessaire pour l'ensemble du système.
|
||||||
|
* **Couche physique :** Le câblage est réalisé via RS485. Un câble Ethernet Cat-7 du commerce avec des connecteurs RJ45 est utilisé :
|
||||||
|
* 1 paire torsadée pour les signaux de bus `A` et `B`.
|
||||||
|
* 3 paires de fils en parallèle pour `+12V` et `GND` pour l'alimentation des esclaves.
|
||||||
|
|
||||||
|
## 6. Logiciel
|
||||||
|
|
||||||
|
* **Système d'exploitation (esclaves) :** `Zephyr OS`. C'est un système d'exploitation temps réel moderne et performant qui permet une structure de firmware propre et maintenable.
|
||||||
|
* **Implémentation de la logique :** Toute la logique de commande (par ex. "Si niveau < 20% et jour de la semaine = Lundi, alors activer la vanne 3 pendant 10 minutes") est exclusivement représentée dans **Home Assistant** via son moteur d'automatisation.
|
||||||
|
|
||||||
|
### 6.1. Mise à jour du firmware des esclaves (OTA)
|
||||||
|
|
||||||
|
Le firmware des esclaves peut être mis à jour en cours de fonctionnement via le bus, sans nécessiter d'accès physique direct.
|
||||||
|
|
||||||
|
* **Concept :** Le bootloader `MCUBoot` est utilisé. Celui-ci est découplé du protocole de communication.
|
||||||
|
* **Déroulement :**
|
||||||
|
1. Un script dans Home Assistant lit le nouveau fichier de firmware (`firmware.bin`).
|
||||||
|
2. Le script décompose le fichier en petits paquets de données et les envoie successivement à l'esclave cible par commande MODBUS.
|
||||||
|
3. L'application en cours d'exécution sur l'esclave reçoit ces paquets et les écrit directement dans la mémoire flash secondaire ("slot de mise à jour").
|
||||||
|
4. Après une transmission réussie, l'esclave est redémarré par commande.
|
||||||
|
5. `MCUBoot` vérifie la signature de la nouvelle image, la copie dans le slot primaire et la démarre.
|
||||||
|
* **Sécurité :** Le nouveau firmware doit se marquer comme "fonctionnel" après le démarrage. S'il ne le fait pas (par ex. à cause d'un crash), la version précédente et stable du firmware est automatiquement restaurée par `MCUBoot` au prochain redémarrage par le watchdog.
|
||||||
|
|
||||||
|
## 7. Concepts de sécurité et de robustesse
|
||||||
|
|
||||||
|
* **Comportement Fail-Safe :** Chaque nœud esclave implémente un watchdog. Si aucune requête MODBUS valide n'est reçue de la passerelle pendant une période définie (par ex. 15 secondes), le nœud esclave passe dans un état sûr : toutes les vannes sont fermées et les relais (par ex. pour la pompe) sont désactivés.
|
||||||
|
* **Circuits de protection électrique :** Toutes les interfaces externes sont protégées. Les lignes de bus RS485 (`A`/`B`) sont protégées contre les surtensions par des diodes TVS sur chaque platine. Les entrées et les sorties reçoivent une protection ESD de base.
|
||||||
|
* **Alimentation électrique :** La tension de bus de 12V est régulée sur chaque nœud esclave avec un convertisseur abaisseur `TPS5430DDAR` efficace pour fournir les 3.3V nécessaires au microcontrôleur et aux pilotes de bus.
|
||||||
86
docs/img/architecture.de.svg
Normal file
86
docs/img/architecture.de.svg
Normal 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 & 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 |
77
docs/img/architecture.en.svg
Normal file
77
docs/img/architecture.en.svg
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<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">System Architecture</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">(Logic & 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">(Protocol Translator)</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">(Valve, Button)</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">(Pump, Sensors)</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">Level Sensor</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 |
77
docs/img/architecture.es.svg
Normal file
77
docs/img/architecture.es.svg
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<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">Arquitectura del sistema</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">(Lógica e Interfaz de Usuario)</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">Pasarela (ESP32C6)</text>
|
||||||
|
<text x="325" y="230" class="label-sub">(Traductor de protocolo)</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">Bus RS485 (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">Nodo esclavo</text>
|
||||||
|
<text x="125" y="385" class="label-sub">(Válvula, Botón)</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">Nodo esclavo</text>
|
||||||
|
<text x="325" y="385" class="label-sub">(Bomba, Sensores)</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">Sensor de nivel</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 |
78
docs/img/architecture.fr.svg
Normal file
78
docs/img/architecture.fr.svg
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
<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">Architecture du système</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">(Logique & 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">Passerelle (ESP32C6)</text>
|
||||||
|
<text x="325" y="230" class="label-sub">(Traducteur de protocole)</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">Bus RS485 (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">Nœud esclave</text>
|
||||||
|
<text x="125" y="385" class="label-sub">(Vanne, Bouton)</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">Nœud esclave</text>
|
||||||
|
<text x="325" y="385" class="label-sub">(Pompe, Capteurs)</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">Capteur de niveau</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 |
BIN
docs/img/logo.png
Normal file
BIN
docs/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
16
docs/img/logo.svg
Normal file
16
docs/img/logo.svg
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<svg width="100" height="120" viewBox="0 0 100 120" xmlns="http://www.w3.org/2000/svg" aria-labelledby="logoTitle">
|
||||||
|
<title id="logoTitle">Logo: Wassertropfen mit integriertem Chip</title>
|
||||||
|
|
||||||
|
<path
|
||||||
|
d="M50 115 C 85 85, 95 65, 95 45 A 45 45 0 1 0 5 45 C 5 65, 15 85, 50 115 Z"
|
||||||
|
fill="#2563EB"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<g fill="#FFFFFF">
|
||||||
|
<rect x="25" y="35" width="50" height="8" rx="2"/>
|
||||||
|
<rect x="25" y="50" width="35" height="8" rx="2"/>
|
||||||
|
<rect x="70" y="50" width="5" height="8" rx="2"/>
|
||||||
|
<rect x="25" y="65" width="50" height="8" rx="2"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 568 B |
94
docs/modbus-registers.de.md
Normal file
94
docs/modbus-registers.de.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
🇩🇪 Deutsch | [🇬🇧 English](modbus-registers.en.md) | [🇫🇷 Français](modbus-registers.fr.md) | [🇪🇸 Español](modbus-registers.es.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** | `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** | `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
|
||||||
|
|
||||||
|
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_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** | `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. |
|
||||||
87
docs/modbus-registers.en.md
Normal file
87
docs/modbus-registers.en.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](modbus-registers.de.md) | 🇬🇧 English | [🇫🇷 Français](modbus-registers.fr.md) | [🇪🇸 Español](modbus-registers.es.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. |
|
||||||
87
docs/modbus-registers.es.md
Normal file
87
docs/modbus-registers.es.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](modbus-registers.de.md) | [🇬🇧 English](modbus-registers.en.md) | [🇫🇷 Français](modbus-registers.fr.md) | 🇪🇸 Español
|
||||||
|
|
||||||
|
# Definición del mapa de registros MODBUS v1.0
|
||||||
|
|
||||||
|
## 1. Introducción
|
||||||
|
|
||||||
|
Este documento define los registros MODBUS para los nodos esclavos universales del sistema de riego.
|
||||||
|
|
||||||
|
### 1.1. Filosofía de direccionamiento
|
||||||
|
|
||||||
|
Todos los registros se definen en una única lista continua por tipo de registro (`Input` o `Holding`). Una columna "Categoría" asigna lógicamente la función. Las direcciones se agrupan en bloques para dejar espacio para futuras ampliaciones y para aumentar la legibilidad.
|
||||||
|
|
||||||
|
* **`0x0000 - 0x000F`**: Control y estado de la válvula
|
||||||
|
* **`0x0010 - 0x001F`**: Salidas digitales (LEDs / relés)
|
||||||
|
* **`0x0020 - 0x002F`**: Entradas digitales (botones / sensores)
|
||||||
|
* **`0x00F0 - 0x00FF`**: Configuración y estado general del dispositivo
|
||||||
|
* **`0x0100 - 0x01FF`**: Mecanismo de actualización de firmware
|
||||||
|
|
||||||
|
### 1.2. Códigos de función utilizados
|
||||||
|
|
||||||
|
* **`0x03` (Read Holding Registers):** Para leer registros `4xxxx`.
|
||||||
|
* **`0x04` (Read Input Registers):** Para leer registros `3xxxx`.
|
||||||
|
* **`0x06` (Write Single Register):** Para escribir un único registro `4xxxx`.
|
||||||
|
* **`0x10` (Write Multiple Registers):** Para escribir varios registros `4xxxx` a la vez.
|
||||||
|
|
||||||
|
## 2. Registros de entrada (3xxxx, solo lectura)
|
||||||
|
|
||||||
|
| Dirección (hex) | Nombre | Categoría | Descripción |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **0x0000** | `VALVE_STATE_MOVEMENT` | Válvula | Registro de estado combinado. **Byte alto**: Movimiento (`0`=Inactivo, `1`=Abriendo, `2`=Cerrando, `3`=Error). **Byte bajo**: Estado (`0`=Cerrado, `1`=Abierto). |
|
||||||
|
| **0x0001** | `MOTOR_CURRENT_MA` | Válvula | Corriente actual del motor en miliamperios (mA). |
|
||||||
|
| **0x0020** | `DIGITAL_INPUTS_STATE` | Entradas | Máscara de bits de las entradas digitales. Bit 0: Entrada 1, Bit 1: Entrada 2. `1`=Activo. |
|
||||||
|
| **0x0021** | `BUTTON_EVENTS` | Entradas | Banderas de eventos para botones (Borrar al leer). Bit 0: Botón 1 presionado. Bit 1: Botón 2 presionado. |
|
||||||
|
| **0x00F0** | `FIRMWARE_VERSION_MAJOR_MINOR` | Sistema | p. ej. `0x0102` para v1.2. |
|
||||||
|
| **0x00F1** | `FIRMWARE_VERSION_PATCH` | Sistema | p. ej. `3` para v1.2.3. |
|
||||||
|
| **0x00F2** | `DEVICE_STATUS` | Sistema | `0`=OK, `1`=Error general. |
|
||||||
|
| **0x00F3** | `UPTIME_SECONDS_LOW` | Sistema | 16 bits inferiores del tiempo de actividad en segundos. |
|
||||||
|
| **0x00F4** | `UPTIME_SECONDS_HIGH` | Sistema | 16 bits superiores del tiempo de actividad. |
|
||||||
|
| **0x0100** | `FWU_LAST_CHUNK_CRC` | Actualización FW | Contiene el CRC16 del último trozo de datos recibido en el búfer. |
|
||||||
|
|
||||||
|
## 3. Registros de retención (4xxxx, lectura/escritura)
|
||||||
|
|
||||||
|
| Dirección (hex) | Nombre | Categoría | Descripción |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **0x0000** | `VALVE_COMMAND` | Válvula | `1`=Abrir, `2`=Cerrar, `0`=Detener movimiento. |
|
||||||
|
| **0x0001** | `MAX_OPENING_TIME_S` | Válvula | Tiempo de espera de seguridad en segundos para el proceso de apertura. |
|
||||||
|
| **0x0002** | `MAX_CLOSING_TIME_S` | Válvula | Tiempo de espera de seguridad en segundos para el proceso de cierre. |
|
||||||
|
| **0x0010** | `DIGITAL_OUTPUTS_STATE` | Salidas | Máscara de bits para leer y escribir las salidas. Bit 0: Salida 1, Bit 1: Salida 2. `1`=ON, `0`=OFF. |
|
||||||
|
| **0x00F0** | `WATCHDOG_TIMEOUT_S` | Sistema | Tiempo de espera del watchdog de seguridad en segundos. `0`=Desactivado. |
|
||||||
|
| **0x0100** | `FWU_COMMAND` | Actualización FW | `1`: **Verificar trozo**: El último trozo transmitido fue considerado válido por el cliente. El esclavo ahora debe escribirlo en la flash. `2`: **Finalizar actualización**: Todos los trozos han sido transmitidos. Finalizar la instalación y reiniciar. |
|
||||||
|
| **0x0101** | `FWU_CHUNK_OFFSET_LOW` | Actualización FW | 16 bits inferiores del desplazamiento de 32 bits en el que se escribirá el siguiente trozo. |
|
||||||
|
| **0x0102** | `FWU_CHUNK_OFFSET_HIGH` | Actualización FW | 16 bits superiores del desplazamiento de 32 bits. |
|
||||||
|
| **0x0103** | `FWU_CHUNK_SIZE` | Actualización FW | Tamaño del siguiente trozo en bytes (máx. 256). |
|
||||||
|
| **0x0180** | `FWU_DATA_BUFFER` | Actualización FW | **Dirección de inicio** de un búfer de 128x16 bits (256 bytes). Corresponde a los registros `40384` a `40511`. |
|
||||||
|
|
||||||
|
## 4. Proceso detallado de actualización de firmware
|
||||||
|
|
||||||
|
Este proceso no tiene estado y es robusto frente a errores de transmisión.
|
||||||
|
|
||||||
|
1. **Cliente:** Selecciona un trozo (máx. 256 bytes) del archivo de firmware y calcula su CRC16.
|
||||||
|
2. **Cliente:** Escribe el desplazamiento de destino (p. ej. `0`) en `FWU_CHUNK_OFFSET_...` y el tamaño en `FWU_CHUNK_SIZE`.
|
||||||
|
3. **Cliente:** Escribe los datos del trozo en el `FWU_DATA_BUFFER` (desde la dirección `0x0180`).
|
||||||
|
4. **Esclavo:** Recibe los datos, los coloca en el búfer de RAM y calcula el CRC. El resultado se proporciona en `FWU_LAST_CHUNK_CRC` (`30256`).
|
||||||
|
5. **Cliente:** Lee `FWU_LAST_CHUNK_CRC` y compara el valor con el CRC autocalculado.
|
||||||
|
* **Error:** Volver al paso 3 para enviar el mismo trozo de nuevo.
|
||||||
|
* **Éxito:** Continúa con el siguiente paso.
|
||||||
|
6. **Cliente:** Escribe el comando `1` ("Verificar trozo") en `FWU_COMMAND` (`40256`).
|
||||||
|
7. **Esclavo:** Recibe el comando, toma el trozo verificado del búfer de RAM y lo escribe en la ubicación correcta en la memoria flash.
|
||||||
|
8. **Cliente:** Continúa con el siguiente trozo (vuelta al paso 1 con nuevo desplazamiento y datos).
|
||||||
|
9. **Último trozo:** Después de que el último trozo ha sido transferido y escrito en la flash con el comando `1`, el cliente escribe el comando `2` ("Finalizar actualización") en `FWU_COMMAND`.
|
||||||
|
10. **Esclavo:** Realiza las comprobaciones finales y se reinicia para que MCUBoot pueda realizar la instalación.
|
||||||
|
|
||||||
|
## Apéndice: Registros del sensor de nivel QDY30A
|
||||||
|
|
||||||
|
Estos registros pertenecen al sensor de nivel externo y también se pueden direccionar en el bus. Según el fabricante, se trata de registros de retención (`4xxxx`) que se leen con el código de función `0x03`.
|
||||||
|
|
||||||
|
| Dirección (hex) | Nombre | L/E | Descripción |
|
||||||
|
| :--- | :--- | :-- | :--- |
|
||||||
|
| **0x0000** | `NODE_ADDRESS` | L/E | Dirección del dispositivo del sensor (1-255). |
|
||||||
|
| **0x0001** | `BAUDRATE` | L/E | `0`=1200, `1`=2400, `2`=4800, `3`=9600, `4`=19200, `5`=38400, `6`=57600, `7`=115200. |
|
||||||
|
| **0x0002** | `UNIT` | L/E | `0`=Ninguno, `1`=cm, `2`=mm, `3`=MPa, `4`=Pa, `5`=kPa. |
|
||||||
|
| **0x0003** | `DECIMAL_PLACES` | L/E | Número de decimales para el valor medido (0-3). |
|
||||||
|
| **0x0004** | `CURRENT_MEASUREMENT` | L | El valor medido escalado como un entero de 16 bits con signo. |
|
||||||
|
| **0x0005** | `MEASURING_RANGE_ZERO_POINT` | L/E | Valor bruto para el punto cero de la escala. |
|
||||||
|
| **0x0006** | `MEASURING_RANGE_END_POINT` | L/E | Valor bruto para el punto final de la escala. |
|
||||||
87
docs/modbus-registers.fr.md
Normal file
87
docs/modbus-registers.fr.md
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](modbus-registers.de.md) | [🇬🇧 English](modbus-registers.en.md) | 🇫🇷 Français | [🇪🇸 Español](modbus-registers.es.md)
|
||||||
|
|
||||||
|
# Définition de la carte des registres MODBUS v1.0
|
||||||
|
|
||||||
|
## 1. Introduction
|
||||||
|
|
||||||
|
Ce document définit les registres MODBUS pour les nœuds esclaves universels du système d'irrigation.
|
||||||
|
|
||||||
|
### 1.1. Philosophie d'adressage
|
||||||
|
|
||||||
|
Tous les registres sont définis dans une seule liste continue par type de registre (`Input` ou `Holding`). Une colonne "Catégorie" attribue logiquement la fonction. Les adresses sont regroupées en blocs pour laisser de la place à de futures extensions et pour améliorer la lisibilité.
|
||||||
|
|
||||||
|
* **`0x0000 - 0x000F`** : Commande et état de la vanne
|
||||||
|
* **`0x0010 - 0x001F`** : Sorties numériques (LEDs / relais)
|
||||||
|
* **`0x0020 - 0x002F`** : Entrées numériques (boutons / capteurs)
|
||||||
|
* **`0x00F0 - 0x00FF`** : Configuration et état général de l'appareil
|
||||||
|
* **`0x0100 - 0x01FF`** : Mécanisme de mise à jour du firmware
|
||||||
|
|
||||||
|
### 1.2. Codes de fonction utilisés
|
||||||
|
|
||||||
|
* **`0x03` (Read Holding Registers) :** Pour lire les registres `4xxxx`.
|
||||||
|
* **`0x04` (Read Input Registers) :** Pour lire les registres `3xxxx`.
|
||||||
|
* **`0x06` (Write Single Register) :** Pour écrire un seul registre `4xxxx`.
|
||||||
|
* **`0x10` (Write Multiple Registers) :** Pour écrire plusieurs registres `4xxxx` à la fois.
|
||||||
|
|
||||||
|
## 2. Registres d'entrée (3xxxx, Lecture seule)
|
||||||
|
|
||||||
|
| Adresse (hex) | Nom | Catégorie | Description |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **0x0000** | `VALVE_STATE_MOVEMENT` | Vanne | Registre d'état combiné. **Octet haut** : Mouvement (`0`=Inactif, `1`=Ouverture, `2`=Fermeture, `3`=Erreur). **Octet bas** : État (`0`=Fermé, `1`=Ouvert). |
|
||||||
|
| **0x0001** | `MOTOR_CURRENT_MA` | Vanne | Courant moteur actuel en milliampères (mA). |
|
||||||
|
| **0x0020** | `DIGITAL_INPUTS_STATE` | Entrées | Masque de bits des entrées numériques. Bit 0 : Entrée 1, Bit 1 : Entrée 2. `1`=Actif. |
|
||||||
|
| **0x0021** | `BUTTON_EVENTS` | Entrées | Indicateurs d'événements pour les boutons (Effacement à la lecture). Bit 0 : Bouton 1 pressé. Bit 1 : Bouton 2 pressé. |
|
||||||
|
| **0x00F0** | `FIRMWARE_VERSION_MAJOR_MINOR` | Système | ex. `0x0102` pour v1.2. |
|
||||||
|
| **0x00F1** | `FIRMWARE_VERSION_PATCH` | Système | ex. `3` pour v1.2.3. |
|
||||||
|
| **0x00F2** | `DEVICE_STATUS` | Système | `0`=OK, `1`=Erreur générale. |
|
||||||
|
| **0x00F3** | `UPTIME_SECONDS_LOW` | Système | 16 bits inférieurs du temps de fonctionnement en secondes. |
|
||||||
|
| **0x00F4** | `UPTIME_SECONDS_HIGH` | Système | 16 bits supérieurs du temps de fonctionnement. |
|
||||||
|
| **0x0100** | `FWU_LAST_CHUNK_CRC` | Mise à jour FW | Contient le CRC16 du dernier bloc de données reçu dans le tampon. |
|
||||||
|
|
||||||
|
## 3. Registres de maintien (4xxxx, Lecture/Écriture)
|
||||||
|
|
||||||
|
| Adresse (hex) | Nom | Catégorie | Description |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **0x0000** | `VALVE_COMMAND` | Vanne | `1`=Ouvrir, `2`=Fermer, `0`=Arrêter le mouvement. |
|
||||||
|
| **0x0001** | `MAX_OPENING_TIME_S` | Vanne | Temporisation de sécurité en secondes pour le processus d'ouverture. |
|
||||||
|
| **0x0002** | `MAX_CLOSING_TIME_S` | Vanne | Temporisation de sécurité en secondes pour le processus de fermeture. |
|
||||||
|
| **0x0010** | `DIGITAL_OUTPUTS_STATE` | Sorties | Masque de bits pour lire et écrire les sorties. Bit 0 : Sortie 1, Bit 1 : Sortie 2. `1`=ON, `0`=OFF. |
|
||||||
|
| **0x00F0** | `WATCHDOG_TIMEOUT_S` | Système | Temporisation du watchdog de sécurité en secondes. `0`=Désactivé. |
|
||||||
|
| **0x0100** | `FWU_COMMAND` | Mise à jour FW | `1` : **Vérifier le bloc** : Le dernier bloc transmis a été jugé valide par le client. L'esclave doit maintenant l'écrire en flash. `2` : **Finaliser la mise à jour** : Tous les blocs ont été transmis. Finaliser l'installation et redémarrer. |
|
||||||
|
| **0x0101** | `FWU_CHUNK_OFFSET_LOW` | Mise à jour FW | 16 bits inférieurs de l'offset 32 bits où le prochain bloc doit être écrit. |
|
||||||
|
| **0x0102** | `FWU_CHUNK_OFFSET_HIGH` | Mise à jour FW | 16 bits supérieurs de l'offset 32 bits. |
|
||||||
|
| **0x0103** | `FWU_CHUNK_SIZE` | Mise à jour FW | Taille du prochain bloc en octets (max. 256). |
|
||||||
|
| **0x0180** | `FWU_DATA_BUFFER` | Mise à jour FW | **Adresse de début** d'un tampon de 128x16 bits (256 octets). Correspond aux registres `40384` à `40511`. |
|
||||||
|
|
||||||
|
## 4. Processus détaillé de mise à jour du firmware
|
||||||
|
|
||||||
|
Ce processus est sans état et robuste aux erreurs de transmission.
|
||||||
|
|
||||||
|
1. **Client :** Sélectionne un bloc (max. 256 octets) dans le fichier de firmware et calcule son CRC16.
|
||||||
|
2. **Client :** Écrit l'offset cible (par ex. `0`) dans `FWU_CHUNK_OFFSET_...` et la taille dans `FWU_CHUNK_SIZE`.
|
||||||
|
3. **Client :** Écrit les données du bloc dans le `FWU_DATA_BUFFER` (à partir de l'adresse `0x0180`).
|
||||||
|
4. **Esclave :** Reçoit les données, les place dans le tampon RAM et calcule le CRC. Le résultat est fourni dans `FWU_LAST_CHUNK_CRC` (`30256`).
|
||||||
|
5. **Client :** Lit `FWU_LAST_CHUNK_CRC` et compare la valeur avec le CRC auto-calculé.
|
||||||
|
* **Erreur :** Retourner à l'étape 3 pour renvoyer le même bloc.
|
||||||
|
* **Succès :** Continue à l'étape suivante.
|
||||||
|
6. **Client :** Écrit la commande `1` ("Vérifier le bloc") dans `FWU_COMMAND` (`40256`).
|
||||||
|
7. **Esclave :** Reçoit la commande, prend le bloc vérifié du tampon RAM et l'écrit à l'emplacement correct dans la mémoire flash.
|
||||||
|
8. **Client :** Continue avec le bloc suivant (retour à l'étape 1 avec un nouvel offset et de nouvelles données).
|
||||||
|
9. **Dernier bloc :** Après que le dernier bloc a été transféré et écrit en flash avec la commande `1`, le client écrit la commande `2` ("Finaliser la mise à jour") dans `FWU_COMMAND`.
|
||||||
|
10. **Esclave :** Effectue les vérifications finales et redémarre pour que MCUBoot puisse effectuer l'installation.
|
||||||
|
|
||||||
|
## Annexe : Registres du capteur de niveau QDY30A
|
||||||
|
|
||||||
|
Ces registres appartiennent au capteur de niveau externe et peuvent également être adressés sur le bus. Selon le fabricant, il s'agit de registres de maintien (`4xxxx`) qui sont lus avec le code de fonction `0x03`.
|
||||||
|
|
||||||
|
| Adresse (hex) | Nom | L/E | Description |
|
||||||
|
| :--- | :--- | :-- | :--- |
|
||||||
|
| **0x0000** | `NODE_ADDRESS` | L/E | Adresse de l'appareil du capteur (1-255). |
|
||||||
|
| **0x0001** | `BAUDRATE` | L/E | `0`=1200, `1`=2400, `2`=4800, `3`=9600, `4`=19200, `5`=38400, `6`=57600, `7`=115200. |
|
||||||
|
| **0x0002** | `UNIT` | L/E | `0`=Aucun, `1`=cm, `2`=mm, `3`=MPa, `4`=Pa, `5`=kPa. |
|
||||||
|
| **0x0003** | `DECIMAL_PLACES` | L/E | Nombre de décimales pour la valeur mesurée (0-3). |
|
||||||
|
| **0x0004** | `CURRENT_MEASUREMENT` | L | La valeur mesurée mise à l'échelle sous forme d'entier signé de 16 bits. |
|
||||||
|
| **0x0005** | `MEASURING_RANGE_ZERO_POINT` | L/E | Valeur brute pour le point zéro de l'échelle. |
|
||||||
|
| **0x0006** | `MEASURING_RANGE_END_POINT` | L/E | Valeur brute pour le point final de l'échelle. |
|
||||||
37
docs/planning.de.md
Normal file
37
docs/planning.de.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
🇩🇪 Deutsch | [🇬🇧 English](planning.en.md) | [🇫🇷 Français](planning.fr.md) | [🇪🇸 Español](planning.es.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. |
|
||||||
|
| ✅ | 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 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. |
|
||||||
|
| ☐ | **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. |
|
||||||
35
docs/planning.en.md
Normal file
35
docs/planning.en.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](planning.de.md) | 🇬🇧 English | [🇫🇷 Français](planning.fr.md) | [🇪🇸 Español](planning.es.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 | 2025-06-30 | 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. |
|
||||||
35
docs/planning.es.md
Normal file
35
docs/planning.es.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](planning.de.md) | [🇬🇧 English](planning.en.md) | [🇫🇷 Français](planning.fr.md) | 🇪🇸 Español
|
||||||
|
|
||||||
|
# Plan del proyecto: Sistema de riego modular
|
||||||
|
|
||||||
|
| Hecho | Tarea | Fecha | Observaciones |
|
||||||
|
| :---: | :--- | :--- | :--- |
|
||||||
|
| ✅ | **Fase 0: Planificación y definición** | | |
|
||||||
|
| ✅ | Crear y finalizar el concepto | 30.06.2025 | La arquitectura, los componentes y la arquitectura básica están definidos. |
|
||||||
|
| ✅ | Definir el mapa de registros MODBUS | 30.06.2025 | La "API" de los esclavos está definida y constituye la base para el desarrollo del software. |
|
||||||
|
| ☐ | **Fase 1: Prototipo de nodo esclavo (placa de evaluación STM32)** | | **Objetivo:** Un único esclavo cobra vida en la placa de evaluación. |
|
||||||
|
| ✅ | 1.1 Configurar el entorno de desarrollo para STM32/Zephyr | 30.06.2025 | Instalar la cadena de herramientas, VS Code, el SDK de Zephyr, MCUBoot, etc. y hacer que funcione un "Hola Mundo". |
|
||||||
|
| ☐ | 1.2 Crear el firmware básico para el nodo esclavo | | Implementar la abstracción de hardware (GPIO, ADC, UART para RS485). |
|
||||||
|
| ☐ | 1.3 Implementar la pila MODBUS-RTU en el esclavo | | Basado en el mapa de registros definido. Inicialmente solo funciones de lectura (estado, versión). |
|
||||||
|
| ☐ | 1.4 Implementar la lógica central (p. ej., control de válvulas) | | Implementación de la lógica `VALVE_STATE_MOVEMENT`, medición de corriente para posiciones finales, etc. |
|
||||||
|
| ☐ | **Fase 2: Verificación del firmware del esclavo** | | **Objetivo:** Demostrar que el esclavo se adhiere exactamente a la especificación MODBUS. |
|
||||||
|
| ☐ | 2.1 Probar el nodo esclavo con un PC a través de un adaptador USB-MODBUS | | **Hito crítico.** Leer y escribir los registros con herramientas como "QModMaster" o un script de Python. El firmware del esclavo se valida así independientemente de la puerta de enlace. |
|
||||||
|
| ☐ | 2.2 Probar el mecanismo de actualización de firmware | | Probar el proceso de actualización completo (fragmentación, comprobación de CRC) con un script desde el PC. El esclavo inicialmente solo escribe el firmware en un área de RAM no utilizada. |
|
||||||
|
| ☐ | **Fase 3: Diseño de hardware y construcción de prototipos** | | **Objetivo:** Pasar del desarrollo en la placa de evaluación a un PCB a medida. |
|
||||||
|
| ☐ | 3.1 Diseñar el esquema y el diseño del PCB para el nodo esclavo | | Basado en la experiencia con la placa de evaluación. |
|
||||||
|
| ☐ | 3.2 Pedir y ensamblar placas prototipo | | Por ejemplo, en JLCPCB. Soldar los componentes THT (conectores, etc.) uno mismo. |
|
||||||
|
| ☐ | 3.3 Puesta en marcha del hardware del primer prototipo | | Comprobar voltajes, cargar el firmware y repetir las pruebas de la fase 2 para validar el hardware. |
|
||||||
|
| ☐ | 3.4 Implementar la rutina de escritura en flash para la actualización del firmware | | Aplicar el proceso validado en la RAM en el paso 2.2 a la memoria flash real. |
|
||||||
|
| ☐ | **Fase 4: Desarrollo de la puerta de enlace (placa de evaluación ESP32)** | | **Objetivo:** Construir el puente desde el mundo RS485 a la red doméstica. |
|
||||||
|
| ☐ | 4.1 Crear el firmware de la puerta de enlace (ESPHome) | | Configurar una puerta de enlace simple de MODBUS TCP a RTU en la placa de evaluación ESP32C6. |
|
||||||
|
| ☐ | 4.2 Conectar y probar la puerta de enlace con el prototipo de nodo esclavo | | Probar la cadena: PC (como cliente MODBUS TCP) -> WLAN -> Puerta de enlace -> RS485 -> Esclavo. |
|
||||||
|
| ☐ | **Fase 5: Integración del sistema en Home Assistant** | | **Objetivo:** Hacer que el sistema sea "inteligente". |
|
||||||
|
| ☐ | 5.1 Configurar la integración de MODBUS en Home Assistant | | Crear los sensores y las entidades para el nodo esclavo en `configuration.yaml` o a través de la interfaz de usuario. |
|
||||||
|
| ☐ | 5.2 Crear paneles y automatizaciones en HA | | Visualización de los estados (válvula, bomba, etc.) y creación de la lógica de riego real. |
|
||||||
|
| ☐ | 5.3 Desarrollar un script de Python para la actualización de firmware en HA | | Implementación de la carga basada en fragmentos como un script que se puede llamar desde HA. |
|
||||||
|
| ☐ | **Fase 6: Montaje y puesta en marcha** | | **Objetivo:** Instalar el sistema terminado. |
|
||||||
|
| ☐ | 6.1 Construir y probar todos los nodos esclavos necesarios | | Probar cada esclavo individualmente con el PC a través de un adaptador USB y configurar la dirección MODBUS. |
|
||||||
|
| ☐ | 6.2 Instalación final y cableado del sistema | | Instalación de los componentes en el cobertizo, cableado del bus RS485. |
|
||||||
|
| ☐ | 6.3 Prueba y calibración general del sistema | | Calibrar el sensor de nivel, comprobar los tiempos de funcionamiento de las válvulas, probar el comportamiento a prueba de fallos. |
|
||||||
35
docs/planning.fr.md
Normal file
35
docs/planning.fr.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<img src="./img/logo.png" alt="Logo" width="100"/>
|
||||||
|
|
||||||
|
[🇩🇪 Deutsch](planning.de.md) | [🇬🇧 English](planning.en.md) | 🇫🇷 Français | [🇪🇸 Español](planning.es.md)
|
||||||
|
|
||||||
|
# Plan de projet : Système d'irrigation modulaire
|
||||||
|
|
||||||
|
| Fait | Tâche | Date | Remarques |
|
||||||
|
| :---: | :--- | :--- | :--- |
|
||||||
|
| ✅ | **Phase 0 : Planification & Définition** | | |
|
||||||
|
| ✅ | Créer et finaliser le concept | 30.06.2025 | L'architecture, les composants et l'architecture de base sont définis. |
|
||||||
|
| ✅ | Définir la carte des registres MODBUS | 30.06.2025 | L'"API" des esclaves est définie et constitue la base du développement logiciel. |
|
||||||
|
| ☐ | **Phase 1 : Prototype de nœud esclave (carte d'évaluation STM32)** | | **Objectif :** Un seul esclave est mis en service sur la carte d'évaluation. |
|
||||||
|
| ✅ | 1.1 Mettre en place l'environnement de développement pour STM32/Zephyr | 30.06.2025 | Installer la chaîne d'outils, VS Code, le SDK Zephyr, MCUBoot, etc. et faire fonctionner un "Hello World". |
|
||||||
|
| ☐ | 1.2 Créer le firmware de base pour le nœud esclave | | Implémenter l'abstraction matérielle (GPIO, ADC, UART pour RS485). |
|
||||||
|
| ☐ | 1.3 Implémenter la pile MODBUS-RTU sur l'esclave | | Basé sur la carte des registres définie. D'abord uniquement les fonctions de lecture (état, version). |
|
||||||
|
| ☐ | 1.4 Implémenter la logique de base (par ex. commande de vanne) | | Implémentation de la logique `VALVE_STATE_MOVEMENT`, mesure du courant pour les positions finales, etc. |
|
||||||
|
| ☐ | **Phase 2 : Vérification du firmware de l'esclave** | | **Objectif :** Prouver que l'esclave respecte exactement la spécification MODBUS. |
|
||||||
|
| ☐ | 2.1 Tester le nœud esclave avec un PC via un adaptateur USB-MODBUS | | **Jalon critique.** Lire et écrire les registres avec des outils comme "QModMaster" ou un script Python. Le firmware de l'esclave est ainsi validé indépendamment de la passerelle. |
|
||||||
|
| ☐ | 2.2 Tester le mécanisme de mise à jour du firmware | | Tester le processus de mise à jour complet (fragmentation, vérification CRC) avec un script depuis le PC. L'esclave n'écrit d'abord le firmware que dans une zone RAM inutilisée. |
|
||||||
|
| ☐ | **Phase 3 : Conception matérielle et construction de prototypes** | | **Objectif :** Passer du développement sur la carte d'évaluation à un PCB sur mesure. |
|
||||||
|
| ☐ | 3.1 Concevoir le schéma et le layout du PCB pour le nœud esclave | | Basé sur l'expérience avec la carte d'évaluation. |
|
||||||
|
| ☐ | 3.2 Commander et assembler les cartes prototypes | | Par ex. chez JLCPCB. Souder soi-même les composants THT (connecteurs, etc.). |
|
||||||
|
| ☐ | 3.3 Mise en service matérielle du premier prototype | | Vérifier les tensions, charger le firmware et répéter les tests de la phase 2 pour valider le matériel. |
|
||||||
|
| ☐ | 3.4 Implémenter la routine d'écriture flash pour la mise à jour du firmware | | Appliquer le processus validé en RAM à l'étape 2.2 à la mémoire flash réelle. |
|
||||||
|
| ☐ | **Phase 4 : Développement de la passerelle (carte d'évaluation ESP32)** | | **Objectif :** Construire le pont entre le monde RS485 et le réseau domestique. |
|
||||||
|
| ☐ | 4.1 Créer le firmware de la passerelle (ESPHome) | | Mettre en place une simple passerelle MODBUS TCP vers RTU sur la carte d'évaluation ESP32C6. |
|
||||||
|
| ☐ | 4.2 Connecter et tester la passerelle avec le prototype de nœud esclave | | Tester la chaîne : PC (en tant que client MODBUS TCP) -> WLAN -> Passerelle -> RS485 -> Esclave. |
|
||||||
|
| ☐ | **Phase 5 : Intégration du système dans Home Assistant** | | **Objectif :** Rendre le système "intelligent". |
|
||||||
|
| ☐ | 5.1 Configurer l'intégration MODBUS dans Home Assistant | | Créer les capteurs et les entités pour le nœud esclave dans `configuration.yaml` ou via l'interface utilisateur. |
|
||||||
|
| ☐ | 5.2 Créer des tableaux de bord et des automatisations dans HA | | Visualisation des états (vanne, pompe, etc.) et création de la logique d'irrigation réelle. |
|
||||||
|
| ☐ | 5.3 Développer un script Python pour la mise à jour du firmware dans HA | | Implémentation du téléchargement basé sur des fragments sous forme de script pouvant être appelé depuis HA. |
|
||||||
|
| ☐ | **Phase 6 : Montage et mise en service** | | **Objectif :** Installer le système final. |
|
||||||
|
| ☐ | 6.1 Construire et tester tous les nœuds esclaves nécessaires | | Tester chaque esclave individuellement avec le PC via un adaptateur USB et configurer l'adresse MODBUS. |
|
||||||
|
| ☐ | 6.2 Installation finale et câblage du système | | Installation des composants dans l'abri, câblage du bus RS485. |
|
||||||
|
| ☐ | 6.3 Test et calibrage de l'ensemble du système | | Calibrer le capteur de niveau, vérifier les temps de fonctionnement des vannes, tester le comportement de sécurité. |
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -139,6 +139,16 @@
|
|||||||
)
|
)
|
||||||
(uuid "bb67995e-dd26-405f-a180-469631afb974")
|
(uuid "bb67995e-dd26-405f-a180-469631afb974")
|
||||||
)
|
)
|
||||||
|
(wire
|
||||||
|
(pts
|
||||||
|
(xy 87.63 71.12) (xy 115.57 71.12)
|
||||||
|
)
|
||||||
|
(stroke
|
||||||
|
(width 0)
|
||||||
|
(type default)
|
||||||
|
)
|
||||||
|
(uuid "ce130c11-4895-4efc-9628-ec9d597d9ae6")
|
||||||
|
)
|
||||||
(wire
|
(wire
|
||||||
(pts
|
(pts
|
||||||
(xy 87.63 80.01) (xy 115.57 80.01)
|
(xy 87.63 80.01) (xy 115.57 80.01)
|
||||||
@@ -499,6 +509,16 @@
|
|||||||
(justify left)
|
(justify left)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
(pin "Ivalve" output
|
||||||
|
(at 115.57 71.12 180)
|
||||||
|
(uuid "8d6736b5-3866-4df2-bad1-f2395c16d41f")
|
||||||
|
(effects
|
||||||
|
(font
|
||||||
|
(size 1.27 1.27)
|
||||||
|
)
|
||||||
|
(justify left)
|
||||||
|
)
|
||||||
|
)
|
||||||
(instances
|
(instances
|
||||||
(project "Valve Node"
|
(project "Valve Node"
|
||||||
(path "/161a9599-9ba4-4610-99d7-67cfc29e63e3"
|
(path "/161a9599-9ba4-4610-99d7-67cfc29e63e3"
|
||||||
@@ -581,26 +601,6 @@
|
|||||||
(justify right)
|
(justify right)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
(pin "ESclose" input
|
|
||||||
(at 87.63 87.63 0)
|
|
||||||
(uuid "a290c1a4-d848-4121-a675-4dd8e2e53a46")
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(justify right)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin "ESopen" input
|
|
||||||
(at 87.63 85.09 0)
|
|
||||||
(uuid "aa847fd3-ccb3-4c7d-b0b7-91459ad41b22")
|
|
||||||
(effects
|
|
||||||
(font
|
|
||||||
(size 1.27 1.27)
|
|
||||||
)
|
|
||||||
(justify right)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
(pin "LEDclose" output
|
(pin "LEDclose" output
|
||||||
(at 87.63 92.71 0)
|
(at 87.63 92.71 0)
|
||||||
(uuid "a8dbd0ef-81a9-476b-87d5-ae62f707d780")
|
(uuid "a8dbd0ef-81a9-476b-87d5-ae62f707d780")
|
||||||
@@ -621,6 +621,16 @@
|
|||||||
(justify right)
|
(justify right)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
(pin "Ivalve" input
|
||||||
|
(at 87.63 71.12 0)
|
||||||
|
(uuid "de7fd0b6-2acb-41c1-b304-b822527b109f")
|
||||||
|
(effects
|
||||||
|
(font
|
||||||
|
(size 1.27 1.27)
|
||||||
|
)
|
||||||
|
(justify right)
|
||||||
|
)
|
||||||
|
)
|
||||||
(instances
|
(instances
|
||||||
(project "Valve Node"
|
(project "Valve Node"
|
||||||
(path "/161a9599-9ba4-4610-99d7-67cfc29e63e3"
|
(path "/161a9599-9ba4-4610-99d7-67cfc29e63e3"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
56
setup-format-hook.sh
Executable file
56
setup-format-hook.sh
Executable 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
142
software/.clang-format
Normal 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
|
||||||
10
software/.vscode/extensions.json
vendored
Normal file
10
software/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack",
|
||||||
|
"ms-python.python",
|
||||||
|
"ms-vscode.vscode-embedded-tools",
|
||||||
|
"ms-vscode.vscode-serial-monitor",
|
||||||
|
"marus25.cortex-debug",
|
||||||
|
"donjayamanne.python-environment-manager"
|
||||||
|
]
|
||||||
|
}
|
||||||
28
software/.vscode/launch.json
vendored
Normal file
28
software/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch",
|
||||||
|
"device": "STM32F103RB",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"executable": "build/zephyr/zephyr.elf",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
//"runToEntryPoint": "main",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"gdbPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb",
|
||||||
|
"preLaunchTask": "West Build"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach",
|
||||||
|
"device": "nRF52840_xxAA",
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"executable": "build/zephyr/zephyr.elf",
|
||||||
|
"request": "attach",
|
||||||
|
"type": "cortex-debug",
|
||||||
|
// "runToEntryPoint": "main",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"gdbPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gdb"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
26
software/.vscode/settings.json
vendored
26
software/.vscode/settings.json
vendored
@@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"nrf-connect.applications": [
|
// Hush CMake
|
||||||
"${workspaceFolder}\\modbus_test"
|
"cmake.configureOnOpen": false,
|
||||||
],
|
// IntelliSense
|
||||||
"files.associations": {
|
"C_Cpp.default.compilerPath": "${userHome}/zephyr-sdk-0.17.1/arm-zephyr-eabi/bin/arm-zephyr-eabi-gcc.exe",
|
||||||
"log.h": "c",
|
"C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json",
|
||||||
"modbus.h": "c",
|
// File Associations
|
||||||
"array": "c",
|
"files.associations": {
|
||||||
"string": "c",
|
"app_version.h": "c"
|
||||||
"string_view": "c",
|
},
|
||||||
"canbus.h": "c",
|
"C_Cpp.clang_format_style": "file",
|
||||||
"kernel.h": "c",
|
"nrf-connect.applications": [
|
||||||
"settings.h": "c"
|
"${workspaceFolder}/apps/slave_node"
|
||||||
}
|
],
|
||||||
}
|
}
|
||||||
84
software/.vscode/tasks.json
vendored
Normal file
84
software/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
"presentation": {
|
||||||
|
"reveal": "silent",
|
||||||
|
"clear": true,
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "West Configurable Build",
|
||||||
|
"type": "shell",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
},
|
||||||
|
"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",
|
||||||
|
"${input:pristine}",
|
||||||
|
"-b",
|
||||||
|
"${input:board}"
|
||||||
|
],
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "West Flash",
|
||||||
|
"type": "shell",
|
||||||
|
"linux": {
|
||||||
|
"command": "${userHome}/zephyrproject/.venv/bin/west"
|
||||||
|
},
|
||||||
|
"windows": {
|
||||||
|
"command": "${userHome}/zephyrproject/.venv/Scripts/west.exe"
|
||||||
|
},
|
||||||
|
"osx": {
|
||||||
|
"command": "${userHome}/zephyrproject/.venv/bin/west"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"flash"
|
||||||
|
],
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"id": "board",
|
||||||
|
"type": "promptString",
|
||||||
|
"default": "valve-node",
|
||||||
|
"description": "See https://docs.zephyrproject.org/latest/boards/index.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "pristine",
|
||||||
|
"type": "pickString",
|
||||||
|
"description": "Choose when to run a pristine build",
|
||||||
|
"default": "auto",
|
||||||
|
"options": [
|
||||||
|
"auto",
|
||||||
|
"always",
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1
software/Kconfig
Normal file
1
software/Kconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rsource "lib/Kconfig"
|
||||||
7
software/apps/bl_test/CMakeLists.txt
Normal file
7
software/apps/bl_test/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20.0)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
project(bl_test)
|
||||||
|
|
||||||
|
# Add application source files
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
||||||
5
software/apps/bl_test/VERSION
Normal file
5
software/apps/bl_test/VERSION
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
VERSION_MAJOR = 0
|
||||||
|
VERSION_MINOR = 0
|
||||||
|
PATCHLEVEL = 1
|
||||||
|
VERSION_TWEAK = 1
|
||||||
|
EXTRAVERSION = devel
|
||||||
46
software/apps/bl_test/prj.conf
Normal file
46
software/apps/bl_test/prj.conf
Normal 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
|
||||||
11
software/apps/bl_test/src/main.c
Normal file
11
software/apps/bl_test/src/main.c
Normal 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;
|
||||||
|
}
|
||||||
1
software/apps/bl_test/sysbuild.conf
Normal file
1
software/apps/bl_test/sysbuild.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SB_CONFIG_BOOTLOADER_MCUBOOT=y
|
||||||
9
software/apps/bl_test/sysbuild/bl_test.overlay
Normal file
9
software/apps/bl_test/sysbuild/bl_test.overlay
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
94
software/apps/bl_test/sysbuild/common.dtsi
Normal file
94
software/apps/bl_test/sysbuild/common.dtsi
Normal 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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
0
software/apps/bl_test/sysbuild/flash.dtsi
Normal file
0
software/apps/bl_test/sysbuild/flash.dtsi
Normal file
23
software/apps/bl_test/sysbuild/mcuboot.conf
Normal file
23
software/apps/bl_test/sysbuild/mcuboot.conf
Normal 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
|
||||||
8
software/apps/bl_test/sysbuild/mcuboot.overlay
Normal file
8
software/apps/bl_test/sysbuild/mcuboot.overlay
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#include "common.dtsi"
|
||||||
|
|
||||||
|
/* MCUboot Configuration - Bootloader wird in boot_partition geschrieben */
|
||||||
|
/ {
|
||||||
|
chosen {
|
||||||
|
zephyr,code-partition = &boot_partition;
|
||||||
|
};
|
||||||
|
};
|
||||||
10
software/apps/can_node/CMakeLists.txt
Normal file
10
software/apps/can_node/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
|
||||||
|
project(can_node LANGUAGES C)
|
||||||
|
|
||||||
|
zephyr_include_directories(../../include)
|
||||||
|
add_subdirectory(../../lib lib)
|
||||||
|
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
||||||
2
software/apps/can_node/Kconfig
Normal file
2
software/apps/can_node/Kconfig
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
rsource "../../lib/Kconfig"
|
||||||
|
source "Kconfig.zephyr"
|
||||||
5
software/apps/can_node/VERSION
Normal file
5
software/apps/can_node/VERSION
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
VERSION_MAJOR = 0
|
||||||
|
VERSION_MINOR = 0
|
||||||
|
PATCHLEVEL = 1
|
||||||
|
VERSION_TWEAK = 1
|
||||||
|
EXTRAVERSION = devel
|
||||||
7
software/apps/can_node/boards/bluepill_f103rb.conf
Normal file
7
software/apps/can_node/boards/bluepill_f103rb.conf
Normal 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
|
||||||
43
software/apps/can_node/boards/bluepill_f103rb.overlay
Normal file
43
software/apps/can_node/boards/bluepill_f103rb.overlay
Normal 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";
|
||||||
|
};
|
||||||
47
software/apps/can_node/boards/native_sim.overlay
Normal file
47
software/apps/can_node/boards/native_sim.overlay
Normal 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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
48
software/apps/can_node/boards/weact_stm32g431_core.overlay
Normal file
48
software/apps/can_node/boards/weact_stm32g431_core.overlay
Normal 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";
|
||||||
|
};
|
||||||
16
software/apps/can_node/cdc-acm.overlay
Normal file
16
software/apps/can_node/cdc-acm.overlay
Normal 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;
|
||||||
|
};
|
||||||
88
software/apps/can_node/dts/bindings/st,vnd7050aj.yaml
Normal file
88
software/apps/can_node/dts/bindings/st,vnd7050aj.yaml
Normal 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.
|
||||||
|
|
||||||
4
software/apps/can_node/overlay-cdc-acm.conf
Normal file
4
software/apps/can_node/overlay-cdc-acm.conf
Normal 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
|
||||||
31
software/apps/can_node/prj.conf
Normal file
31
software/apps/can_node/prj.conf
Normal 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
|
||||||
40
software/apps/can_node/src/main.c
Normal file
40
software/apps/can_node/src/main.c
Normal 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;
|
||||||
|
}
|
||||||
5
software/apps/can_node/sysbuild.conf
Normal file
5
software/apps/can_node/sysbuild.conf
Normal 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
|
||||||
8
software/apps/gateway/CMakeLists.txt
Normal file
8
software/apps/gateway/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.20.5)
|
||||||
|
|
||||||
|
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||||
|
|
||||||
|
project(gateway)
|
||||||
|
target_sources(app PRIVATE src/main.c)
|
||||||
|
|
||||||
|
target_include_directories(app PRIVATE include)
|
||||||
44
software/apps/gateway/README.md
Normal file
44
software/apps/gateway/README.md
Normal 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.
|
||||||
5
software/apps/gateway/VERSION
Normal file
5
software/apps/gateway/VERSION
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
VERSION_MAJOR = 0
|
||||||
|
VERSION_MINOR = 0
|
||||||
|
PATCHLEVEL = 1
|
||||||
|
VERSION_TWEAK = 0
|
||||||
|
EXTRAVERSION = devel
|
||||||
16
software/apps/gateway/boards/common_4MB.dtsi
Normal file
16
software/apps/gateway/boards/common_4MB.dtsi
Normal 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";
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "common_4MB.dtsi"
|
||||||
47
software/apps/gateway/prj.conf
Normal file
47
software/apps/gateway/prj.conf
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# -------------------
|
||||||
|
# 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
|
||||||
136
software/apps/gateway/src/main.c
Normal file
136
software/apps/gateway/src/main.c
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
2
software/apps/gateway/sysbuild.conf
Normal file
2
software/apps/gateway/sysbuild.conf
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
SB_CONFIG_BOOTLOADER_MCUBOOT=y
|
||||||
|
SB_CONFIG_MCUBOOT_MODE_SINGLE_APP=y
|
||||||
8
software/apps/gateway/sysbuild/gateway.overlay
Normal file
8
software/apps/gateway/sysbuild/gateway.overlay
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#include "../boards/common_4MB.dtsi"
|
||||||
|
|
||||||
|
/* Application Configuration - Firmware goes to slot0_partition (0x20000) */
|
||||||
|
/ {
|
||||||
|
chosen {
|
||||||
|
zephyr,code-partition = &slot0_partition;
|
||||||
|
};
|
||||||
|
};
|
||||||
3
software/apps/gateway/sysbuild/mcuboot.conf
Normal file
3
software/apps/gateway/sysbuild/mcuboot.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_LOG=y
|
||||||
|
CONFIG_MCUBOOT_LOG_LEVEL_INF=y
|
||||||
|
CONFIG_UART_CONSOLE=n
|
||||||
12
software/apps/gateway/sysbuild/mcuboot.overlay
Normal file
12
software/apps/gateway/sysbuild/mcuboot.overlay
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
1
software/apps/mqtt_gateway
Submodule
1
software/apps/mqtt_gateway
Submodule
Submodule software/apps/mqtt_gateway added at 6e669cfc4e
12
software/apps/slave_node/.vscode/c_cpp_properties.json
vendored
Normal file
12
software/apps/slave_node/.vscode/c_cpp_properties.json
vendored
Normal 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
|
||||||
|
}
|
||||||
10
software/apps/slave_node/CMakeLists.txt
Normal file
10
software/apps/slave_node/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
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)
|
||||||
2
software/apps/slave_node/Kconfig
Normal file
2
software/apps/slave_node/Kconfig
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
rsource "../../lib/Kconfig"
|
||||||
|
source "Kconfig.zephyr"
|
||||||
5
software/apps/slave_node/VERSION
Normal file
5
software/apps/slave_node/VERSION
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
VERSION_MAJOR = 0
|
||||||
|
VERSION_MINOR = 0
|
||||||
|
PATCHLEVEL = 1
|
||||||
|
VERSION_TWEAK = 1
|
||||||
|
EXTRAVERSION = devel
|
||||||
7
software/apps/slave_node/boards/bluepill_f103rb.conf
Normal file
7
software/apps/slave_node/boards/bluepill_f103rb.conf
Normal 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
|
||||||
43
software/apps/slave_node/boards/bluepill_f103rb.overlay
Normal file
43
software/apps/slave_node/boards/bluepill_f103rb.overlay
Normal 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";
|
||||||
|
};
|
||||||
47
software/apps/slave_node/boards/native_sim.overlay
Normal file
47
software/apps/slave_node/boards/native_sim.overlay
Normal 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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
48
software/apps/slave_node/boards/weact_stm32g431_core.overlay
Normal file
48
software/apps/slave_node/boards/weact_stm32g431_core.overlay
Normal 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 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>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&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";
|
||||||
|
};
|
||||||
16
software/apps/slave_node/cdc-acm.overlay
Normal file
16
software/apps/slave_node/cdc-acm.overlay
Normal 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;
|
||||||
|
};
|
||||||
88
software/apps/slave_node/dts/bindings/st,vnd7050aj.yaml
Normal file
88
software/apps/slave_node/dts/bindings/st,vnd7050aj.yaml
Normal 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.
|
||||||
|
|
||||||
4
software/apps/slave_node/overlay-cdc-acm.conf
Normal file
4
software/apps/slave_node/overlay-cdc-acm.conf
Normal 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
|
||||||
28
software/apps/slave_node/prj.conf
Normal file
28
software/apps/slave_node/prj.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Enable Console and printk for logging
|
||||||
|
CONFIG_CONSOLE=y
|
||||||
|
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
|
||||||
|
CONFIG_SETTINGS_NVS=y
|
||||||
|
CONFIG_NVS=y
|
||||||
|
CONFIG_FLASH=y
|
||||||
|
CONFIG_FLASH_MAP=y
|
||||||
|
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||||
|
CONFIG_SETTINGS_LOG_LEVEL_DBG=y
|
||||||
|
|
||||||
|
# Config modbus
|
||||||
|
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||||
|
CONFIG_MODBUS=y
|
||||||
|
CONFIG_MODBUS_ROLE_SERVER=y
|
||||||
|
CONFIG_MODBUS_LOG_LEVEL_DBG=y
|
||||||
|
|
||||||
|
# Enable VND7050AJ
|
||||||
|
CONFIG_VND7050AJ=y
|
||||||
30
software/apps/slave_node/src/main.c
Normal file
30
software/apps/slave_node/src/main.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/settings/settings.h>
|
||||||
|
#include <lib/fwu.h>
|
||||||
|
#include <lib/modbus_server.h>
|
||||||
|
#include <lib/valve.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()) {
|
||||||
|
LOG_ERR("Settings initialization or loading failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
valve_init();
|
||||||
|
fwu_init();
|
||||||
|
|
||||||
|
rc = modbus_server_init();
|
||||||
|
if (rc) {
|
||||||
|
LOG_ERR("Modbus server initialization failed: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INF("Irrigation System Slave Node started successfully");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
software/apps/slave_node/sysbuild.conf
Normal file
5
software/apps/slave_node/sysbuild.conf
Normal 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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Copyright (c) 2025 Eduard Iten
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config BOARD_BLUEPILL_F103RB
|
||||||
|
select SOC_STM32F103XB
|
||||||
106
software/boards/iten/bluepill_f103rb/bluepill_f103rb.dts
Normal file
106
software/boards/iten/bluepill_f103rb/bluepill_f103rb.dts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Eduard Iten
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/dts-v1/;
|
||||||
|
#include <st/f1/stm32f1.dtsi>
|
||||||
|
#include <st/f1/stm32f103Xb.dtsi>
|
||||||
|
#include <st/f1/stm32f103r(8-b)tx-pinctrl.dtsi>
|
||||||
|
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||||
|
|
||||||
|
/ {
|
||||||
|
model = "Blue-Pill STM32F103RB";
|
||||||
|
compatible = "iten,bluepill-f103rb";
|
||||||
|
|
||||||
|
chosen {
|
||||||
|
zephyr,console = &usart1;
|
||||||
|
zephyr,shell-uart = &usart1;
|
||||||
|
zephyr,sram = &sram0;
|
||||||
|
zephyr,flash = &flash0;
|
||||||
|
};
|
||||||
|
|
||||||
|
leds {
|
||||||
|
compatible = "gpio-leds";
|
||||||
|
led_status: led_status {
|
||||||
|
gpios = <&gpioc 13 GPIO_ACTIVE_LOW>;
|
||||||
|
label = "User LED";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
aliases {
|
||||||
|
led0 = &led_status;
|
||||||
|
watchdog0 = &iwdg;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
&clk_lsi {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&clk_hse {
|
||||||
|
clock-frequency = <DT_FREQ_M(8)>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&pll {
|
||||||
|
mul = <9>;
|
||||||
|
clocks = <&clk_hse>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&rcc {
|
||||||
|
clocks = <&pll>;
|
||||||
|
clock-frequency = <DT_FREQ_M(72)>;
|
||||||
|
ahb-prescaler = <1>;
|
||||||
|
apb1-prescaler = <2>;
|
||||||
|
apb2-prescaler = <1>;
|
||||||
|
adc-prescaler = <6>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
&usart1 {
|
||||||
|
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
current-speed = <115200>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&iwdg {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
&clk_lsi {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&clk_hse {
|
||||||
|
clock-frequency = <DT_FREQ_M(8)>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&pll {
|
||||||
|
mul = <9>;
|
||||||
|
clocks = <&clk_hse>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&rcc {
|
||||||
|
clocks = <&pll>;
|
||||||
|
clock-frequency = <DT_FREQ_M(72)>;
|
||||||
|
ahb-prescaler = <1>;
|
||||||
|
apb1-prescaler = <2>;
|
||||||
|
apb2-prescaler = <1>;
|
||||||
|
adc-prescaler = <6>;
|
||||||
|
};
|
||||||
|
|
||||||
|
&exti {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
&dma1 {
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Copyright (c) 2025 Eduard Iten
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
CONFIG_SERIAL=y
|
||||||
|
CONFIG_GPIO=y
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
|
# Copyright (c) 2025 Eduard Iten
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
# keep first
|
# keep first
|
||||||
board_runner_args(stm32cubeprogrammer "--port=swd" "--reset-mode=hw")
|
|
||||||
board_runner_args(jlink "--device=STM32F103RB" "--speed=4000")
|
board_runner_args(jlink "--device=STM32F103RB" "--speed=4000")
|
||||||
|
board_runner_args(stm32cubeprogrammer "--port=swd" "--reset-mode=hw")
|
||||||
|
|
||||||
# keep first
|
# keep first
|
||||||
|
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
||||||
include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake)
|
include(${ZEPHYR_BASE}/boards/common/stm32cubeprogrammer.board.cmake)
|
||||||
include(${ZEPHYR_BASE}/boards/common/openocd-stm32.board.cmake)
|
include(${ZEPHYR_BASE}/boards/common/openocd-stm32.board.cmake)
|
||||||
include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake)
|
include(${ZEPHYR_BASE}/boards/common/blackmagicprobe.board.cmake)
|
||||||
8
software/boards/iten/bluepill_f103rb/board.yml
Normal file
8
software/boards/iten/bluepill_f103rb/board.yml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Copyright (c) 2025 Eduard Iten
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
board:
|
||||||
|
name: bluepill_f103rb
|
||||||
|
vendor: iten
|
||||||
|
socs:
|
||||||
|
- name: stm32f103xb
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
config BOARD_VALVE_NODE
|
|
||||||
select SOC_STM32F103XB
|
|
||||||
|
|
||||||
mainmenu "Controller Area Network sample application"
|
|
||||||
config LOOPBACK_MODE
|
|
||||||
bool "Loopback LOOPBACK_MODE"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Set the can controller to loopback mode.
|
|
||||||
This allows testing without a second board.
|
|
||||||
|
|
||||||
config LOG_CAN_LEVEL
|
|
||||||
int "Log level for CAN"
|
|
||||||
default 3
|
|
||||||
range 0 7
|
|
||||||
help
|
|
||||||
Set the log level for CAN messages.
|
|
||||||
0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Trace, 6 = Debug2, 7 = Debug3
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
board:
|
|
||||||
name: valve_node
|
|
||||||
full_name: Irrigation system CANbus valve node
|
|
||||||
socs:
|
|
||||||
- name: stm32f103xb
|
|
||||||
# revision:
|
|
||||||
# format: number
|
|
||||||
# default: "1"
|
|
||||||
# revisions:
|
|
||||||
# -name: "1"
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2017 Linaro Limited
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/dts-v1/;
|
|
||||||
#include <st/f1/stm32f103Xb.dtsi>
|
|
||||||
#include <st/f1/stm32f103r(8-b)tx-pinctrl.dtsi>
|
|
||||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
|
||||||
|
|
||||||
/ {
|
|
||||||
model = "Iten engineering Valve Node";
|
|
||||||
compatible = "iten,valve-node", "st,stm32f103rb";
|
|
||||||
|
|
||||||
chosen {
|
|
||||||
zephyr,console = &usart1;
|
|
||||||
zephyr,shell-uart = &usart1;
|
|
||||||
zephyr,sram = &sram0;
|
|
||||||
zephyr,flash = &flash0;
|
|
||||||
zephyr,canbus = &can1;
|
|
||||||
};
|
|
||||||
|
|
||||||
leds: leds {
|
|
||||||
compatible = "gpio-leds";
|
|
||||||
|
|
||||||
green_led_2: led_2 {
|
|
||||||
gpios = <&gpiob 2 GPIO_ACTIVE_HIGH>;
|
|
||||||
label = "User LD2";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
gpio_keys {
|
|
||||||
compatible = "gpio-keys";
|
|
||||||
|
|
||||||
user_button: button {
|
|
||||||
label = "User";
|
|
||||||
gpios = <&gpioc 13 GPIO_ACTIVE_LOW>;
|
|
||||||
zephyr,code = <INPUT_KEY_0>;
|
|
||||||
};
|
|
||||||
|
|
||||||
endstopopen: endstop_open {
|
|
||||||
gpios = <&gpiob 4 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
|
||||||
label = "Endstop Open";
|
|
||||||
};
|
|
||||||
|
|
||||||
endstopclose: endstop_closed {
|
|
||||||
gpios = <&gpiob 5 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
|
||||||
label = "Endstop Close";
|
|
||||||
};
|
|
||||||
|
|
||||||
statusopen: status_open {
|
|
||||||
gpios = <&gpiob 14 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
|
||||||
label = "Status Open";
|
|
||||||
};
|
|
||||||
|
|
||||||
statusclose: status_close {
|
|
||||||
gpios = <&gpioa 8 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>;
|
|
||||||
label = "Status Close";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
aliases {
|
|
||||||
led0 = &green_led_2;
|
|
||||||
sw0 = &user_button;
|
|
||||||
watchdog0 = &iwdg;
|
|
||||||
die-temp0 = &die_temp;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
zephyr,user {
|
|
||||||
motoropen: motor_open {
|
|
||||||
gpios = <&gpiob 13 0>;
|
|
||||||
label = "Motor Open";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
&clk_lsi {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&clk_hse {
|
|
||||||
clock-frequency = <DT_FREQ_M(8)>;
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&pll {
|
|
||||||
mul = <9>;
|
|
||||||
clocks = <&clk_hse>;
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&rcc {
|
|
||||||
clocks = <&pll>;
|
|
||||||
clock-frequency = <DT_FREQ_M(72)>;
|
|
||||||
ahb-prescaler = <1>;
|
|
||||||
apb1-prescaler = <2>;
|
|
||||||
apb2-prescaler = <1>;
|
|
||||||
adc-prescaler = <2>;
|
|
||||||
};
|
|
||||||
|
|
||||||
&usart1 {
|
|
||||||
pinctrl-0 = <&usart1_tx_pa9 &usart1_rx_pa10>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
status = "okay";
|
|
||||||
current-speed = <115200>;
|
|
||||||
};
|
|
||||||
|
|
||||||
&usart2 {
|
|
||||||
pinctrl-0 = <&usart2_tx_pa2 &usart2_rx_pa3>;
|
|
||||||
current-speed = <9600>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
status = "okay";
|
|
||||||
modbus0 {
|
|
||||||
compatible = "zephyr,modbus-serial";
|
|
||||||
status = "okay";
|
|
||||||
//de-gpios = <&gpioa 15 GPIO_ACTIVE_LOW>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
&usart3 {
|
|
||||||
pinctrl-0 = <&usart3_tx_pb10 &usart3_rx_pb11>;
|
|
||||||
current-speed = <115200>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
};
|
|
||||||
|
|
||||||
&i2c1 {
|
|
||||||
pinctrl-0 = <&i2c1_scl_remap1_pb8 &i2c1_sda_remap1_pb9>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
status = "okay";
|
|
||||||
clock-frequency = <I2C_BITRATE_FAST>;
|
|
||||||
};
|
|
||||||
|
|
||||||
&iwdg {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&rtc {
|
|
||||||
clocks = <&rcc STM32_CLOCK_BUS_APB1 0x10000000>,
|
|
||||||
<&rcc STM32_SRC_LSI RTC_SEL(2)>;
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&adc1 {
|
|
||||||
pinctrl-0 = <&adc1_in0_pa0>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&die_temp {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&dma1 {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
|
|
||||||
&flash0 {
|
|
||||||
partitions {
|
|
||||||
compatible = "fixed-partitions";
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <1>;
|
|
||||||
|
|
||||||
/* Set 2KB of storage at the end of 128KB flash */
|
|
||||||
storage_partition: partition@1f800 {
|
|
||||||
label = "storage";
|
|
||||||
reg = <0x0001f800 DT_SIZE_K(2)>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
&can1 {
|
|
||||||
pinctrl-0 = <&can_rx_pa11 &can_tx_pa12>;
|
|
||||||
pinctrl-names = "default";
|
|
||||||
status= "okay";
|
|
||||||
bus-speed = < 125000 >;
|
|
||||||
};
|
|
||||||
|
|
||||||
&exti {
|
|
||||||
status = "okay";
|
|
||||||
};
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
# enable uart driver
|
|
||||||
CONFIG_SERIAL=y
|
|
||||||
# enable console
|
|
||||||
CONFIG_CONSOLE=y
|
|
||||||
|
|
||||||
# enable GPIO
|
|
||||||
CONFIG_GPIO=y
|
|
||||||
|
|
||||||
# modbus config
|
|
||||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
|
||||||
CONFIG_UART_LINE_CTRL=n
|
|
||||||
CONFIG_MODBUS=y
|
|
||||||
CONFIG_MODBUS_ROLE_CLIENT=y
|
|
||||||
|
|
||||||
# can config
|
|
||||||
CONFIG_CAN=y
|
|
||||||
CONFIG_CAN_INIT_PRIORITY=80
|
|
||||||
#CONFIG_CAN_MAX_FILTER=5
|
|
||||||
|
|
||||||
# settings
|
|
||||||
CONFIG_FLASH=y
|
|
||||||
CONFIG_FLASH_MAP=y
|
|
||||||
CONFIG_SETTINGS=y
|
|
||||||
CONFIG_SETTINGS_RUNTIME=y
|
|
||||||
CONFIG_NVS=y
|
|
||||||
CONFIG_SETTINGS_NVS=y
|
|
||||||
CONFIG_HEAP_MEM_POOL_SIZE=256
|
|
||||||
CONFIG_MPU_ALLOW_FLASH_WRITE=y
|
|
||||||
5
software/esphome/.gitignore
vendored
Normal file
5
software/esphome/.gitignore
vendored
Normal 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
106
software/esphome/can.yaml
Normal 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]
|
||||||
55
software/esphome/create_secrets.py
Executable file
55
software/esphome/create_secrets.py
Executable 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}")
|
||||||
166
software/esphome/irrigation_system.yaml
Normal file
166
software/esphome/irrigation_system.yaml
Normal 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);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user