===== 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