====== WWW: Platinen mit TESTCODE======
* [[platinenschaltung-Außenbox|WWW: Platinen mit TESTCODE für die Außenbox]]
^ Seite ^ Inhalt ^
| [[www-messdatenerfassung-sas:startseite|Startseite]] | Übersicht | |
| [[www-messdatenerfassung-sas:klimastation|Hauptanleitung]] | Gesamtüberblick, Begriffsklärung, Teil 1–3 (FRITZ!Box, Raspberry Pi/EmonCMS, Sensor-Boxen), Ein-/Ausschalten, Zugänge-Checkliste, SENSOR_ID-Übersicht | |
| [[www-messdatenerfassung-sas:sensoren_einbinden|Sensoren in EmonCMS einbinden]] | Wiederkehrender Ablauf für jede neue Sensor-Box: API-Key, Inputs, Feeds, Dashboard | |
| [[www-messdatenerfassung-sas:sensor_code|Programmcode der Sensor-Boxen]] | Vollständiger Arduino-Code: Testsensor, einfacher BME280-Sensor, Klimamessbox, Raummessbox | |
| [[www-messdatenerfassung-sas:raspi_rtc_hw111|Raspberry Pi um Echtzeituhr (RTC) erweitern – HW-111]] | Nachrüstung eines DS1307-RTC-Moduls, Shutdown-Taster | |
| [[Platinenschaltung|Schaltung]] | **Platine** |
| [[3D-Gehäuse|Gehäuse]] | 3D-Druck oder LASER-Cutter |
| [[Unterrichtseinsatz|Schule]] | Einsatz im Unterricht |
| [[Sonstiges|Sonstiges]]| dies und das....|
* [[https://easyeda.com|Platinenerstellung - extern]]
* Nach Erstellung benötigt die günstigste "Verschiffung" 2 Wochen (kostet aber unter 5 EURO)
* Bei unseren Platinen kostete eine Platine 1,5 Euro
* [[https://www.youtube.com/watch?v=WAt0aocWS6w&t=121s|Nach diesem Video hat es bei uns auf Anhieb funktioniert ;-)]]
* [[lehrer:arduino:p52d1-raumklima-feinstaub-licht|nur intern für SAS-Lehrer: P52-D1-mini]]
* [[info:3d-druck:sds-011-feinstaubsensor|Gehäuse - intern]]
===== Raumklima - Messbox =====
* [[info:arduino:p42d1-raumklima|P-42 intern]]
* [[lehrer:arduino:p42d1-raumklima|L-P42 intern]]
{{:info:arduino:esp8266-wemos-d1-mini-pinout-gpio-pin-2.png?300|}}
^ Sensor ^ Pin am D1 mini ^ GPIO ^ Verwendung ^ Status ^
| DHT22 | D7 | 13 | | ✅ gut |
| MH-Z19B | Tx nach Rx D6 beim D1 OHNE Spannungsteiler!; Rx nach Tx D5 | 14 12 | UART | ✅ |
| BMP180 | D1 (SCL) + D2 (SDA) | 5 + 4 | I2C | ✅ Standard |
| LED | D4 | 2 | | ✅ |
| LDR | A0 | A0 | analogRead | ✅ Standard |
==== D1-mini ====
* Inzwischen wird der DHT22 mit BMP180 bei neueren Messboxen durch den BME280 ersetzt, was den Aufbau und den Code nochmalig vereinfacht.
* Der MHZ19B wird inzwischen **ohne Spannungsteiler** betrieben - Der D1-mini ist 5V tolerant!
* Bei einem ESP32 wäre es nicht ratsam, da dieser nicht 5V tolerant ist...
{{:www-messdatenerfassung-sas:d1-mini-raumklima-2026-01-08-dht22-bmp180.png?400 |Verkabelung der Klimamessbox, erstellt mit Fritzing}}
{{:www-messdatenerfassung-sas:uart-mhz-19b-rxd-d6-spannungsteiler-nicht-notwendig.png?400 |}}
==== Bestückung ====
=== Materialliste ===
* auf Vollständigkeit prüfen
{{:www-messdatenerfassung-sas:0-raumklima-platine-loeten-2026-06-13-01.png?400 |}}
=== 1. Drahtbrücke ===
* überstehende Reste entfernen
{{:www-messdatenerfassung-sas:1-raumklima-platine-loeten-2026-06-13-02.png?800 |}}
=== 2. Winkelstifte LED und BMP ===
* ACHTUNG: auf die genaue Position achten
{{:www-messdatenerfassung-sas:2-raumklima-platine-loeten-2026-06-13-03.png?800 |}}
=== 3. 3er Stifte DHT22 ===
* auf senkrechte Montage achten
{{:www-messdatenerfassung-sas:3-raumklima-platine-loeten-2026-06-13-04.png?800 |}}
=== 4. 8er Buchsen D1-mini ==
* auf senkrechte Montage achten
* Reste nach dem Löten entfernen
{{:www-messdatenerfassung-sas:4-raumklima-platine-loeten-2026-06-13-05.png?800 |}}
=== 5. 4er- und 5er-Buchse MHZ19B ===
* auf senkrechte Montage achten
* Reste nach dem Löten entfernen
{{:www-messdatenerfassung-sas:5-raumklima-platine-loeten-2026-06-13-06.png?800 |}}
=== 6. Widerstand 470kΩ ===
* Reste nach dem Löten entfernen
{{:www-messdatenerfassung-sas:6-raumklima-platine-loeten-2026-06-13-07.png?800 |}}
=== 7. 2er-Buchse LDR ===
* auf senkrechte Montage achten
* Reste nach dem Löten entfernen
{{:www-messdatenerfassung-sas:7-raumklima-platine-loeten-2026-06-13-08.png?800 |}}
=== 8. Externe Sensoren ===
{{:www-messdatenerfassung-sas:8-raumklima-platine-loeten-2026-06-13-09.png?800 |}}
=== 9. fertiges Modul ===
{{:info:platinen:raumklima-platine-loeten-2026-06-13-10.png?800|}}
====== Testcode - Raumklima ======
[[lehrer:arduino:p42d1-raumklima#testcode|Vollständiger TESTCODE]]
===== BMP180 =====
/********************************************************************************************
* BMP180 Test - Luftdruck und Temperatur
*
* Verdrahtung (I2C):
* - SCL: D1 (GPIO5)
* - SDA: D2 (GPIO4)
* - VCC: 3.3V
* - GND: GND
********************************************************************************************/
#include
#include
#define ALTITUDE 42.0 // Höhe über Meeresspiegel in Metern (anpassen!)
SFE_BMP180 pressure;
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n=== BMP180 Test ===");
// I2C initialisieren (Standard-Pins D1=SCL, D2=SDA)
Wire.begin();
if (pressure.begin()) {
Serial.println("BMP180 erfolgreich initialisiert!");
} else {
Serial.println("FEHLER: BMP180 nicht gefunden!");
Serial.println("Prüfe:");
Serial.println(" - I2C-Verbindung (D1=SCL, D2=SDA)");
Serial.println(" - 3.3V Versorgung");
Serial.println(" - GND-Verbindung");
while(1) delay(10);
}
Serial.println("\nStarte Messungen (alle 2 Sekunden)...\n");
}
void loop() {
char status;
double T, P, p0;
// 1. Temperatur messen
status = pressure.startTemperature();
if (status != 0) {
delay(status); // Warte auf Messung
status = pressure.getTemperature(T);
if (status != 0) {
Serial.print("Temperatur: ");
Serial.print(T, 2);
Serial.println(" °C");
// 2. Luftdruck messen (benötigt Temperatur)
status = pressure.startPressure(3); // Oversampling Level 3 (höchste Genauigkeit)
if (status != 0) {
delay(status);
status = pressure.getPressure(P, T);
if (status != 0) {
Serial.print("Stationsdruck: ");
Serial.print(P, 2);
Serial.println(" hPa");
// 3. Auf Meereshöhe korrigierter Druck
p0 = pressure.sealevel(P, ALTITUDE);
Serial.print("Meereshöhe (p0): ");
Serial.print(p0, 2);
Serial.println(" hPa");
}
}
}
} else {
Serial.println("FEHLER: Messung fehlgeschlagen!");
}
Serial.println("---");
delay(2000);
}
18:33:53.491 -> ---
18:33:55.487 -> Temperatur: 24.95 °C
18:33:55.518 -> Stationsdruck: 1003.11 hPa
18:33:55.518 -> Meereshöhe (p0): 1008.12 hPa
18:33:55.518 -> ---
===== DHT22 =====
/********************************************************************************************
* DHT22 Test - Luftfeuchtigkeit und Temperatur
*
* Verdrahtung:
* - DATA: D7 (GPIO13)
* - VCC: 3.3V oder 5V
* - GND: GND
* - Pull-up Widerstand (4.7k-10k) zwischen DATA und VCC (oft schon auf Modul vorhanden)
********************************************************************************************/
#include "DHT.h"
#define DHTPIN 13 // D7
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n=== DHT22 Test ===");
dht.begin();
Serial.println("DHT22 initialisiert");
Serial.println("Warte 2 Sekunden auf ersten Messwert...\n");
delay(2000); // DHT22 braucht kurze Aufwärmzeit
}
void loop() {
// Luftfeuchtigkeit und Temperatur auslesen
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
// Prüfen ob Messung erfolgreich
if (isnan(humidity) || isnan(temperature)) {
Serial.println("FEHLER: DHT22-Messung fehlgeschlagen!");
Serial.println("Prüfe:");
Serial.println(" - Datenpin (D7)");
Serial.println(" - Versorgungsspannung (3.3V oder 5V)");
Serial.println(" - Pull-up Widerstand vorhanden?");
} else {
Serial.print("Luftfeuchtigkeit: ");
Serial.print(humidity, 1);
Serial.println(" %");
Serial.print("Temperatur: ");
Serial.print(temperature, 1);
Serial.println(" °C");
// Taupunkt berechnen (optional)
float dewPoint = temperature - ((100 - humidity) / 5.0);
Serial.print("Taupunkt (ca.): ");
Serial.print(dewPoint, 1);
Serial.println(" °C");
}
Serial.println("---");
delay(2500); // DHT22 sollte max. alle 2 Sekunden ausgelesen werden
}
18:41:12.618 -> ---
18:41:15.127 -> Luftfeuchtigkeit: 43.9 %
18:41:15.127 -> Temperatur: 22.3 °C
18:41:15.127 -> Taupunkt (ca.): 11.1 °C
18:41:15.127 -> ---
===== MHZ19B =====
/********************************************************************************************
* MH-Z19B Test - CO₂-Sensor
*
* Verdrahtung (UART):
* - RX (Sensor) → TX (D5 / GPIO14) des D1 Mini
* - TX (Sensor) → RX (D6 / GPIO12) des D1 Mini
* - VCC: 5V (wichtig! Sensor braucht 5V)
* - GND: GND
*
* Hinweis: Sensor braucht ~3 Minuten Aufwärmzeit für stabile Werte!
********************************************************************************************/
#include
// Software Serial: RX=D6 (GPIO12), TX=D5 (GPIO14)
SoftwareSerial mhzSerial(12, 14);
// Kommando zum Auslesen der CO₂-Konzentration
byte cmdReadCO2[] = {0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79};
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n=== MH-Z19B CO₂-Sensor Test ===");
mhzSerial.begin(9600);
Serial.println("UART (9600 baud) initialisiert");
Serial.println("\nHinweis: Sensor braucht ~3 Min. Aufwärmzeit!");
Serial.println("Starte Messungen alle 5 Sekunden...\n");
delay(2000);
}
void loop() {
int ppm = -1;
int temp = -999;
if (readMHZ19B(ppm, temp)) {
Serial.print("CO₂: ");
Serial.print(ppm);
Serial.print(" ppm");
// Bewertung
if (ppm < 700) {
Serial.print(" [Sehr gut - Außenluftniveau]");
} else if (ppm < 1000) {
Serial.print(" [Gut - Innenraumluft]");
} else if (ppm < 1500) {
Serial.print(" [Mäßig - Lüften empfohlen]");
} else if (ppm < 2000) {
Serial.print(" [Schlecht - Dringend lüften!]");
} else {
Serial.print(" [Sehr schlecht - SOFORT lüften!]");
}
Serial.println();
Serial.print("Sensor-Temperatur: ");
Serial.print(temp);
Serial.println(" °C");
} else {
Serial.println("FEHLER: Keine gültigen Daten vom Sensor!");
Serial.println("Prüfe:");
Serial.println(" - RX/TX vertauscht? (RX Sensor → TX D1, TX Sensor → RX D1)");
Serial.println(" - 5V Versorgung vorhanden?");
Serial.println(" - Sensor aufgewärmt? (3 Min. warten)");
}
Serial.println("---");
delay(5000); // Alle 5 Sekunden (Sensor kann häufiger, aber nicht nötig)
}
// MH-Z19B auslesen mit Checksummen-Prüfung
bool readMHZ19B(int &ppm, int &tempC) {
// Puffer leeren (alte Daten verwerfen)
while (mhzSerial.available()) {
mhzSerial.read();
}
// Kommando senden
mhzSerial.write(cmdReadCO2, 9);
// Antwort lesen (9 Bytes, max. 200ms warten)
byte response[9];
int bytesRead = 0;
unsigned long startTime = millis();
while (bytesRead < 9 && millis() - startTime < 200) {
if (mhzSerial.available()) {
response[bytesRead++] = mhzSerial.read();
}
delay(1);
}
// Prüfen ob vollständiges Paket empfangen
if (bytesRead != 9) {
Serial.print("Nur ");
Serial.print(bytesRead);
Serial.println(" von 9 Bytes empfangen");
return false;
}
// Header prüfen (sollte 0xFF 0x86 sein)
if (response[0] != 0xFF || response[1] != 0x86) {
Serial.print("Falscher Header: 0x");
Serial.print(response[0], HEX);
Serial.print(" 0x");
Serial.println(response[1], HEX);
return false;
}
// Checksumme berechnen und prüfen
byte checksum = 0;
for (int i = 1; i < 8; i++) {
checksum += response[i];
}
checksum = 0xFF - checksum + 1;
if (checksum != response[8]) {
Serial.print("Checksumme falsch! Erwartet: 0x");
Serial.print(checksum, HEX);
Serial.print(" Erhalten: 0x");
Serial.println(response[8], HEX);
return false;
}
// Werte extrahieren
ppm = response[2] * 256 + response[3];
tempC = response[4] - 40; // Temperatur ist offset um 40°C
// Plausibilitätsprüfung
if (ppm < 0 || ppm > 5000) {
Serial.print("Unplausibel: ppm=");
Serial.println(ppm);
return false;
}
return true;
}
18:44:51.629 -> ---
18:44:56.637 -> CO₂: 1349 ppm [Mäßig - Lüften empfohlen]
18:44:56.637 -> Sensor-Temperatur: 18 °C
18:44:56.637 -> ---
==== Kalibrierung ====
Falls der MHZ-19B falsche Werte anzeigt hilft vielleicht eine Kalibrierung an der frischen Luft ;-)
⚡ Start → ABC wird sofort ausgeschaltet
⏱️ 20 Minuten → Sensor stabilisiert sich in Frischluft (zeigt Countdown)
🎯 Kalibrierung → Zero Point Calibration auf Frischluft
📊 Messbetrieb → Kontinuierliche Messung alle 10 Sekunden
Vorgehensweise:
* Code auf den D1 mini hochladen
* Sofort mit Powerbank vor die Tür legen (wichtig: nicht erst drinnen testen!)
* 20 Minuten warten (Sie sehen den Countdown am seriellen Monitor)
* Nach Kalibrierung können Sie das Gerät wieder reinholen
* Fertig - der Sensor zeigt jetzt korrekte Werte!
/********************************************************************************************
* MH-Z19B CO2-Sensor KALIBRIERUNGS-PROGRAMM
* für D1 mini (ESP8266)
*
* ABLAUF:
* =======
* 1. Start: ABC (Automatic Baseline Correction) wird ausgeschaltet
* 2. Warten: 20 Minuten Stabilisierungszeit in Frischluft
* 3. Kalibrierung: Zero Point Calibration auf Frischluft (~420 ppm)
* 4. Messbetrieb: Kontinuierliche Messung alle 10 Sekunden
*
* VERWENDUNG:
* ===========
* - Sensor mit Powerbank versorgen
* - SOFORT nach dem Start vor die Tür legen (Frischluft!)
* - 20 Minuten warten
* - Nach Kalibrierung kann Sensor wieder ins Haus
*
* VERDRAHTUNG MH-Z19B:
* ====================
* MH-Z19B → D1 mini
* Vin → 5V (oder VIN bei Powerbank)
* GND → GND
* TX → D6 (GPIO12) = RX vom D1 mini
* RX → D5 (GPIO14) = TX vom D1 mini
*
* Erstellt: 2025-01-08
********************************************************************************************/
#include
/********************************************************************************************
* KONFIGURATION
********************************************************************************************/
// Serielle Schnittstelle für MH-Z19B (Software Serial)
#define MHZ_RX_PIN D6 // D1 mini D6 → MH-Z19B TX
#define MHZ_TX_PIN D5 // D1 mini D5 → MH-Z19B RX
// Zeitsteuerung
const unsigned long WARTEZEIT_MS = 20UL * 60UL * 1000UL; // 20 Minuten in Millisekunden
const unsigned long MESS_INTERVALL = 10000; // 10 Sekunden Messintervall
/********************************************************************************************
* GLOBALE VARIABLEN
********************************************************************************************/
SoftwareSerial mhzSerial(MHZ_RX_PIN, MHZ_TX_PIN);
// Status-Flags
bool abcDeaktiviert = false;
bool kalibriert = false;
unsigned long startzeit = 0;
/********************************************************************************************
* MH-Z19B BEFEHLE
********************************************************************************************/
// Befehl: CO2-Wert lesen
byte cmdReadCO2[9] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
// Befehl: ABC ausschalten
byte cmdABCoff[9] = {0xFF, 0x01, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86};
// Befehl: Zero Point Calibration (Frischluft-Kalibrierung)
byte cmdCalibrate[9] = {0xFF, 0x01, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78};
/********************************************************************************************
* SETUP
********************************************************************************************/
void setup() {
// Serielle Schnittstelle zum PC starten
Serial.begin(115200);
delay(200);
Serial.println();
Serial.println("============================================================");
Serial.println(" MH-Z19B KALIBRIERUNGS-PROGRAMM");
Serial.println("============================================================");
Serial.println();
Serial.println("⚠️ WICHTIG: Sensor JETZT sofort in Frischluft bringen!");
Serial.println(" (Fenster öffnen oder vor die Tür legen)");
Serial.println();
// Serielle Schnittstelle zum MH-Z19B starten (9600 Baud)
mhzSerial.begin(9600);
delay(500);
// SCHRITT 1: ABC ausschalten
Serial.println("SCHRITT 1: ABC (Automatic Baseline Correction) ausschalten...");
mhzSerial.write(cmdABCoff, 9);
delay(1000);
abcDeaktiviert = true;
Serial.println("✓ ABC deaktiviert");
Serial.println();
// Startzeit merken
startzeit = millis();
// Berechne Kalibrierungszeitpunkt
unsigned long minuten = WARTEZEIT_MS / 60000;
Serial.println("============================================================");
Serial.println("SCHRITT 2: Wartezeit für Stabilisierung");
Serial.println("============================================================");
Serial.printf("Kalibrierung erfolgt in %lu Minuten\n", minuten);
Serial.println();
Serial.println("Der Sensor stabilisiert sich jetzt in der Frischluft.");
Serial.println("Sie können den Fortschritt am seriellen Monitor beobachten.");
Serial.println();
Serial.println("------------------------------------------------------------");
Serial.println(" MESSWERTE WÄHREND STABILISIERUNG:");
Serial.println("------------------------------------------------------------");
Serial.println();
}
/********************************************************************************************
* LOOP
********************************************************************************************/
void loop() {
unsigned long jetzt = millis();
unsigned long vergangen = jetzt - startzeit;
// CO2-Wert auslesen
int co2 = leseCO2();
// ========== VOR DER KALIBRIERUNG ==========
if (!kalibriert && vergangen < WARTEZEIT_MS) {
// Zeit bis zur Kalibrierung berechnen
unsigned long verbleibend = WARTEZEIT_MS - vergangen;
unsigned long minutenVerbleibend = verbleibend / 60000;
unsigned long sekundenVerbleibend = (verbleibend % 60000) / 1000;
// Fortschritt anzeigen
Serial.print("⏱️ ");
Serial.printf("%02lu:%02lu", minutenVerbleibend, sekundenVerbleibend);
Serial.print(" | CO2: ");
if (co2 > 0) {
Serial.print(co2);
Serial.println(" ppm (Stabilisierung läuft...)");
} else {
Serial.println("--- (Lesefehler)");
}
// Warte 10 Sekunden bis zur nächsten Messung
delay(MESS_INTERVALL);
}
// ========== KALIBRIERUNG DURCHFÜHREN ==========
else if (!kalibriert && vergangen >= WARTEZEIT_MS) {
Serial.println();
Serial.println("============================================================");
Serial.println("SCHRITT 3: KALIBRIERUNG AUF FRISCHLUFT");
Serial.println("============================================================");
Serial.println();
// Aktuellen Wert vor Kalibrierung anzeigen
Serial.print("CO2-Wert vor Kalibrierung: ");
if (co2 > 0) {
Serial.print(co2);
Serial.println(" ppm");
} else {
Serial.println("nicht verfügbar");
}
Serial.println();
// Kalibrierungs-Befehl senden
Serial.println("Sende Zero Point Calibration Befehl...");
mhzSerial.write(cmdCalibrate, 9);
delay(2000); // Warten auf Verarbeitung
Serial.println("✓ Kalibrierung abgeschlossen!");
Serial.println();
Serial.println("Der Sensor verwendet jetzt den aktuellen Wert als");
Serial.println("Frischluft-Referenz (~420 ppm).");
Serial.println();
kalibriert = true;
Serial.println("============================================================");
Serial.println("SCHRITT 4: NORMALER MESSBETRIEB");
Serial.println("============================================================");
Serial.println();
Serial.println("✓ Sensor ist kalibriert und einsatzbereit!");
Serial.println();
Serial.println("Sie können den Sensor jetzt wieder ins Haus bringen.");
Serial.println("Die Messwerte werden jetzt alle 10 Sekunden angezeigt.");
Serial.println();
Serial.println("------------------------------------------------------------");
Serial.println(" MESSWERTE:");
Serial.println("------------------------------------------------------------");
Serial.println();
}
// ========== NORMALER MESSBETRIEB ==========
else if (kalibriert) {
// Betriebszeit berechnen
unsigned long betriebszeit = (jetzt - startzeit) / 1000; // in Sekunden
unsigned long stunden = betriebszeit / 3600;
unsigned long minuten = (betriebszeit % 3600) / 60;
unsigned long sekunden = betriebszeit % 60;
// Messwert anzeigen
Serial.printf("%02lu:%02lu:%02lu | ", stunden, minuten, sekunden);
Serial.print("CO2: ");
if (co2 > 0) {
Serial.print(co2);
Serial.print(" ppm");
// Bewertung hinzufügen
if (co2 < 600) {
Serial.println(" ✓ Sehr gut (Frischluft)");
} else if (co2 < 1000) {
Serial.println(" ✓ Gut (normale Raumluft)");
} else if (co2 < 1500) {
Serial.println(" ⚠️ Mäßig (Lüften empfohlen)");
} else {
Serial.println(" ⚠️ Schlecht (Dringend lüften!)");
}
} else {
Serial.println("--- (Lesefehler)");
}
delay(MESS_INTERVALL);
}
}
/********************************************************************************************
* HILFSFUNKTIONEN
********************************************************************************************/
/**
* Liest den CO2-Wert vom MH-Z19B Sensor
* @return CO2-Wert in ppm (0 bei Fehler)
*/
int leseCO2() {
// Puffer leeren
while (mhzSerial.available()) {
mhzSerial.read();
}
// Read-Befehl senden
mhzSerial.write(cmdReadCO2, 9);
// Auf Antwort warten (max. 1 Sekunde)
unsigned long timeout = millis() + 1000;
while (mhzSerial.available() < 9 && millis() < timeout) {
delay(10);
}
// Prüfen, ob genug Daten empfangen wurden
if (mhzSerial.available() < 9) {
return 0; // Timeout
}
// 9 Bytes lesen
byte response[9];
for (int i = 0; i < 9; i++) {
response[i] = mhzSerial.read();
}
// Prüfen, ob Antwort gültig ist
if (response[0] != 0xFF || response[1] != 0x86) {
return 0; // Ungültige Antwort
}
// Checksum überprüfen (optional, für mehr Zuverlässigkeit)
byte checksum = 0;
for (int i = 1; i < 8; i++) {
checksum += response[i];
}
checksum = 0xFF - checksum + 1;
if (checksum != response[8]) {
return 0; // Checksum-Fehler
}
// CO2-Wert aus Bytes 2 und 3 extrahieren
int co2 = (response[2] << 8) | response[3];
return co2;
}
/********************************************************************************************
* ZUSÄTZLICHE INFORMATIONEN
********************************************************************************************
*
* MH-Z19B KALIBRIERUNG - HINTERGRUND:
* ====================================
*
* Warum kalibrieren?
* ------------------
* - CO2-Sensoren können im Laufe der Zeit "driften"
* - Referenzwert verschiebt sich → falsche Messwerte
* - Kalibrierung stellt bekannten Referenzpunkt wieder her
*
*
* Was ist ABC (Automatic Baseline Correction)?
* --------------------------------------------
* - Sensor nimmt an: niedrigster CO2-Wert in 24h = Frischluft
* - Passt Referenz automatisch an
* - Problem: In geschlossenen Räumen ohne Lüftung funktioniert das nicht
* - Kann zu falschen Kalibrierungen führen (z.B. 800 ppm als "Frischluft")
* - Deshalb: ABC für manuelle Kalibrierung ausschalten!
*
*
* Zero Point Calibration:
* -----------------------
* - Manuelle Kalibrierung auf bekannten Referenzwert
* - Frischluft hat ca. 420 ppm CO2 (weltweiter Durchschnitt)
* - Sensor speichert diesen Wert als neue Referenz
* - Wichtig: Muss wirklich in Frischluft durchgeführt werden!
*
*
* Wartezeit 20 Minuten:
* ---------------------
* - Sensor braucht Zeit zur Stabilisierung
* - Temperatur muss sich angleichen
* - Interne Elektronik muss sich einpendeln
* - Erste Messwerte sind oft ungenau
*
*
* Typische CO2-Werte:
* -------------------
* 400-420 ppm: Frischluft (Außenluft)
* 400-600 ppm: Sehr gute Raumluft
* 600-1000 ppm: Normale Raumluft
* 1000-1500 ppm: Mäßig, Lüften empfohlen
* > 1500 ppm: Schlecht, dringend lüften!
* > 2000 ppm: Gesundheitlich bedenklich
*
*
* Fehlersuche:
* ------------
* - "Lesefehler": Verkabelung prüfen (TX/RX vertauscht?)
* - Wert ändert sich nicht: ABC ist noch aktiv oder Sensor defekt
* - Zu niedrige Werte: In Innenraum kalibriert statt in Frischluft
* - Zu hohe Werte: Sensor muss neu kalibriert werden
*
********************************************************************************************/
===== LDR =====
/********************************************************************************************
* LDR Test - Lichtsensor (Light Dependent Resistor)
*
* Verdrahtung:
* - Analog Pin: A0
* - LDR in Spannungsteiler-Schaltung:
* - 3.3V → LDR → A0 → Widerstand (z.B. 10k) → GND
* oder
* - 3.3V → Widerstand → A0 → LDR → GND
*
* Hinweis: Im Hauptcode ist invertiert (hell=klein, dunkel=groß)
********************************************************************************************/
#define LDR_PIN A0
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n=== LDR Helligkeitssensor Test ===");
Serial.println("\nZeigt Rohwert (0-1023) und invertiert wie im Hauptcode\n");
pinMode(LDR_PIN, INPUT);
delay(1000);
}
void loop() {
// Rohwert auslesen (0-1023)
int rawValue = analogRead(LDR_PIN);
// Invertiert wie im Hauptcode (450 - raw)
// → hell wird zu kleinem Wert, dunkel zu großem Wert
int inverted = 450 - rawValue;
// Bargraph für Rohwert erstellen
Serial.print("Rohwert: ");
printBar(rawValue, 1023);
Serial.print(" ");
Serial.print(rawValue);
Serial.print(" / 1023");
// Interpretation
if (rawValue > 800) {
Serial.print(" [SEHR HELL]");
} else if (rawValue > 500) {
Serial.print(" [Hell]");
} else if (rawValue > 200) {
Serial.print(" [Mittel]");
} else if (rawValue > 50) {
Serial.print(" [Dunkel]");
} else {
Serial.print(" [SEHR DUNKEL]");
}
Serial.println();
// Invertierter Wert (wie er an EmonCMS gesendet wird)
Serial.print("Invertiert (für EmonCMS): ");
Serial.println(inverted);
Serial.println("---");
delay(500); // Schnelles Update zum Testen
}
// Hilfsfunktion: Bargraph im Serial Monitor
void printBar(int value, int maxValue) {
int barLength = map(value, 0, maxValue, 0, 40);
Serial.print("[");
for (int i = 0; i < 40; i++) {
if (i < barLength) {
Serial.print("=");
} else {
Serial.print(" ");
}
}
Serial.print("]");
}
---
18:47:22.468 -> Rohwert: [=== ] 69 / 1023 [Dunkel]
18:47:22.501 -> Invertiert (für EmonCMS): 381
18:47:22.501 -> ---
===== LED-Band =====
===== LED-Band =====
/********************************************************************************************
* LED-Band Test - NeoPixel / WS2812B LED-Streifen (CO₂-Ampel)
*
* Verdrahtung:
* - DATA: D4 (GPIO2)
* - VCC: 5V (für volle Helligkeit, 3.3V funktioniert auch mit reduzierter Leistung)
* - GND: GND
*
* Hinweis: 5 LEDs, wie im Hauptcode definiert
********************************************************************************************/
#include
#define LED_PIN 2 // D4
#define NUM_LEDS 5
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n=== LED-Band NeoPixel Test ===");
Serial.println("5 LEDs an D4 (GPIO2)\n");
strip.begin();
strip.show(); // Alle LEDs aus
Serial.println("Test-Sequenz startet...\n");
}
void loop() {
// 1. Einzelne LEDs durchgehen (Rot)
Serial.println("Test 1: Einzelne LEDs (Rot)");
strip.clear();
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(255, 0, 0));
strip.show();
delay(300);
}
delay(1000);
// 2. Alle LEDs in verschiedenen Farben
Serial.println("Test 2: Alle LEDs - verschiedene Farben");
Serial.println(" - Rot");
strip.fill(strip.Color(255, 0, 0));
strip.show();
delay(1000);
Serial.println(" - Grün");
strip.fill(strip.Color(0, 255, 0));
strip.show();
delay(1000);
Serial.println(" - Blau");
strip.fill(strip.Color(0, 0, 255));
strip.show();
delay(1000);
Serial.println(" - Gelb");
strip.fill(strip.Color(255, 255, 0));
strip.show();
delay(1000);
Serial.println(" - Weiß");
strip.fill(strip.Color(255, 255, 255));
strip.show();
delay(1000);
// 3. CO₂-Ampel Simulation (wie im Hauptcode)
Serial.println("\nTest 3: CO₂-Ampel Simulation");
Serial.println(" - 600 ppm: BLAU (sehr gut)");
setCO2Color(600);
delay(2000);
Serial.println(" - 900 ppm: GRÜN (gut)");
setCO2Color(900);
delay(2000);
Serial.println(" - 1400 ppm: GELB (mittel)");
setCO2Color(1400);
delay(2000);
Serial.println(" - 2000 ppm: ROT (schlecht)");
setCO2Color(2000);
delay(2000);
Serial.println(" - 2600 ppm: ALARM (blinken)");
setCO2Color(2600);
delay(3000);
// 4. Helligkeitstest
Serial.println("\nTest 4: Helligkeit (Rot)");
for (int brightness = 0; brightness <= 255; brightness += 25) {
Serial.print(" Helligkeit: ");
Serial.println(brightness);
strip.setBrightness(brightness);
strip.fill(strip.Color(255, 0, 0));
strip.show();
delay(500);
}
// 5. Regenbogen-Effekt
Serial.println("\nTest 5: Regenbogen");
rainbowCycle(5);
Serial.println("\n=== Sequenz beendet, wiederhole... ===\n");
delay(2000);
}
// CO₂-Ampel Funktion (aus Hauptcode übernommen)
void setCO2Color(int co2) {
if (co2 < 700) {
strip.setBrightness(2);
strip.fill(strip.Color(0, 0, 255)); // Blau
} else if (co2 < 1100) {
strip.setBrightness(3);
strip.fill(strip.Color(0, 255, 0)); // Grün
} else if (co2 < 1800) {
strip.setBrightness(4);
strip.fill(strip.Color(255, 255, 0)); // Gelb
} else if (co2 < 2500) {
strip.setBrightness(120);
strip.fill(strip.Color(255, 0, 0)); // Rot
} else {
// Alarm: Rot-Weiß blitzen
for (int i = 0; i < 5; i++) {
strip.setBrightness(255);
strip.fill(strip.Color(255, 0, 0));
strip.show();
delay(200);
strip.fill(strip.Color(255, 255, 255));
strip.show();
delay(200);
strip.clear();
strip.show();
delay(200);
}
strip.setBrightness(120);
strip.fill(strip.Color(255, 0, 0));
strip.show();
return;
}
strip.show();
}
// Regenbogen-Effekt
void rainbowCycle(int cycles) {
for (int j = 0; j < 256 * cycles; j++) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, Wheel((i * 256 / NUM_LEDS + j) & 255));
}
strip.show();
delay(5);
}
}
// Hilfsfunktion für Regenbogen (Farbrad)
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}