====== CO2-Ampel ====== * [[www-fuer-alle:arduino|SAS-Projekte: Arduino ab Klasse 8]] * Fragen und Anregungen: [[ARDUINO@GymSAS.de]] * [[https://www.cik-solutions.com/anwendungen/co2-im-innenraum/|Co2-Werte und Bedeutung]] * [[info:arduino:iot-co2-ampelprojekt|intern]] ===== Varianten ===== Arduino mit Z19B-Sensor und * LEDs * LEDs und Buzzer * LEDs, Buzzer und OLED-Anzeige * LEDs, BUZZER, OLED-Anzeige und SD-Speicher (und UHR) * LEDs, BUZZER, OLED-Anzeige und WLAN-Anbindung an einen Raspi mit Datenbank und Apache * [[https://www.youtube.com/watch?v=-zFD7MYOmb4|Weiterentwicklung der CO2-Ampel]] * [[https://www.heinz-schmitz.org/index.php/hiz-nachrichtenleser/hiz323.html|Materialien]] ===== fertige Ampel - LED mit Buzzer und OLED-Anzeige (ohne UHR und SD-Karte)===== {{:www-fuer-alle:co2-nano-oled-2.jpg?300|}}{{:www-fuer-alle:arduino-co2-nano.jpg?200|}} * Nano 5 € * CO2-Sensor 20 € * OLED 5 € * Kleinteile 4 € * POWERBANK 6 € * 40 € pro Ampel ===== Sensorbeschaltung ===== {{:www-fuer-alle:z19b.png?200|}} * PWM - Pin 6 * GND - Masse * Vin - +5V * HD über einen Taster oder ein Steckkabel an Masse zur Kalibrierung ===== Messbeispiel ===== {{:www-fuer-alle:2020-12-10-co2-messung-klausur.png?400|}} ===== Breadboardvariante ===== {{:www-fuer-alle:co2-oled-sd-schaltung-breadboard.jpg?400|}}{{:www-fuer-alle:co2-oled-sd-schaltung-breadboard-fertig.jpg?400|}} ===== Programmcode ===== * Zusammenkopierter Sketch * ACHTUNG: der dieser Sketch ist für die kleinste (billigste) NANO-Variente zur groß. * mit UHR und SD-Kartenschreiber * Aufgabe: Reduziere den Programmcode auf ein Minimum /** * CO2-Messung mit Sensor Typ MHZ19B * Messwerterfassung durch PWM-Signal */ /************************************** * für den Betrieb der OLED-Anzeige * **************************************/ #include "SSD1306AsciiAvrI2c.h" // OLED-Bibliothek #define I2C_ADDRESS_OLED 0x3C // 0X3C+SA0 - 0x3C or 0x3D OLED #define RST_PIN -1 // Define proper RST_PIN if required. OLED #include // In diesem Sketch verwenden wir die library #include "RTClib.h" SSD1306AsciiAvrI2c oled; RTC_DS3231 rtc; File Messdaten; // An dieser Stelle wird die Variable "Messdaten" als File (dts. Datei) deklariert. int z = 0; // globale Zaehlvariable für die SD-Kartennutzung in der loop /************************************************ * für den Betrieb der LED-Anzeige und Summer * ************************************************/ const int summer = 4; const int gruen = 7; const int gelb = 8; const int rot = 9; const int blau = 10; /************************************************ * Alarmstufen * *********************************************/ const int guteLuft = 800; const int mittlereQualitaet = 950; const int schlechteLuft = 1200; const int vielzuvielCO2 = 2000; const int luftAlarm = 2500; /************************************** * für den Betrieb des MH-Z19B * **************************************/ const int pwmpin = 6; // Der Sensor hängt an Pin 6 const int range = 5000; // Der eingestellte Messbereich (0-5000ppm) int ppm_pwm = 0; /************************************** * Festlegung Messintervall * **************************************/ const int schreibPause = 12000; // nächste Messung erst nach 12 Sekunden void setup() { /************************************** * für den Betrieb der OLED-Anzeige * **************************************/ #if RST_PIN >= 0 oled.begin(&Adafruit128x64, I2C_ADDRESS_OLED, RST_PIN); #else // RST_PIN >= 0 oled.begin(&Adafruit128x64, I2C_ADDRESS_OLED); #endif // RST_PIN >= 0 oled.setFont(System5x7); // Call oled.setI2cClock(frequency) to change from the default frequency. oled.clear(); oled.println(" CO2 - Ampel "); oled.println(" ------------"); oled.println(" SAS-Projekt 2020"); tone(summer,444); // delay(20); noTone; /************************************** * für den Betrieb der LED-Anzeige * **************************************/ pinMode(gruen, OUTPUT); pinMode(gelb, OUTPUT); pinMode(rot, OUTPUT); pinMode(blau, OUTPUT); noTone; ledTest(); Serial.begin(9600); /********************************************************************************************************** * * UHR * **********************************************************************************************************/ if (! rtc.begin()) { Serial.println("Couldn't find RTC"); while (1); } if (rtc.lostPower()) { Serial.println("RTC lost power, lets set the time!"); // following line sets the RTC to the date & time this sketch was compiled rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // This line sets the RTC with an explicit date & time, for example to set // January 21, 2014 at 3am you would call: // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); } /********************************************************************************************************** * * SD-Kartenleser - Schreibtest * *********************************************************************************************************/ Serial.println("Initialisiere SD-Karte"); if (!SD.begin(5)) { // Wenn die SD-Karte nicht (!SD.begin) gefunden werden kann, ... Serial.println("Initialisierung fehlgeschlagen!"); // ... soll eine Fehlermeldung ausgegeben werden. .... return; } Serial.println("Initialisierung abgeschlossen"); // ... Ansonsten soll die Meldung "Initialisierung abgeschlossen." ausgegeben werden. /********************************************************************************************************** * * SD-Kartenleser - Lesetest NUN WIRD der bisherige Text AUSGELESEN * *********************************************************************************************************/ Messdaten = SD.open("co2.txt"); // Die Textdatei auf der SD-Karte wird wieder geoeffnet... if (Messdaten) { Serial.println("alte Werte: "); // ... und der Name der Datei wird ausgegeben. while (Messdaten.available()) // Anschließend wird die Datei so lange ausgelesen (while)... { Serial.write(Messdaten.read()); // ... bis keine Daten mehr gefunden werden können. digitalWrite(blau, HIGH); digitalWrite(blau, LOW); } Messdaten.close(); // Im Anschluss wird die Textdatei wieder geschlossen. } else // Sollte keine Textdatei (also test.txt) gefunden werden können... { Serial.println("Textdatei konnte nicht geoeffnet werden"); // ... erscheint eine Fehlermeldung im seriellen Monitor. } digitalWrite(blau, LOW); /************************************** * für den Betrieb des MH-Z19B * **************************************/ pinMode(pwmpin, INPUT); // PWM-Pin auf Eingang setzen } // ENDE Setup /*********************************************************************************************************************** * * LOOP * ***********************************************************************************************************************/ void loop() { z = z+1; ppm_pwm = readCO2PWM(); // Messung der PWM-Länge mittels einer eigenen Funktion if (z > 4) { // erst die 5. Messung wird gesichert, dann ca. pro Minute eine messWertSchreibenSD(); // jeden 5. Messwert auf die SD-Karte schreiben (blaue LED) z = 0; // Zaehler zurueck setzen } oledAnzeige(); // Messwerte auf dem OLED ausgeben delay(schreibPause); // PAUSE: nächste Messungen in 12 Sekunden (ca. 5 Messungen pro Minute) } /************************************************************************************************************************ * * LOOP ENDE * ************************************************************************************************************************/ /********************************* * * * Funktionen und Prozeduren * * * *********************************/ /********************************************************************************************************** * * Funktionen zum PWM-Auslese des CO2-Wertes * ***********************************************************************************************************/ int readCO2PWM() { unsigned long th; int ppm_pwm = 0; float pulsepercent; do { th = pulseIn(pwmpin, HIGH, 1004000) / 1000; float pulsepercent = th / 1004.0; // Pulslänge in Prozent (%) ppm_pwm = range * pulsepercent; // PPM-Werte bei gegebenem Range } while (th == 0); // Der gemessene Wert wird an die loop()-Funktion zurückgegeben, return ppm_pwm; // wo er dann ausgegeben wird. } /********************************************************************************************************** * * SD-Schreibprocedur * *********************************************************************************************************/ void messWertSchreibenSD() { Messdaten = SD.open("co2.txt", FILE_WRITE); // An dieser Stelle wird die Messdaten erstellt. if (Messdaten) // Wenn die Messdaten ("co2.txt") gefunden wurde.... { ledSD(); // Schreibanzeige - KEINE SD entfernen Serial.println("Schreibe in Messdaten..."); // Meldung im seriellen Monitor für den Schreibvorgang DateTime now = rtc.now(); // altuelle Zeit auslesen Messdaten.print(now.year(), DEC); Messdaten.print('-'); Messdaten.print(now.month(), DEC); Messdaten.print('-'); Messdaten.print(now.day(), DEC); // im Format 2020-10-13 schreiben Messdaten.print(' '); Messdaten.print(now.hour(), DEC); Messdaten.print(':'); Messdaten.print(now.minute(), DEC); Messdaten.print(':'); Messdaten.print(now.second(), DEC); // im Format 10:35:22 schreiben Messdaten.print(' ' ); Messdaten.print(ppm_pwm); Messdaten.println(" ppm CO2"); Messdaten.close(); // Anschließend wird die Messdaten wieder geschlossen... Serial.println("Abgeschlossen."); // ... und eine erneute Meldung im seriellen Monitor ausgegeben. Serial.println(); } else { // Wenn !keine! Messdaten gefunden werden kann ... Serial.println("Messdaten konnte nicht ausgelesen werden"); // ... erscheint eine Fehlermeldung im seriellen Monitor. } } /********************************************************************************************************** * * OLED-Anzeigeprocedur * *********************************************************************************************************/ void oledAnzeige() { DateTime now = rtc.now(); // altuelle Zeit auslesen oled.clear(); oled.println(" CO2 - Ampel "); oled.println("-------------------"); oled.println(" GYPT-HH "); oled.println("-------------------"); oled.println(" "); oled.print(" CO2 Wert: "); oled.print(ppm_pwm); oled.println(" ppm"); oled.println("-------------------"); oled.print(" "); oled.print(now.hour(), DEC); oled.print(':'); oled.println(now.minute(), DEC); ledRot(); ledGruen(); if (ppm_pwm > mittlereQualitaet) { ledGelb(); } if (ppm_pwm > schlechteLuft) { ledRot(); } if (ppm_pwm > vielzuvielCO2) { ledblau(); } if (ppm_pwm > luftAlarm) { alarm(); } Serial.print("PWM-Messung: "); // Ausgabe der Werte über die serielle USB-Verbindung Serial.print(ppm_pwm); Serial.println(" ppm CO2"); } /************************************************************************************************************ * * LED-Anzeige mit Summer * ***************************************************************************************************************/ void ledTest() { digitalWrite(gruen, HIGH); digitalWrite(gelb, HIGH); digitalWrite(rot, HIGH); digitalWrite(blau, LOW); delay(600); digitalWrite(blau, HIGH); delay(100); digitalWrite(gruen, LOW); digitalWrite(gelb, LOW); digitalWrite(rot, LOW); digitalWrite(blau, LOW); noTone(summer); } void ledGruen(){ digitalWrite(gruen, HIGH); digitalWrite(gelb, LOW); digitalWrite(rot, LOW); digitalWrite(blau, LOW); noTone(summer); } void ledGelb(){ digitalWrite(gruen, LOW); digitalWrite(gelb, HIGH); digitalWrite(rot, LOW); digitalWrite(blau, LOW); noTone(summer); } void ledRot(){ digitalWrite(gruen, LOW); digitalWrite(gelb, LOW); digitalWrite(rot, HIGH); digitalWrite(blau, LOW); noTone(summer); } void ledblau(){ digitalWrite(gruen, LOW); digitalWrite(gelb, LOW); digitalWrite(rot, HIGH); digitalWrite(blau, HIGH); delay(100); digitalWrite(blau, LOW); delay(200); digitalWrite(blau, HIGH); delay(200); digitalWrite(blau, LOW); delay(200); noTone(summer); } void alarm(){ digitalWrite(gruen, HIGH); digitalWrite(gelb, HIGH); digitalWrite(rot, HIGH); digitalWrite(blau, HIGH); tone(summer,600); } void ledSD(){ digitalWrite(gruen, LOW); digitalWrite(gelb, HIGH); digitalWrite(rot, LOW); digitalWrite(blau, HIGH); noTone(summer); } void ledAus() { digitalWrite(gruen, LOW); digitalWrite(gelb, LOW); digitalWrite(rot, LOW); digitalWrite(blau, LOW); noTone(summer); }