====== WWW: Platinen mit TESTCODE======
{{:www-fuer-alle:raumklima-105-box-geschlossen.png?200|}}{{:www-fuer-alle:klimabox-aussen-2025-05.png?200|}}
* [[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);
}
====== Copyright (C) 2025 Biebl/Sankt-Ansgar-Schule Hamburg ======
"Klimamessstation für Schulen" von Martin Biebl/Sankt-Ansgar-Schule Hamburg,
lizenziert unter CC BY-SA 4.0
(https://creativecommons.org/licenses/by-sa/4.0/)
Diese Programme sind freie Software: Sie können es unter den
Bedingungen der GNU General Public License, wie von der Free
Software Foundation veröffentlicht, weitergeben und/oder
modifizieren, entweder gemäß Version 3 der Lizenz oder (nach
Ihrer Wahl) jeder späteren Version.
Die Veröffentlichung dieser Programme erfolgt in der Hoffnung,
dass es Ihnen von Nutzen sein wird, aber OHNE JEDE GEWÄHRLEISTUNG,
sogar ohne die implizite Gewährleistung der MARKTFÄHIGKEIT oder
EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Details finden Sie in der
GNU General Public License.
Vollständiger Lizenztext: https://www.gnu.org/licenses/gpl-3.0.de.html