/* Arduino Sketch zum Messen der Luftgüte und Signalisierenung zum Lüften über eine RGB LED. Die Messung geschieht als virtuelle CO2-Konzentrationsmessung unter der Annahme, dass nur CO2 als Spurengas vorhanden ist. Die ppm-Werte sind nur als sehr grobe Abschätzung zu sehen, da sie einerseits von der Kalibrierung frischer Luft abhängen und andererseits die Annahme, dass nur CO2 in der Luft ist in den meisten Fällen nicht zutrifft. Als Anzeiger, wann es Zeit ist zum Lüften kann es dennoch nützlich sein. Based on: https://github.com/NEOE-IO/NEOE-IOT-Kit-1 https://github.com/GeorgK/MQ135 PPM Calculations done using power regression: https://davidegironi.blogspot.com/2014/01/cheap-co2-meter-using-mq135-sensor-with.html More Infofmation to the Setup about to come. */ /* Ideen: * - Sensor liest nach dem Einbrennen und langer Kalibrierungszeit den Mittelwert des Widerstands aus * und schreibt den als RZERO in den EEPROM * - Buzzer implementieren */ /*********************************************************** Globale Parameter ***********************************************************/ //// Grenzwerte // immer untere Grenze des Bereichs float mittlereLuft = 900; float schlechteLuft = 1500; //// Verwendete Pins // Analog PIN - Messung des Widerstands am MQ-135 uint8_t _pin = A0; int pinWert; // Parameter für die Indikator-LED int pinBlau = 9; int pinGruen = 10; int pinRot = 11; int ledBlau; int ledGruen; int ledRot; //// Weitere Parameter, die keiner Anpassung bedürfen // Parameter zur Berechnung der Durchschnittswerte const int anzahlMessungen = 10; int messungen[anzahlMessungen]; // Array zur Berechnung der Durchschnittswerte int messungIndex = 0; // Index eines einzelnen Wertes float gesamt = 0; // Summe aller Werte float durchschnitt = 0; // Durchschnittswert // Lastwiderstand des MQ-135 // Bei Aufbau mit Platine: Wert des mittleren SMD-Elements oben auf der Platine // Bei Aufbau mit Sensor ohne Platine: Wert des mit dem Sensor in Reihe geschalteten Widerstands. #define RLAST 1.0 // Parameter zur CO2 Berechnung in ppm auf Basis des Sensor-Widerstands // Aus dem Datenblatt zum MQ135 entnehmen "Fig.2 sensitivity characteristics of the MQ-135" #define PARA 116.6020682 #define PARB 2.769034857 // CO2-Wert in der Atmosphäre, als Basis für die Kalibrierung #define ATMOCO2 397.13 // Alle 1 Sekunden eine Messung durchführen (ausser beim Kalibrieren) int zeitAbstand = 1000; // Für die Kalibrierung erforderliche Parameter float widerstandMomentan; float widerstandMaximal; /*********************************************************** Funktionen ***********************************************************/ // Widerstand des Sensors (in Kiloohm) berechnen float berechneWiderstand() { float wert = durchschnitt; return ((1023. / wert) - 1.) * RLAST; } // CO2 Wert in ppm berechnen. Auf Basis der Vereinfachung, dass sich nur CO2 in der Luft befindet. // Fig.2 sensitivity characteristics of the MQ-135 float berechnePPM() { return PARA * pow((berechneWiderstand() / widerstandMaximal), -PARB); } // RZero Widerstand des Sensors (in Kiloohm) für die Kalibrierung berechnen float berechneWiderstandAtmo() { return berechneWiderstand() * pow((ATMOCO2 / PARA), (1. / PARB)); } //Sensor auslesen und Mittelwert ueber die letzten 10 Messwerte berechnen float berechneDurchschnittlichenSensorWert(){ gesamt = gesamt - messungen[messungIndex]; messungen[messungIndex] = analogRead(_pin); gesamt = gesamt + messungen[messungIndex]; messungIndex = messungIndex + 1; if (messungIndex >= anzahlMessungen) { messungIndex = 0; } return gesamt / anzahlMessungen; } // Sensor Kalibrierung // Volatiler Sensor, daher sanfte Kalibrierung, RCurrent nur mit 0,001% void kalibriereSensor(){ float widerstandMomentan = berechneWiderstandAtmo(); if ((((9999 * widerstandMaximal) + widerstandMomentan) / 10000) > widerstandMaximal) { widerstandMaximal = (((9999 * widerstandMaximal) + widerstandMomentan) / 10000); } } //LED-Farben void setzeGruen(){ledGruen=255; ledRot=0; ledBlau=0; analogWrite(pinGruen, ledGruen); analogWrite(pinRot, ledRot); analogWrite(pinBlau, ledBlau);} void setzeBlau(){ledGruen=0; ledRot=0; ledBlau=255; analogWrite(pinGruen, ledGruen); analogWrite(pinRot, ledRot); analogWrite(pinBlau, ledBlau);} void setzeRot(){ledGruen=0; ledRot=255; ledBlau=0; analogWrite(pinGruen, ledGruen); analogWrite(pinRot, ledRot); analogWrite(pinBlau, ledBlau);} void setzeOrange(){ledGruen=165; ledRot=255; ledBlau=0; analogWrite(pinGruen, ledGruen); analogWrite(pinRot, ledRot); analogWrite(pinBlau, ledBlau);} /*********************************************************** Setup: wird einmal zu Beginn ausgeführt ***********************************************************/ void setup() { // Array zur Berechnung der Durchschnittswerte aufbauen for (int dieseMessung = 0; dieseMessung < anzahlMessungen; dieseMessung++) { messungen[dieseMessung] = 0; } Serial.begin(9600); } /*********************************************************** Loop: Wird immer wieder ausgeführt, wenn am Ende angelangt. ***********************************************************/ void loop() { // Sensor auslesen und Durchschnittswerte berechnen durchschnitt = berechneDurchschnittlichenSensorWert(); // Volatiler Sensor, daher sanfte Kalibrierung, RCurrent nur mit 0,001% kalibriereSensor(); // ppm berechnen float ppm = berechnePPM(); Serial.println(ppm); // Indikator-LED if (ppm < ATMOCO2) setzeBlau(); else if (( ppm >= ATMOCO2 ) && (ppm < mittlereLuft)) setzeGruen(); else if (( ppm >= mittlereLuft ) && (ppm < schlechteLuft)) setzeOrange(); else setzeRot(); // Nach erfolgter Kalibrierung Anzahl der Messungen reduzieren, da sonst zu viele MQTT-Meldungen versendet werden if (ppm > ATMOCO2) delay(zeitAbstand); }