Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
WWW: Platinen mit TESTCODE
| Seite | Inhalt | |
|---|---|---|
| Startseite | Übersicht | |
| Hauptanleitung | Gesamtüberblick, Begriffsklärung, Teil 1–3 (FRITZ!Box, Raspberry Pi/EmonCMS, Sensor-Boxen), Ein-/Ausschalten, Zugänge-Checkliste, SENSOR_ID-Übersicht | |
| Sensoren in EmonCMS einbinden | Wiederkehrender Ablauf für jede neue Sensor-Box: API-Key, Inputs, Feeds, Dashboard | |
| Programmcode der Sensor-Boxen | Vollständiger Arduino-Code: Testsensor, einfacher BME280-Sensor, Klimamessbox, Raummessbox | |
| Raspberry Pi um Echtzeituhr (RTC) erweitern – HW-111 | Nachrüstung eines DS1307-RTC-Moduls, Shutdown-Taster | |
| Schaltung | Platine | |
| Gehäuse | 3D-Druck oder LASER-Cutter | |
| Schule | Einsatz im Unterricht | |
| Sonstiges | dies und das…. |
-
- Nach Erstellung benötigt die günstigste „Verschiffung“ 2 Wochen (kostet aber unter 5 EURO)
- Bei unseren Platinen kostete eine Platine 1,5 Euro
Raumklima - Messbox
| 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 durch den BME280 ersetzt, was den Aufbau und den Code nochmalig vereinfacht.
ohne Spannungsteiler - er ist 5V tolerant
Bestückung
Materialliste
1. Drahtbrücke
2. Winkelstifte LED und BMP
3. 3er Stifte DHT22
4. 8er Buchsen D1-mini
5. 4er- und 5er-Buchse MHZ19B
6. Widerstand 470kΩ
7. 2er-Buchse LDR
8. Externe Sensoren
9. fertiges Modul
Testcode - Raumklima
BMP180
/********************************************************************************************
* BMP180 Test - Luftdruck und Temperatur
*
* Verdrahtung (I2C):
* - SCL: D1 (GPIO5)
* - SDA: D2 (GPIO4)
* - VCC: 3.3V
* - GND: GND
********************************************************************************************/
#include <Wire.h>
#include <SFE_BMP180.h>
#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 <SoftwareSerial.h>
// 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 <SoftwareSerial.h>
/********************************************************************************************
* 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 <Adafruit_NeoPixel.h>
#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);
}
Klimamessbox
| Sensor | Pin (GPIO) | Anschluss | Besonderheit |
|---|---|---|---|
| MH-Z19B | GPIO12/14 | D6 / D5 | Spannungsteiler für D6; SoftwareSerial |
| SDS011 | GPIO13/15 | D7 / D8 | Spannungsteiler für D7 darf NICHT eingebaut werden, der SDS sendet bereits mit 3,3V Logik; SoftwareSerial |
| BMP180 | GPIO4/5 | D2 / D1 | I²C (fest: SDA=SDA, SCL=SCL) |
| LDR | ADC0 | A0 | analog |
| DHT22 | GPIO3 | RX | Nur nach Upload einstecken! |
Di-mini-Platine
Sensorplatine
TESTCODE KLIMABOX BME, MHZ und SDS
BME280
#include <Wire.h>
#include <Adafruit_BME280.h>
#define BME280_ADDRESS 0x76 // ggf. 0x77 testen
Adafruit_BME280 bme;
void setup() {
Serial.begin(115200);
delay(200);
Serial.println("\nBME280 Test");
Wire.begin(4, 5); // SDA = GPIO4, SCL = GPIO5
if (!bme.begin(BME280_ADDRESS)) {
Serial.println("BME280 nicht gefunden, Adresse oder Verkabelung prüfen!");
while (1) delay(100);
}
Serial.println("BME280 gefunden.");
}
void loop() {
float t = bme.readTemperature();
float h = bme.readHumidity();
float p = bme.readPressure() / 100.0; // hPa
Serial.print("Temp: "); Serial.print(t); Serial.println(" °C");
Serial.print("Feuchte: "); Serial.print(h); Serial.println(" %");
Serial.print("Druck: "); Serial.print(p); Serial.println(" hPa");
Serial.println("----");
delay(2000);
}
18:11:23.194 -> ---- 18:11:25.191 -> Temp: 22.28 °C 18:11:25.191 -> Feuchte: 35.97 % 18:11:25.191 -> Druck: 1008.70 hPa 18:11:25.191 -> ----
MHZ-19B
#include <SoftwareSerial.h>
SoftwareSerial mhzSerial(12, 14); // RX = GPIO12, TX = GPIO14
byte cmdReadCO2[] = {0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79};
bool readMHZ19B(int &ppm, int &tempC) {
while (mhzSerial.available()) mhzSerial.read();
mhzSerial.write(cmdReadCO2, 9);
uint8_t resp[9];
int i = 0;
uint32_t start = millis();
while (millis() - start < 200) {
if (mhzSerial.available()) {
resp[i++] = mhzSerial.read();
if (i == 9) break;
}
}
if (i != 9) return false;
if (resp[0] != 0xFF || resp[1] != 0x86) return false;
uint8_t sum = 0;
for (int k = 1; k < 8; k++) sum += resp[k];
sum = 0xFF - sum + 1;
if (sum != resp[8]) return false;
ppm = resp[2] * 256 + resp[3];
tempC = resp[4] - 40;
return true;
}
void setup() {
Serial.begin(115200);
delay(200);
Serial.println("\nMH-Z19B Test");
mhzSerial.begin(9600);
Serial.println("Sensor heizt auf (ca. 3 Minuten)...");
}
void loop() {
int ppm, t;
if (readMHZ19B(ppm, t)) {
Serial.print("CO2: ");
Serial.print(ppm);
Serial.print(" ppm, T-Sensor: ");
Serial.print(t);
Serial.println(" °C");
} else {
Serial.println("Fehler beim Lesen (noch am Aufwärmen?)");
}
delay(2000);
}
18:10:07.806 -> CO2: 674 ppm, T-Sensor: 24 °C 18:10:09.844 -> CO2: 675 ppm, T-Sensor: 24 °C
SDS011
#include <SoftwareSerial.h>
SoftwareSerial sdsSerial(13, 15); // RX = GPIO13, TX = GPIO15
void setup() {
Serial.begin(115200);
delay(200);
Serial.println("\nSDS011 Test");
sdsSerial.begin(9600);
}
bool readSDS011(float &pm25, float &pm10) {
uint8_t buf[10];
int idx = 0;
uint32_t start = millis();
while (millis() - start < 2000) {
if (sdsSerial.available()) {
uint8_t b = sdsSerial.read();
if (idx == 0 && b != 0xAA) continue;
buf[idx++] = b;
if (idx == 10) {
if (buf[0] == 0xAA && buf[1] == 0xC0 && buf[9] == 0xAB) {
pm25 = (buf[2] + 256 * buf[3]) / 10.0;
pm10 = (buf[4] + 256 * buf[5]) / 10.0;
return true;
} else {
idx = 0;
}
}
}
}
return false;
}
void loop() {
float pm25, pm10;
if (readSDS011(pm25, pm10)) {
Serial.print("PM2.5: ");
Serial.print(pm25);
Serial.print(" µg/m³, PM10: ");
Serial.print(pm10);
Serial.println(" µg/m³");
} else {
Serial.println("Kein gültiges SDS011 Paket empfangen.");
}
delay(2000);
}
18:29:09.513 -> PM2.5: 4.60 µg/m³, PM10: 8.10 µg/m³ 18:29:11.513 -> PM2.5: 4.60 µg/m³, PM10: 8.00 µg/m³
Alle drei Sensoren
#include <Wire.h>
#include <Adafruit_BME280.h>
#include <SoftwareSerial.h>
// BME280 an I2C: SDA = GPIO4 (D2), SCL = GPIO5 (D1)
#define BME280_ADDRESS 0x76
Adafruit_BME280 bme;
// MH-Z19B an GPIO12/14
SoftwareSerial mhzSerial(12, 14); // RX = 12, TX = 14
byte cmdReadCO2[] = {0xFF, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79};
// SDS011 an GPIO13/15
SoftwareSerial sdsSerial(13, 15); // RX = 13, TX = 15
byte sds011_sleep[] = {0xAA, 0xB4, 0x06, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x05, 0xAB};
byte sds011_wakeup[] = {0xAA, 0xB4, 0x06, 0x01, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0x06, 0xAB};
// ---- Hilfsfunktionen ----
bool readMHZ19B(int &ppm, int &tempC) {
while (mhzSerial.available()) mhzSerial.read();
mhzSerial.write(cmdReadCO2, 9);
uint8_t resp[9];
int i = 0;
uint32_t start = millis();
while (millis() - start < 200) {
if (mhzSerial.available()) {
resp[i++] = mhzSerial.read();
if (i == 9) break;
}
}
if (i != 9) return false;
if (resp[0] != 0xFF || resp[1] != 0x86) return false;
uint8_t sum = 0;
for (int k = 1; k < 8; k++) sum += resp[k];
sum = 0xFF - sum + 1;
if (sum != resp[8]) return false;
ppm = resp[2] * 256 + resp[3];
tempC = resp[4] - 40;
return true;
}
bool readSDS011(float &pm25, float &pm10) {
uint8_t buf[10];
int idx = 0;
uint32_t start = millis();
while (millis() - start < 2000) {
if (sdsSerial.available()) {
uint8_t b = sdsSerial.read();
if (idx == 0 && b != 0xAA) continue;
buf[idx++] = b;
if (idx == 10) {
if (buf[0] == 0xAA && buf[1] == 0xC0 && buf[9] == 0xAB) {
pm25 = (buf[2] + 256 * buf[3]) / 10.0;
pm10 = (buf[4] + 256 * buf[5]) / 10.0;
return true;
} else {
idx = 0;
}
}
}
}
return false;
}
void setSDS011Sleep(bool sleep) {
if (sleep) {
sdsSerial.write(sds011_sleep, sizeof(sds011_sleep));
} else {
sdsSerial.write(sds011_wakeup, sizeof(sds011_wakeup));
}
delay(100);
}
// ---- Setup & Loop ----
void setup() {
Serial.begin(115200);
delay(200);
Serial.println("\nGesamttest: BME280 + MH-Z19B + SDS011");
// BME280
Wire.begin(4, 5); // SDA, SCL
if (!bme.begin(BME280_ADDRESS)) {
Serial.println("BME280 nicht gefunden!");
while (1) delay(100);
}
Serial.println("BME280 OK.");
// MH-Z19B
mhzSerial.begin(9600);
Serial.println("MH-Z19B UART gestartet (9600 Baud).");
// SDS011
sdsSerial.begin(9600);
setSDS011Sleep(false);
Serial.println("SDS011 aufgeweckt, Lüfter sollte laufen.");
}
void loop() {
Serial.println("\n--- neuer Messzyklus ---");
// BME280
bme.takeForcedMeasurement();
float t = bme.readTemperature();
float h = bme.readHumidity();
float p = bme.readPressure() / 100.0;
Serial.print("BME280: ");
Serial.print(t); Serial.print(" °C, ");
Serial.print(h); Serial.print(" %, ");
Serial.print(p); Serial.println(" hPa");
// MH-Z19B
int ppm, tco2;
if (readMHZ19B(ppm, tco2)) {
Serial.print("MH-Z19B: ");
Serial.print(ppm); Serial.print(" ppm, ");
Serial.print(tco2); Serial.println(" °C (Sensorsensor)");
} else {
Serial.println("MH-Z19B: kein gültiges Paket.");
}
// SDS011
float pm25, pm10;
if (readSDS011(pm25, pm10)) {
Serial.print("SDS011: PM2.5=");
Serial.print(pm25);
Serial.print(" µg/m³, PM10=");
Serial.print(pm10);
Serial.println(" µg/m³");
} else {
Serial.println("SDS011: kein gültiges Paket.");
}
delay(10000);
}
18:33:44.196 -> --- neuer Messzyklus --- 18:33:44.196 -> BME280: 21.54 °C, 37.46 %, 1008.72 hPa 18:33:44.259 -> MH-Z19B: 673 ppm, 22 °C (Sensorsensor) 18:33:44.259 -> SDS011: PM2.5=4.20 µg/m³, PM10=7.20 µg/m³




























