Sensoren¶
Auf dieser Seite verbinden Sie zwei Sensoren und geben ihre Daten auf dem Portal aus. Zunächst SHT31 (Schrank-Klima), dann Thermistor (Heizer-Temperatur). Dies ist der Schritt „Daten erhalten" vor dem Hinzufügen der Steuerlogik.
Das Prinzip der Arbeit mit dem Kern ist einfach: Ihr Code in loop() schreibt frische Messwerte in die Felder s_link.telemetry.*, und die Fassade veröffentlicht sie automatisch jeden telemetryPeriodMs aus Config in die Cloud. Ein manueller Aufruf der Veröffentlichung ist nicht erforderlich.
Telemetrie-Felder¶
Für unseren Schrank werden drei Felder verwendet (Index [0] — erste und einzige Kammer):
| Feld | Speicherinhalt | Flag in Config |
|---|---|---|
s_link.telemetry.airTempC[0] |
Lufttemperatur, °C | hasAirTemp |
s_link.telemetry.airHumidityPct[0] |
Luftfeuchte, % | hasAirHumidity |
s_link.telemetry.heaterTempC[0] |
Heizer-Temperatur, °C | hasHeaterTemp |
Diese drei Flags werden genau hier in Config aktiviert (siehe das vollständige Listing am Ende des Kapitels). Das Flag teilt Portal und App mit, dass das Gerät einen solchen Sensor hat: ohne es erscheint die Zelle auf der Karte nicht.
Regel: Sensoren-Code darf loop() nicht blockieren¶
Die idryer-core Fassade bedient Wi-Fi und MQTT in derselben loop(). Daher können Sie beim Auslesen von Sensoren nicht delay() aufrufen — eine Pause unterbricht die Netzsitzung. Der Sensor wird nach einem Timer abgefragt, und der fertige Wert wird einfach gelesen. Fertige Treiber aus dem Ökosystem sind bereits so konstruiert.
Schritt 1. SHT31: Schrank-Klima¶
Es ist nicht nötig, den SHT31-Treiber von Grund auf zu schreiben — die fertige Klasse Sht31ClimateSensor liegt im Beispiel dieses Kapitels, example/09-cabinet. Sie verwendet die robtillaart/SHT31 Bibliothek und liest den Sensor blockierungsfrei.
-
Fügen Sie die SHT31-Bibliothek in
lib_depsIhrerplatformio.inihinzu: -
Kopieren Sie vier Treiber-Dateien in Ihren
src/-Ordner: -
Verbinden Sie den Sensor über I2C (siehe Schaltplan) und lesen Sie ihn in
src/main.cpp:
#include <Wire.h>
#include <iDryer.h>
#include "Sht31ClimateSensor.h"
static Sht31ClimateSensor s_climate(&Wire);
static bool s_climateOk = false;
void setup() {
Serial.begin(115200);
Wire.begin(8, 9); // SDA, SCL — Pins Ihres Boards
s_climateOk = s_climate.begin(); // findet die Adresse 0x44 oder 0x45 selbst
s_link.begin();
// Gerät im Portal entkoppelt: Geheimnis löschen, auf neue Kopplung warten.
s_link.onCommand("revoke", [](JsonObjectConst) { s_link.handleRevoke(); });
}
void loop() {
s_link.loop();
if (s_climateOk) {
s_climate.tick(millis());
SensorReading r = s_climate.get();
if (r.ok) {
s_link.telemetry.airTempC[0] = r.temperature;
s_link.telemetry.airHumidityPct[0] = r.humidity;
}
}
}
Eine einzelne Messwertaufnahme gibt der Treiber als Struktur SensorReading aus sensor_reading.h zurück:
struct SensorReading {
float temperature = NAN; // °C, NAN wenn es keinen Wert gibt
float humidity = NAN; // % RH, NAN wenn es keinen Wert gibt
float pressure = NAN; // hPa, für künftige Sensoren
uint32_t ts_ms = 0; // millis() zum Zeitpunkt des Lesens
bool ok = false; // true, wenn Temperatur und Feuchte gültig sind
int err = 0; // Fehlercode, 0 — kein Fehler
};
Nach dem Firmware-Upload werden Temperatur und Feuchte des Schranks auf dem Portal angezeigt — dies ist die erste Rückmeldung vom Gerät.
Schritt 2. Thermistor: Heizer-Temperatur¶
Es gibt keine fertige Thermistor-Klasse für den ESP32, daher schreiben wir sie direkt in src/main.cpp. Der Thermistor ist an einem ADC-Pin über einen Spannungswandler angeschlossen (siehe Schaltplan): Der Controller misst die Spannung an der mittleren Leitung, berechnet daraus den Thermistor-Widerstand und dann die Temperatur.
#include <math.h>
static const int THERM_PIN = 2; // ADC-Pin
static const float SERIES_R = 4700.0f; // Teiler-Widerstand, Ohm
static const float NOMINAL_R = 100000.0f; // Thermistor-Widerstand bei 25 °C, Ohm
static const float NOMINAL_T = 25.0f; // °C
static const float BETA = 3950.0f; // B-Koeffizient aus dem Thermistor-Datenblatt
// Gibt die Heizer-Temperatur in °C zurück.
static float readHeaterTempC() {
int raw = analogRead(THERM_PIN); // 0..4095 auf ESP32
float v = (float)raw / 4095.0f; // Bruchteil der Gesamtskala
float r = SERIES_R * (1.0f - v) / v; // Thermistor-Widerstand, Ohm
// Steinhart-Hart-Gleichung in der B-Parameter-Form — siehe Wikipedia:
// https://en.wikipedia.org/wiki/Steinhart%E2%80%93Hart_equation
float tK = 1.0f / (1.0f / (NOMINAL_T + 273.15f) + logf(r / NOMINAL_R) / BETA);
return tK - 273.15f;
}
Schreiben Sie das Ergebnis in loop() in die Telemetrie neben der SHT31-Ablesung:
Dies ist vereinfachtes Auslesen — passen Sie die Parameter an Ihren Thermistor an
Die Konstanten NOMINAL_R und BETA hängen vom spezifischen Thermistor ab — nehmen Sie sie aus dem Datenblatt (ein verbreiteter gewöhnlicher Thermistor — Generic 3950, 100 kΩ). Die Teiler-Formel entspricht der Schaltung aus Schaltplan: Thermistor auf 3.3V, Widerstand auf GND. Bei anderer Verdrahtung ändert sich die Formel. Der ADC auf dem ESP32 ist nichtlinear, daher werden die Messwerte für genaue Messungen kalibriert — in Serie iDryer-Controllern wird dafür eine Thermistor-Tabelle verwendet (Bibliothek Thermistor).
Überprüfung des Thermistors mit einem Multimeter — Thermistor überprüfen.
Schritt 3. Keine Sensoren zur Hand? Demo-Modus¶
Den Weg bis zur Karte können Sie auch ohne Hardware gehen: die Messwerte berechnet ein Modell des Schranks. Kopieren Sie die Datei demo_sensors.h aus demselben Beispiel:
und fügen Sie in platformio.ini das Build-Flag hinzu:
Beide Zweige stecken hinter einer einzigen Funktion, und loop() weiß nicht, woher die Werte kommen:
static void readSensors() {
#ifdef DEMO_SENSORS
demoSensors(s_link.telemetry);
#else
// Auslesen von SHT31 und Thermistor — wie oben
#endif
}
Das Modell verhält sich wie ein echter Schrank: der Raum schwankt langsam um 24 °C, ein eingeschalteter Heizer erwärmt die Luft, ein ausgeschalteter lässt sie abkühlen, beim Aufheizen sinkt die Feuchte. Die Heizleistung liest das Modell aus der Telemetrie, deshalb sieht die Logik aus dem Kapitel Heizungssteuerung eine Reaktion und die Hysterese funktioniert. Die Screenshots in diesem Abschnitt sind genau so entstanden.
Für ein Arbeitsgerät setzt man das Flag nicht: dann wird der Zweig mit den echten Sensoren gebaut.
Vollständig src/main.cpp nach diesem Kapitel¶
Unten ist die gesamte Datei. Neue Zeilen relativ zum letzten Kapitel sind mit // ← Kapitel 5 gekennzeichnet; der Rest hat sich nicht geändert.
Was bisher da war — src/main.cpp nach Kapitel 4
#include <iDryer.h>
static const iDryer::Config CFG = {
.deviceType = iDryer::DeviceType::Unknown, // eigenes Gerät: die Karte baut das Manifest
.unitsCount = 1,
.telemetryPeriodMs = 5000,
.statusPeriodMs = 10000,
.hardwareVersion = "1.0",
.firmwareVersion = "0.1.0",
.model = "DIY Storage Cabinet",
};
static iDryer::Link s_link(CFG);
void setup() {
Serial.begin(115200);
s_link.begin();
// Gerät im Portal entkoppelt: Geheimnis löschen, auf neue Kopplung warten.
s_link.onCommand("revoke", [](JsonObjectConst) { s_link.handleRevoke(); });
}
void loop() {
s_link.loop();
}
#include <iDryer.h>
#include <Wire.h> // ← Kapitel 5
#include <math.h> // ← Kapitel 5
#include "Sht31ClimateSensor.h" // ← Kapitel 5
#include "demo_sensors.h" // ← Kapitel 5: Messwerte ohne Sensoren (-DDEMO_SENSORS=1)
static const iDryer::Config CFG = {
.deviceType = iDryer::DeviceType::Unknown, // eigenes Gerät: die Karte baut das Manifest
.unitsCount = 1,
.hasAirTemp = true, // ← Kapitel 5
.hasAirHumidity = true, // ← Kapitel 5
.hasHeaterTemp = true, // ← Kapitel 5
.telemetryPeriodMs = 5000,
.statusPeriodMs = 10000,
.hardwareVersion = "1.0",
.firmwareVersion = "0.1.0",
.model = "DIY Storage Cabinet",
};
static iDryer::Link s_link(CFG);
// ← Kapitel 5: SHT31 Klimasensor
static Sht31ClimateSensor s_climate(&Wire);
static bool s_climateOk = false;
// ← Kapitel 5: Heizer-Thermistor
static const int THERM_PIN = 2;
static const float SERIES_R = 4700.0f;
static const float NOMINAL_R = 100000.0f;
static const float NOMINAL_T = 25.0f;
static const float BETA = 3950.0f;
static float readHeaterTempC() {
int raw = analogRead(THERM_PIN);
float v = (float)raw / 4095.0f;
float r = SERIES_R * (1.0f - v) / v;
float tK = 1.0f / (1.0f / (NOMINAL_T + 273.15f) + logf(r / NOMINAL_R) / BETA);
return tK - 273.15f;
}
// ← Kapitel 5: Sensoren oder, mit -DDEMO_SENSORS=1, das Modell des Schranks
static void readSensors() {
#ifdef DEMO_SENSORS
demoSensors(s_link.telemetry);
#else
if (s_climateOk) {
s_climate.tick(millis());
SensorReading r = s_climate.get();
if (r.ok) {
s_link.telemetry.airTempC[0] = r.temperature;
s_link.telemetry.airHumidityPct[0] = r.humidity;
}
}
s_link.telemetry.heaterTempC[0] = readHeaterTempC();
#endif
}
void setup() {
Serial.begin(115200);
Wire.begin(8, 9); // ← Kapitel 5 (SDA, SCL — Pins Ihres Boards)
s_climateOk = s_climate.begin(); // ← Kapitel 5
s_link.begin();
// Gerät im Portal entkoppelt: Geheimnis löschen, auf neue Kopplung warten.
s_link.onCommand("revoke", [](JsonObjectConst) { s_link.handleRevoke(); });
}
void loop() {
s_link.loop();
readSensors(); // ← Kapitel 5
}
Überprüfung des Ergebnisses¶
Messwerte auf der Karte und Telemetrie-Diagramm. Die Heizer-Temperatur läuft als eigene Linie im Diagramm. Das Menü unten auf der Seite ist noch leer — darum kümmert sich das nächste Kapitel.
Nach diesem Schritt sollten drei Werte auf dem Portal angezeigt werden:
- Lufttemperatur im Schrank;
- Feuchte im Schrank;
- Heizer-Temperatur.
Wenn die Messwerte „schwankend" sind oder offensichtlich falsch:
- überprüfen Sie die Gesamterde und die Verdrahtung (Störungen von Stromkabeln) — Verdrahtungsfehler;
- überprüfen Sie den Nominalwert des Teiler-Widerstands und den Typ des Thermistors;
- stellen Sie sicher, dass der SHT31 über I2C antwortet (richtige Adresse und Leitungen).
Diagnose „Sensor zeigt Müll" — Thermistor überprüfen und Typische Fehler.
Was ist als Nächstes¶
Wir haben Daten von den Sensoren. Jetzt werden wir die Geräteeinstellungen (Zieltemperatur, Hysterese) im Menü aus YAML beschreiben, um sie vom Portal aus ändern und im Speicher speichern zu können.