TTGO-lora32-V1 Frostschutz
Der daumengrosse ESP32 mit OLED und Gehäuse ist schon mehrere Jahre alt. Für Meshtastic taugt er nicht mehr und hat jetzt seine Aufgabe als Frostschutz-Wächter in der Schreinerhütte gefunden. Im Team mit einer smarten Steckdose NOUS A1T und einem alten Heizlüfter hält er dort die Temperatur knapp über dem Gefrierpunkt.
Interessant an der Aufgabe war, dass ich bestehende Messreihen aus anderen Sensoren schon in einer Influxdatenbank zur Verfügung habe. Der ESP32 fragt über http.get die Influxdatenbank auf einem anderen Rechner ab, ermittelt den letzten Temperaturwert für Innen in der Schreinerhütte und entscheidet dann, ob er über einen http.get die Api der Steckdose nutzt und Strom für den Heizlüfter freischält.
Überblick
Der Code ist ein Frostschutz-System, das auf einem ESP32-Mikrocontroller basiert. Es liest die Temperatur aus einer Influx-Datenbank, prüft, ob die Temperatur unter einer bestimmten Schwelle liegt, und schaltet ein Relais ein oder aus, um eine Heizung zu steuern. Der Code verwendet auch ein OLED-Display, um die aktuelle Temperatur und die Einstellungen anzuzeigen.
Komponenten
- ESP32-Mikrocontroller
- OLED-Display
- Influx-Datenbank
- Relais
- Heizung
Funktionen
- Lesen der Temperatur aus der Influx-Datenbank
- Prüfen, ob die Temperatur unter einer bestimmten Schwelle liegt
- Schalten des Relais ein oder aus, um die Heizung zu steuern
- Anzeigen der aktuellen Temperatur und Einstellungen auf dem OLED-Display
- Webserver, um die Einstellungen zu ändern
Code-Struktur
setup(): Initialisiert den ESP32, das OLED-Display und den Webserverloop(): Lesen der Temperatur aus der Influx-Datenbank, Prüfen der Temperatur, Schalten des Relais und Anzeigen der aktuellen Temperatur und Einstellungen auf dem OLED-DisplaygenerateHtml(): Generiert die HTML-Seite für den WebserverhandleRoot(): Handelt die Anfrage auf die Wurzel-URL des WebserverhandleUpdate(): Handelt die Anfrage auf die Update-URL des Webserver
Variablen
ein_temp: Untergrenze für die Temperatur, unter der die Heizung eingeschaltet wirdaus_temp: Obergrenze für die Temperatur, über der die Heizung ausgeschaltet wirdtemperatur: Aktuelle Temperatur, die aus der Influx-Datenbank gelesen wirdlastQueryTime: Letzter Zeitpunkt, an dem die Influx-Datenbank abgefragt wurdequeryInterval: Intervall, in dem die Influx-Datenbank abgefragt wird
Bibliotheken
WiFi.h: Für die Verbindung zum WLANHTTPClient.h: Für die Kommunikation mit der Influx-DatenbankArduinoJson.h: Für die Verarbeitung von JSON-DatenWire.h: Für die Kommunikation mit dem OLED-DisplayWebServer.h: Für den WebserverU8g2lib.h: Für das OLED-Display
Die Bilder oben zeigen das schon in die Jahre gekommene TTGO lora32 mit dem OLED und dem dort angezeigten Infotext.
Darunter die unter der angegebenen IP-Adresse bereitgestellte Webseite um die Grenzen für Ein- und Aus-Temperatur einzustellen ohne neu flashen zu müssen.
Die beiden Textblöcke zeigen exemplarisch die Ausgabe im seriellen Terminal.

