IoT-Erweiterung der Regelung einer Junker-Therme (Teil 2a: Vorbereitung der Software)

https://oer-informatik.de/esp32-iot-heizung-software

tl/dr; (ca. 10 min Lesezeit): Für eine Heizungsregelung (ESP32 basierend auf 1-2-4-Bus) sollen Heizungsthermostate (Fritz SmartHome, Dect ULE) ausgelesen werden. Basierend auf den Werten soll die Steuerspannung am Analog-Ausgang des ESP32 eingestellt werden.

Das Gesamtprojekt ist in drei HowTos unterteilt: 1. die Hardware, dazwischen dieser Exkurs zur ESP-Vorbereitung und Teil 2. zur Software.

Teil 2: die Software

Vorbereiten des ESP32

Damit die Software vernünftig erstellt werden kann sind ein paar Vorarbeiten nötig. Wer schon mit dem ESP32 gearbeitet hat, kann diesen Teil hier überspringen:

Der ESP32 sollte installiert und getestet sein. Um ein erstes Gefühl zu erhalten, wie ein Programm mit der Arduino-IDE für den ESP32 geschrieben wird und aus welchen Komponenten es besteht, sollte diese kleine dort verlinkte Blink-Spielerei mal gemacht haben.

Für die folgenden Abschnitte werden jeweils ein Tutuorial verlinkt, was die grundlegenden Schritte erklärt. Weiterhin werden die relevanten neuen Codeabschnitte genannt, die für den jeweiligen Bereich zugeschnitten für dieses Projekt sinnvoll sind. Es wird zudem der Ort angegeben, von dem die Codeabschnitte stammen. Dabei werden - wenn nicht genauer spezifiziert - folgende fünf Bereiche unterschieden:

Eine WiFi-Verbindung aufbauen

Eine stabile WiFi-Verbindung ist natürlich Ausgangspunkt für die weitere Kommunikation, da der ESP die Daten der Thermostate von der Fritz-Box per HTTP-Request erhält. Welche Schritte im Allgemeinen nötig sind ist in diesem Tutorial beschrieben. Ich benenne hier nur kurz die Codeschnipsel, die im Ergebnis dafür nötig sind:

Zwei Bibliotheken müssen ganz oben, bei den relevanten Imports (Abschnitt 1) ergänzt werden und eine Variable für die Verbindung deklariert werden:

Die Zugangsdaten speichere ich immer in einer gesonderten Datei (die nicht mit im Versionscontrollsystem gespeichert wird). Diese muss ebenso per #include eingebunden werden. Der Inhalt dieser Datei ist weiter unten abgedruckt. Alternativ könnten die Werte auch direkt im Code eingegeben werden (an Stelle der mit SECRET_ gekennzeichneten Konstanten). Folgendes muss also in den zweiten Abschnitt (Variablendeklarationen):

Eine Datei secrets.h muss erstellt werden, die (möglichst) nicht in die Versionskontrolle aufgenommen wird. Sie könnte z.B. so aussehen (natürlich individualisiert auf das eigene WLAN). Anstelle von Konstanten nutze ich hier Präprozessor-Direktiven:

In der setup() (Abschnitt 3) wird die Verbindung konfiguriert und erstmalig aufgebaut:

In der loop() (Abschnitt 4) wird immer wieder überprüft, ob eine WLAN Verbindung besteht und diese andernfalls neu aufgebaut:

Die dafür nötige neue Funktion (Abschnitt 5) prüft die Verbindung, baut fehlende Verbindungen auf und bootet bei häufigen Fehlversuchen den ESP einfach neu. Diese Funktion ist so noch nicht lauffähig, da sie bereits die debugOutput()-Funktion aufruft, die wir erst unten ergänzen (ggf. die betreffenden Zeilen zum Testen auskommentieren).

Die Funktion prüft, ob eine WLAN-Verbindung exitiert, baut sie ggf. wieder auf und startet den ESP bei mehreren Fehlversuchen neu. Bei früheren Projekten musste ich immer mal den Strom abklemmen, um einen Neustart herbeizuführen - diese Variante läuft bei mir stabil (bzw. startet sich selbständig neu).

Over the Air (OTA) Updates

Dieser Abschnitt ist optional. Ich finde es bei allen fertigen Projekten sehr hilfreich, über WLAN neue Firmware aufspielen zu können. Vor allem bei Geräten, die nicht mobil sind und über keine dauerhafte USB-Verbindung verfügen kann ich das nur empfehlen.

Die Funktionalität stellt der relevanten Import ArduinoOTA.h bereit. Es sind noch ein paar frei wählbare Konfigurationswerte nötig. Die Werte der Variablen müssen wieder (wie bei den WLAN Credentials auch) in der Datei secrets.h übergeben werden. Die Flag ENABLE_UPDATE_JUMPER dient dazu, OTA per Jumper/Schalter deaktivieren zu können:

