====== 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); }