Der Code für PlatformIO
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Wire.h>
#include <WebServer.h>
// OLED Displaytreiber
#include <U8g2lib.h>
// OLED Pins
#define OLED_SCL 15 // GPIO 15
#define OLED_SDA 4 // GPIO 4
#define OLED_RST 16 // GPIO 16
// define display type
U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ OLED_SCL, /* data=*/ OLED_SDA, /* reset=*/ OLED_RST);
// Zugang WLAN
#include <credentials.h>
// Influx Datenbank
const char* url = "http://192.168.22.148:8086/query";
const char* influxDbDatabase = "klima";
const char* influxDbMeasurement = "huettenluft";
WiFiClient client;
HTTPClient http;
// Zeitverzögerung
unsigned long lastQueryTime = 0;
const unsigned long queryInterval = 300000; // 5 Minuten in Millisekunden
String ipStr;
float temperatur; // Ist-Temp aus der Influx-Datenbank
int ein_temp = -2; // Untergrenze zum einschalten
int aus_temp = 2; // Obergrenze zum wieder ausschalten.
WebServer server(80);// Server
// Funktion, um die Webseite zur Grenz-Einstellung zu generieren
String generateHtml() {
String html = "<!DOCTYPE html><html><head><title>Temperatur-Einstellungen</title></head><body>";
html += "<h1>Temperatur-Einstellungen</h1>";
html += "<p>Aktuelle Einstellungen: Ein-Temperatur = " + String(ein_temp) + ", Aus-Temperatur = " + String(aus_temp) + "</p>";
html += "<form action=\"/update\" method=\"post\">";
html += "<label for=\"ein_temp\">Ein-Temperatur:</label>";
html += "<input type=\"range\" id=\"ein_temp\" name=\"ein_temp\" min=\"-10\" max=\"20\" value=\"" + String(ein_temp) + "\">";
html += "<br>";
html += "<label for=\"aus_temp\">Aus-Temperatur:</label>";
html += "<input type=\"range\" id=\"aus_temp\" name=\"aus_temp\" min=\"-10\" max=\"20\" value=\"" + String(aus_temp) + "\">";
html += "<br>";
html += "<input type=\"submit\" value=\"Speichern\">";
html += "</form>";
html += "</body></html>";
return html;
}
void handleRoot() {
Serial.println("Anfrage auf / empfangen");
server.send(200, "text/html", generateHtml());
}
void handleUpdate() {
Serial.println("Anfrage auf /update empfangen");
if (server.method() == HTTP_POST) {
if (server.hasArg("ein_temp") && server.hasArg("aus_temp")) {
ein_temp = server.arg("ein_temp").toInt();
aus_temp = server.arg("aus_temp").toInt();
Serial.println("Temperatur-Einstellungen aktualisiert: ein_temp = " + String(ein_temp) + ", aus_temp = " + String(aus_temp));
}
}
server.send(200, "text/html", generateHtml());
}
void setup() {
Serial.begin(9600);
// OLED-Display initialisieren
u8x8.begin();
u8x8.setFont(u8x8_font_chroma48medium8_r);
//u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
u8x8.drawString (0, 0, "Frostschutz");
u8x8.drawString (0, 6, "Wartezeit 5min.");
// Verbindung zum WLAN herstellen
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Verbindung zum WLAN herstellen...");
}
Serial.println("Verbindung zum WLAN hergestellt!");
IPAddress ip = WiFi.localIP();
ipStr = ip.toString();
//Serial.println("Aktuelle IP-Adresse: " + ip.toString());
Serial.println("Aktuelle IP-Adresse: " + ipStr);
server.on("/", HTTP_GET, handleRoot);
server.on("/update", HTTP_POST, handleUpdate);
server.begin();
Serial.println("Webserver gestartet");
}
void loop() {
// locale Webseite prüfen
server.handleClient();
delay(2);
// Influx-Datenbank abfragen
unsigned long currentTime = millis();
if (currentTime - lastQueryTime >= queryInterval) {
//u8x8.drawString (0, 5, "Abfrage Influx");
http.begin(client, url);
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
String params = "pretty=true&db=klima&q=SELECT%20last(B_Temperatur)%20FROM%20huettenluft%20WHERE%20(device%20=%20%278266-2DS-BME280%27)";
int httpResponseCode = http.POST(params);
if (httpResponseCode > 0) {
String response = http.getString();
Serial.println(response);
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, response);
u8x8.drawString (0, 5, "InfluxDB Response ok");
if (error) {
Serial.print(F("deserializeJson() fehlgeschlagen: "));
Serial.println(error.f_str());
return;
}
JsonArray results = doc["results"];
JsonObject series = results[0]["series"][0];
JsonArray values = series["values"];
// die Temperatur interessiert uns
temperatur = values[0][1];
String datum_s = values[0][0];
//"2025-11-05T17:06:26.275077541Z
datum_s = datum_s.substring(11,16); // nur Uhrzeit
//const char* datum = datum_s.c_str();
Serial.println(datum_s);
Serial.print("Temperatur: ");
Serial.println(temperatur);
} else {
Serial.print("Fehler bei der Anfrage: ");
Serial.println(httpResponseCode);
}
Serial.println("Aktuelle Temperatur-Einstellungen: ein_temp = " + String(ein_temp) + ", aus_temp = " + String(aus_temp));
// Relais ein- oder ausschalten
//http://<ip>/cm?cmnd=Power%20On
//http://<ip>/cm?cmnd=Power%20off
if (temperatur < ein_temp) {
http.begin(client, "http://192.168.20.30/cm?cmnd=Power%20On");
http.GET();
Serial.println(" Relais eingeschaltet");
u8x8.clearDisplay();
u8x8.drawString (0, 0, "Frostschutz");u8x8.drawString (12, 0, "EIN");
u8x8.drawString (0, 3, "Temp frostig");u8x8.drawString(11, 2, String(temperatur).c_str());
u8x8.drawString (0, 4, String(ipStr).c_str());
} else if (temperatur > aus_temp) {
http.begin(client, "http://192.168.20.30/cm?cmnd=Power%20off");
http.GET();
Serial.println(" Relais ausgeschaltet");
u8x8.clearDisplay();
u8x8.drawString (0, 0, "Frostschutz");u8x8.drawString (12, 0, "AUS");
u8x8.drawString (0, 1, "-->");u8x8.drawString (4, 1, String(ein_temp).c_str());u8x8.drawString (7, 1, " bis ");u8x8.drawString (13, 1, String(aus_temp).c_str());
u8x8.drawString (0, 2, "Temp OK");u8x8.drawString(11, 2, String(temperatur).c_str());
u8x8.drawString (0, 4, String(ipStr).c_str());
} else {
Serial.println(" Temp check: " + String(temperatur) + "°C");
u8x8.clearDisplay();
u8x8.drawString (0, 2, "Temp ist ");u8x8.drawString(11, 2, String(temperatur).c_str());
//u8x8.drawString (0, 4, String(datum).c_str());
}
// http.end();
u8x8.drawString (0, 0, "Frostschutz");
u8x8.drawString (0, 6, "Wartezeit 5min.");
lastQueryTime = currentTime;
}
}



Schreibe einen Kommentar