Daraus folgt, dass auch die secrets.h erweitert werden muss um folgende Eingaben (kann beliebig individualisiert werden - sollte nur bekannt sein, um beides in der Arduino-IDE bei einem WLAN-Update eingeben zu können):

Ich möchte die OTA Funktion per Schalter deaktivieren können, daher muss ich dafür noch einen Pin definieren:

Nach der Aktivierung der WLAN-Verbindung muss der OTA-Service aktiviert werden. Hierzu muss in der Die setup() nach ensureWIFIConnection(); eingegeben werden (Abschnitt 3):

In der loop() (Abschnitt 4) wird dann bei jedem Durchlauf geprüft, ob ein neues Update installiert werden will. Ggf. kann hilfreich sein, diese Funktion über einen Jumper/Taster/Schalter freizuschalten.

Was fehlt ist die Funktion startOTA(). Diese habe ich komplett aus den Beispielen der Bibliothek übernommen, lediglich die debugOutput() Funktion eingefügt - daher ist es auch erst lauffähig, wenn diese implementiert wurde (siehe unten).

Die relevanten neuen Funktionen (Abschnitt 5):

Klappt das Hochladen einer neuen Version per WLAN? Fein.

Loggen der Werte und Debugging-Meldungen

Bei so einem Prototyp gibt es immer viel nachzubessern. Besser, man hat auch im eingebauten Zustand einen Weg, an Log-Dateien zu kommen. Ich löse das über einen Webserver, das setzt natürlich die funktionierende WLAN-Verbindung voraus (aber die sollte ja bereits bestehen). Detailiert habe ich das in diesem Artikel beschrieben. Das Beispiel hier ist abgekürzt. Sinnvoll erscheint es z.B. (wie auch oben bei OTA) über einen Jumper oder ähnliches das Debugging zu aktivieren.

Es werden in Abschnitt 1 und 2 neue Imports für den Webserver und den Zeitserver benötigt und Webserver, Zeitserver und Loglevel konfiguriert werden:

//-------------------------------------------------------------------------------------
// Configuration of the NTP-Server
//-------------------------------------------------------------------------------------

#include "time.h"
const char* NTP_SERVER = "pool.ntp.org";
const long GMT_OFFSET_SEC = 3600;
const int DAYLIGHT_OFFSET_SEC = 3600;

//-------------------------------------------------------------------------------------
// Set Route and Port for the Logpage-Webserver
//-------------------------------------------------------------------------------------

#include <WebServer.h>
const int WEBSERVER_PORT = 8085;
const char* WEBSERVER_ROUTE_TO_DEBUG_OUTPUT = "/log";

WebServer server(WEBSERVER_PORT);
String setupLogText = "";
String loopLogText = "";

//-------------------------------------------------------------------------------------
// Logging to serial console?
// If following line is commentet ("//#define DEBUG") all logging-operations will be
// replaced by "", otherwise if "#define DEBUG" is present logging will be sent to serial
//-------------------------------------------------------------------------------------

#define DEBUG  //Flag to activate logging to serial console (i.e. serial monitor in arduino ide)

#ifdef DEBUG
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif


//-------------------------------------------------------------------------------------
// LogLevels used in this example. Only entries bigger than LOG_LEVEL will be written
//-------------------------------------------------------------------------------------

String LOG_LEVEL_NAMES[] = {"OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE", "ALL"};
const int MIN_LOG_LEVEL = 7;

Ich möchte Logging per Schalter aktivieren/deaktivieren können - muss daher einen Pin definieren:

In der setup() (Abschnitt 3) wird schließlich der Zeitserver konfiguriert und der Webserver gestartet:

In der loop() (Abschnitt 4) wird schließlich nach OTA und ensureWIFIConnection(); abgefragt, ob es Website-Anfragen (GET-Requests) gibt, die beantwortet werden müssen.

Das Logging läuft komplett über eine neue Funktion (in Abschnitt 5). Im restlichen Code muss jetzt statt direkt Serial.println() aufzurufen eine der drei überladenen debugOutput() stehen.

Was fehlt? Die Datei, die den Inhalt der Website erstellt und die response absendet (respondLogfile()) und eine Datei, die das alles in ein HTML-Dokument verpackt (renderHtml()).

So, das waren alles Vorarbeiten. Jetzt fangen wir überhaupt erst an mit der Heizungsteuerung:

Weiter zu Teil 2: Software

Quellen und offene Ressourcen (OER)

Die Ursprungstexte (als Markdown), Grafiken und zugrunde liegende Diagrammquelltexte finden sich (soweit möglich in weiterbearbeitbarer Form) in folgendem git-Repository:

https://gitlab.com/oer-informatik/mcu/iot-therme.

Sofern nicht explizit anderweitig angegeben sind sie zur Nutzung als Open Education Resource (OER) unter Namensnennung (H. Stein, oer-informatik.de) freigegeben gemäß der Creative Commons Namensnennung 4.0 International Lizenz (CC BY 4.0).

Creative Commons Lizenzvertrag

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: