Hier mal eine Zusammenfassung eines CO2-Messsystems (CO2-Ampel) mit dem Raspberry Pi an dem der MH-Z19b, OLED Anzeige, LED-Rgb-Ampel hängen. Auch werden die Daten Online an Thinkspeak, MQTT, NodeRed, Alexa ausgabe von Warnungen und abfrage der Co2 Werte, Pushover nachrichten ans Handy, REST Java-Client, Docker Container und an einen History Browser gesendet. Wie hier auch schon einzeln veröffentlicht.
Die Architektur
Nachhaltiges CO2-Gehäuse
Werbefreie Version der Co2-Ampel.
CO2 Infos
Vor dem CO2-Messprojekt mit einem Raspberry Pi und einem MH-Z19B hier mal ein paar Infos zum CO2:
Bei leichten Tätigkeiten beträgt die CO2-Abgabe des Menschen ungefähr 20 000 ml/h. ( 20 L/h)
Bei 1000 ppm empfinden rund 20 % der Personen die Raumluft als unbefriedigend.
Diese Konzentration entspricht der Pettenkofer-Zahl, die von dem Hygieniker Max von Pettenkofer (1858) als Richtwert für die maximale CO2- Konzentration in Wohn- und Aufenthaltsräumen mit 0,1 Vol% CO2 (1000 ppm) definiert wurde.
Wo es viel CO2 gibt, werden auch besonders viele Keime gefunden. Die amerikanischen Wissenschaftler Rudnick und Milton zum Beispiel untersuchten 2003, wie hoch das Grippe Ansteckungsrisiko in einem Klassenraum ist. 30 Personen waren vier Stunden lang im Klassenraum, eine Person hatte akut Grippe. Das Ergebnis: Bei 1.000 ppm CO2 steckten sich fünf Personen an, bei 2.000 ppm waren es zwölf und bei 3.000 ppm sogar 15.
MAK-Werte Deutschland für CO2: 9100 mg/m3
Die DIN EN 13779 teilt die Raumluft je nach Kohlenstoffdioxid-Konzentration in vier Qualitätsstufen ein.
1. Bei Werten unter 800 ppm gilt die Raumluftqualität als gut,
2. Werte zwischen 800 und 1000 ppm (0,08 bis 0,1 Vol.-%) gelten als mittel,
3. Werte von 1000 bis 1400 ppm als mäßige Qualität.
4. Bei Werten über 1400 ppm gilt die Raumluftqualität als niedrig.
Zum Vergleich: Im globalen Mittel liegt der CO2-Anteil der Luft bei etwa 400 ppm Volumenanteil; er schwankt aber regional, tageszeitabhängig und jahreszeitabhängig stark. Die Maximale Arbeitsplatz-Konzentration für eine tägliche Exposition von acht Stunden pro Tag liegt bei 5000 ppm.
Beim Einatmen enthält frische Luft ungefähr 400 ppm Kohlendioxid. Beim Ausatmen enthält die Luft den ca. 100-fachen Anteil an CO2, also knapp 4% oder 40.000 ppm. Dies ist bei jedem Menschen ziemlich gleich. Nur die tatsächlich ausgeatmete Menge an Kohlendioxid ist stark abhängig vom Lungenvolumen (und damit von Alter und Größe) und der Aktivitätder Person.
Bei einer Konzentration von 1,5 % (15.000 ppm) nimmt das Atemzeitvolumen um mehr als 40 % zu.
Umrechnungsfaktoren (bei 293,15 K, 1013,25 hPa):
1 ppm = 1,83 mg/m3
1 mg/m3 = 0,546 ppm
1 Vol% = 10 000 ppm
1 ppm = 0,0001 Vol%
Kohlendioxidkonzentration unter 1 000 ppm = hygienisch unbedenklich
Kohlendioxidkonzentration zwischen 1 000 und 2 000 ppm = hygienisch auffällig
Kohlendioxidkonzentration über 2 000 ppm = hygienisch inakzeptabe
Weitere Infos bei Wikipedia.
CO2 Mindmap
Dank für die Mindmap geht an (c) Dr.Kleinhirn.eu
CO2 Podcast
Die 3. Folge des Podcast. Das Thema „Acht Fragen zum Kohlendioxid (CO2)„, viel Spaß beim hören …
Der Podcast ist übrigens seit 2019 auch im Apple-Store, Spotify und bei Google verfügbar.
Der Sensor MH-Z19B
Der CO2 Sensor MH-Z19B (NDIR-Sensor). Die Genauigkeit liegt bei ±50 ppm+5%.
Hier gibt es das Handbuch (engl.) zu dem Sensor als PDF vom Hersteller und hier die Kommandos.
Raspberry Pi
Wir wollen nun mal ein CO2-Messsystem mit dem MH-Z19B aufbauen und die CO2-Konzentrationen mit einem Raspberry Pi einlesen und per MQTT an eine NodeRed-Installation senden. Parallel dazu werden die Daten noch in einer CSV-Datei geschrieben für Langzeitauswertungen. Wenn der Grenzwert von 1000 ppm erreicht ist, wird noch eine Pushover Nachricht an ein Handy gesendet, so das rechtzeitig gelüftet werden kann.
Installation
1. raspi-config aufrufen und die Serielle-Schnittstelle aktivieren
2. Sensor mit den vier Drähten an den Pi anschließen wie hier beschrieben
https://github.com/IT-Berater/mh-z19.git
3. Git installieren wenn noch nicht vorhanden mit: sudo apt-get install git
4. Leeres Verzichnis erstellen und darin das setup.sh aufrufen
mkdir co2
cd co2
git clone https://github.com/IT-Berater/mh-z19.git
cd mh-z19
./setup.sh
5. Testen ob die co2 Werte geliefert werden mit Python (Python 2.7.16)
sudo python -m mh_z19
Wenn diese Rückgabe kommt ist es geschafft:
{‚co2‘: 668}
6. MQTT installieren und aktivieren
sudo apt-get install -y mosquitto mosquitto-clients
sudo systemctl enable mosquitto.service
7. Das Bash Script in das Verzeichnis kopieren und ausführen:
sudo chmod +x send-co2-per-mqtt.sh
Das Bash Script send-co2-per-mqtt.sh erstellt ein JSON Objekt und sendet die über Python mh_z19 ermittelten Werte per MQTT unter dem Pfad co2 zum Dashboard.
Das Script liegt auch in /example/send-co2-per-mqtt.sh Verzeichnis.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#!/bin/sh # (c) 2020 Thomas Wenzlaff # send-co2-per-mqtt.sh # Sendet jede alle 1 Minuten die co2 Werte an MQTT NODERED while (true) do zeit=$(date +"%d.%m.%Y %H:%M") wert=$(sudo python -m mh_z19 --all 2>%1) echo "Sende CO2 Messung: $wert um $zeit" # erstellen ein JSON Objekt sende="{ \"messung\":{\"satz\": [{\"zeitpunkt\":\"$zeit\" },$wert ]}}" mosquitto_pub -d -t co2 -m "$sende" sleep 60; done; |
Das Script dann später im Hintergrund gestartet werden mit ./send-co2-to-mqtt.sh &
8. Den NodeRed-Flow kopieren und den Mqtt-Server eingeben. Hier das CO2 Daschboard mit dem Trend und die aktuellen Werte die jede Minute aktualisiert werden:
Der nötige NodeRed-Flow zum Download:
9. Sensor auf 400 ppm kalibrieren. Dazu den Sensor an frischer Luft eine halbe Stunde laufen lassen und mit dem Programm mh_z19
einmal mit dem Parameter 5000 (je nach Sensor) und mit zero_point_calibration aufrufen.
sudo python -m mh_z19 –detection_range_5000
sudo python -m mh_z19 –zero_point_calibration
aufrufen, wie hier beschrieben.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
sudo python -m mh_z19 --help usage: __main__.py [-h] [--serial_device SERIAL_DEVICE] [--serial_console_untouched] [--version | --all | --abc_on | --abc_off] [--span_point_calibration SPAN_POINT_CALIBRATION] [--zero_point_calibration] [--detection_range_10000] [--detection_range_5000] [--detection_range_2000] return CO2 concentration as object as {'co2': 416} optional arguments: -h, --help show this help message and exit --serial_device SERIAL_DEVICE Use this serial device file --serial_console_untouched Don't close/reopen serial console before/after sensor reading --version show version --all return all (co2, temperature, TT, SS and UhUl) as json --abc_on Set ABC functionality on model B as ON. --abc_off Set ABC functionality on model B as OFF. --span_point_calibration SPAN_POINT_CALIBRATION Call calibration function with SPAN point --zero_point_calibration Call calibration function with ZERO point --detection_range_10000 Set detection range as 10000 --detection_range_5000 Set detection range as 5000 --detection_range_2000 Set detection range as 2000 |
OLED Anzeige
OLED CO2 Anzeige
Damit die CO2-Werte der Co2-Ampel auch ohne Internet angezeigt werden, habe ich ein kleine OLED-Display an den Raspberry Pi angeschlossen. Das hatte ich noch in der Bastelkiste. Hatte es auch schon an einem Arduino. So werden die Werte nun dauernd angezeigt:
Für die Anzeige verwende ich den kostenlosen VCR_OSD_MONO_1.001.ttf Font. Die Datei muss parallel zum Script liegen. Dann der Aufruf:
1 |
font = ImageFont.truetype('VCR_OSD_MONO_1.001.ttf', 18) |
OLED Temperatur Anzeige
Zusätzlich zur CO2-Ampel auch noch die Temperatur zur Anzeige des CO2-Wertes hinzufügen. Das ist schnell gemacht, hier das Ergebnis:
Das zusätzliche Einlesen der Temperatur ist in Python auch schnell gemacht:
1 2 3 4 5 6 |
input_json = json.loads(sys.argv[1]) messung_json = input_json['messung'] satz_json = messung_json['satz'] satz_array_json = satz_json[1] co2_wert = satz_array_json['co2'] temperatur_wert = satz_array_json['temperature'] |
So sieht ja das JSon File welches per MQTT gesendet wird aus:
In Java ist das Einlesen auch schnell gemacht:
1 2 3 4 5 6 |
JSONObject jsonNachricht = new JSONObject(nachricht.toString()); JSONArray nachrichten = jsonNachricht.getJSONObject("messung").getJSONArray("satz"); JSONObject satz = (JSONObject) nachrichten.get(1); int co2 = satz.getInt("co2"); int temp = satz.getInt("temperature"); |
RGB LED CO2-Ampel
Für die CO2-Ampel noch eine RGB LED,
die ich seit längerer Zeit liegen hatte angeschlossen:
So zeigt sie ROT:
Das Python-Script für die Ansteuerung liegt hier. Und hier liegen die erstellten Python-Scripte und bash-Scripte für die Ansteuerung der coolen CO2-Ampel.
Schaltplan der CO2-Ampel
CO2 Online an Thinkspeak
Mein Raspberry Pi misst ja seit einigen Tagen die CO2 Werte in der Luft, wie hier beschrieben. Nun können hier die Werte in Echtzeit abgelesen werden. Sie werden von NodeRed jede Minute aktuallisiert und weitergeleite.
CO2 REST Service mit Quarkus
Einen REST Service in 15 min mit Quarkus, OpenAPI, Swagger UI und JUnit-Test erstellen und um CO2-Ampel Service erweitern ist nicht kompliziert.
Vorraussetzungen Java 11 und Maven. Test mit:
mvn -version
auf der Kommandozeile:
1 2 3 4 5 6 |
~ mvn -version Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f) Maven home: /opt/local/share/java/maven3 Java version: 11.0.9, vendor: AdoptOpenJDK, runtime: /Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home Default locale: de_DE, platform encoding: UTF-8 OS name: "mac os x", version: "10.15.7", arch: "x86_64", family: "mac" |
Dann fangen wir in einem leeren Verzeichnis an
1 2 |
mkdir rest-co2ampel cd rest-co2ampel |
jetzt rufen wir mit dem Maven Quarkus Plugin das create Goal auf:
1 |
mvn io.quarkus:quarkus-maven-plugin:1.9.0.Final:create -DprojectGroupId=de.wenzlaff.co2ampel -DprojectArtifactId=info-kleinhirn -DclassName="de.wenzlaff.Co2AmpelResource" -Dpath="/co2ampel" |
Zwei Sekunden später, ist das Projekt angelegt:
1 2 3 4 5 6 7 8 9 10 11 12 |
[INFO] ======================================================================================== [INFO] Your new application has been created in /Users/thomaswenzlaff/rest-co2ampel/info-kleinhirn [INFO] Navigate into this directory and launch your application with mvn quarkus:dev [INFO] Your application will be accessible on http://localhost:8080 [INFO] ======================================================================================== [INFO] [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.898 s [INFO] Finished at: 2020-10-27T12:57:17+01:00 [INFO] ------------------------------------------------------------------------ |
Nun gehen wir in das erzeugte Verzeichnis: cd info-kleinhirn
Nun starten wir den build und den Server mit:
mvn quarkus:dev
Ein paar Sekunden später ist alles gebaut, und der Server läuft:
1 2 3 4 5 6 7 8 9 |
[INFO] Compiling 1 source file to /Users/thomaswenzlaff/rest-co2ampel/info-kleinhirn/target/classes Listening for transport dt_socket at address: 5005 __ ____ __ _____ ___ __ ____ ______ --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \ --\___\_\____/_/ |_/_/|_/_/|_|\____/___/ 2020-10-27 13:00:57,115 INFO [io.quarkus] (Quarkus Main Thread) info-kleinhirn 1.0-SNAPSHOT on JVM (powered by Quarkus 1.9.0.Final) started in 0.836s. Listening on: http://0.0.0.0:8080 2020-10-27 13:00:57,118 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated. 2020-10-27 13:00:57,118 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, resteasy] |
Ein Aufruf von
bringt die statische Webseite. Und ein
http://localhost:8080/co2ampel
liefert den Aufruf des REST CO2Ampel Services.
Nun unterbrechen wir den Server mit CTLR-C und ergänzen für den Test die Swaggert GUI hinzu, einfach ein
1 |
./mvnw quarkus:add-extension -Dextensions="quarkus-smallrye-openapi" |
und ein neues starten:
./mvnw compile quarkus:dev
Dann im Browser die GUI Aufrufen:
http://localhost:8080/swagger-ui/
Cool:
Und openapi geht auch, einfach in einem neuen Fenster ein
curl http://localhost:8080/openapi
So, jetzt nur noch echt CO2 Messung einbauen. Ich habe einen Mqtt-Broker der die Werte liefert.
Wir gehen in das Verzeichnis und /rest-co2ampel/info-kleinhirn/src/main/java/de/wenzlaff und holen uns die zwei Beispielklasse von github mit:
1 2 3 |
curl https://gist.githubusercontent.com/IT-Berater/c58da54b1f20337827f8de827715d728/raw/10d721ca73148b3be195d6fc2fa45ff02af42d9d/CO2AmpelResource.java > CO2AmpelResource.java curl https://gist.githubusercontent.com/IT-Berater/dc7695fdb406d6f93d4a47553d309ec9/raw/a7928a1527d164d2a51ddece19fefc431c3c46a1/MqttCo2Client.java > MqttCo2Client.java |
Dann gehen wir in das Wurzelverzeichnis des Projektes und fügen in der pom.xml diese beiden abhängigkeiten hinzu:
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.eclipse.paho</groupId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20200518</version> </dependency> |
Dann müssen wir noch den JUnit Test anpassen.Dazu die Testklasse Co2AmpelResourceTest.java in der Methode testHelloEndpoint() diesen Inhalt einfügen:
1 |
given().when().get("/v1/rest/co2").then().statusCode(200).body(is("CO2 Wert: 0")); |
So, nun können wir das Projekt starten:
./mvnw compile quarkus:dev
So der JUnit Test wurde erfolgreich ausgeführt. Nun kann der REST-Service über die URL
http://localhost:8080/v1/rest/co2
abgefragt werden. Wenn der Mqtt-Service Werte die entsprechenden Werte liefert, sonst 0 😉
Und auch über die openapi stehen die REST-Services bereit, cool …
Openapi geht auch:
Und hier, wenn der Mqtt-Server echte Werte liefert (evl. den Server localhost in der Java-Klasse MqttCo2Client durch einen anderen ersetzen, bei mir läuf der CO2-Sensor auf einem Raspberry Pi, wie hier im Blog beschrieben):
So, jetzt muss ich aber lüften …
Docker Container
Wer noch 5 Minuten hat, kann das Beispielprojekt mit der CO2-Ampel auch gleich in einem Docker Container laufen lassen. Vorraussetzung Docker läuft und das Beispielprojekt wurde installiert.
Dann sind, dank der automatisch generierten Docker-Files nur diese 3 Schritte im Verzeichnis der root pom nötig:
1 2 3 4 5 6 7 8 |
// 1. das Projekt bauen mvn package -Dquarkus.package.type=fast-jar // 2. das Image bauen, man beachte den Punkt am Ende docker build -f src/main/docker/Dockerfile.fast-jar -t quarkus/info-kleinhirn-fast-jar . // 3. den Container starten mit docker run -i --rm -p 8080:8080 quarkus/info-kleinhirn-fast-jar |
Schon läuft der CO2 Service http://localhost:8080/v1/rest/co2 und CO2-Ampel über http://localhost:8080/v1/rest/co2ampel
Cool. Und hier das Dockerfile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1 ARG JAVA_PACKAGE=java-11-openjdk-headless ARG RUN_JAVA_VERSION=1.3.8 ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \ && microdnf update \ && microdnf clean all \ && mkdir /deployments \ && chown 1001 /deployments \ && chmod "g+rwX" /deployments \ && chown 1001:root /deployments \ && curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \ && chown 1001 /deployments/run-java.sh \ && chmod 540 /deployments/run-java.sh \ && echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" COPY --chown=1001 target/quarkus-app/lib/ /deployments/lib/ COPY --chown=1001 target/quarkus-app/*.jar /deployments/ COPY --chown=1001 target/quarkus-app/app/ /deployments/app/ COPY --chown=1001 target/quarkus-app/quarkus/ /deployments/quarkus/ EXPOSE 8080 USER 1001 |
Autostart nach Reboot
Wie können bash Scripte wie die CO2-Ampel auf dem Raspberry Pi automatisch nach dem reboot gestartet werden? Mit einem Service systemctl. Was ist nötig?
1. Erstellen einer Datei:
/lib/systemd/system/co2ampel.service
mit Inhalt
1 2 3 4 5 6 7 8 9 |
[Unit] Description=CO2Ampel Service After=multi-user.target [Service] ExecStart=/home/pi/twco2ampel/src/main/scripts/send-co2-per-mqtt.sh & [Install] WantedBy=multi-user.target |
2. Dann ausführbar machen:
sudo chmod 644 /lib/systemd/system/co2ampel.service
Service kann nun gestartet werden mit:
sudo systemctl start co2ampel
Status abfragen:
sudo systemctl status co2ampel
Stoppen:
sudo systemctl stop co2ampel
Oder restart:
sudo systemctl restart co2ampel
3. Damit der Service beim rebooten gestartet wird ein
sudo systemctl enable co2ampel
4. Das system reloaden:
sudo systemctl daemon-reload
Alexa CO2
Alexa sagt den Co2-Wert in der Luft in ppm an, wenn man sie fragt. Hier ein kurzes Demo-Video:
Auf dem Raspberry Pi läuft ein NodeRed mit dem node-red-contrib-alexa-remote2 der nicht nur Text ausgeben kann, sondern auch Befehle auswerten kann. Hier der relevante Teil der Sprachausgabe:
Es kommt auch noch eine Ansage, wenn der Wert von 1000 ppm überschritten wird, das nun gelüftet werden muss:
CO2 History Browser
Ein Raspberry Pi dient ja als CO2-Ampel und misst jede Minute den CO2-Wert bei mir in der Luft. Die Werte werden auch als CSV-Datei gespeichert. Jeden Tag wird eine neue Datei erzeugt. Die Dateien sind dann nur ca. 30 kB groß.
Mit dem CO2-History Browser kann ich nun mit jeden Browser auch die CO2-Vergangenheit leicht anzeigen lassen. Es braucht nur die entsprechende Datei ausgewählt zu werden und auf den „GRAPH“ Button geklickt zu werden. Hier z.B. der CO2-Verlauf von gestern:
Mit klick auf den „OPEN“-Button kann die Datei auch im CSV-File geladen, und dann in Excel oder Numbers geöffnet werden:
Hier der NodeRed Flow, der diesen CO2-History-Browser erzeugt:
Weitere Infos unter dieser Kategorie CO2.