===== Klimamessbox - Außenbereich mit Feinstaubmessung===== * NOCH nicht aktualisiert - Nur noch BME280, kein DHT22 mehr... * Die Bilder liegen noch nicht im WWW-Bereich und fehlen somit bei fehlender Anmeldung {{:info:arduino:esp8266-wemos-d1-mini-pinout-gpio-pin-2.png?300|}}{{:info:platinen:sds011-dust-sensor-pinout-diagram.jpg?300|}} ^ 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 ==== ACHTUNG: Für den SDS011 KEINEN Spannungsteiler verwenden, er sendet bereits mit 3,3 V Logik {{:info:platinen:platine-klimamessbox-d1-mini.png?300|}} {{:info:platinen:platine-klimamessbox-d1-mini-2d-unten.png?200|}} {{:info:platinen:platine-klimamessbox-d1-mini-2d-oben.png?200|}} {{:info:platinen:platine-klimamessbox-d1-mini-3d-unten.png?200|}} {{:info:platinen:platine-klimamessbox-d1-mini-3d-oben.png?200|}} ==== Sensorplatine ==== {{:info:platinen:platine-klimamessbox-sensoren.png?300|}} {{:info:platinen:platine-klimamessbox-sensoren-2d-unten.png?200|}} {{:info:platinen:platine-klimamessbox-sensoren-2d-oben.png?200|}} {{:info:platinen:platine-klimamessbox-sensoren-3d-unten.png?200|}} {{:info:platinen:platine-klimamessbox-sensoren-3d-oben.png?200|}} ===== TESTCODE KLIMABOX BME, MHZ und SDS===== ==== BME280 ==== #include #include #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 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 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 #include #include // 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³ ====== 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