Autor Thema: Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680  (Gelesen 1197 mal)

Offline JWRu

  • Full Member
  • ***
  • Beiträge: 378
Ich möchte von einem kleinen Projekt berichten, das ich gerade fertiggestellt habe:
Einen Luftqualitätssensor basierend auf den Sensoren MH-Z14A und BME 680.
Die Messwerte werden von einem Wemos D1 mini verarbeitet und per MQTT auf einen MQTT2_SERVER in FHEM gesendet.

Ich habe den Schaltplan als PDF und den Arduino-Sketch angehängt.
Mit dem Sketch hatte ich anfangs Probleme mit WDT-Resets. Nachdem ich an zwei Stellen "yield()" eingefügt habe, läuft er jetzt problemlos.

Das einzig nervende ist, dass der MH-Z14A nach dem Start mindestens 20 Minuten Reinluft mit ca. 400 ppm. CO2-Gehalt braucht, um sich zu kalibrieren.
Bisher habe ich das mit einem langen Kabel gelöst und lege den Sensor nach dem Einschalten außen auf die Fensterbank.
Jetzt habe ich mir mal so was bestellt: https://www.ebay.de/itm/5V-auf-4-7V-UPS-USV-18650-Li-ion-Batterie-Boost-Modul-Step-Up-Battery-Lademodul/264572893726?ssPageName=STRK%3AMEBIDX%3AIT&_trksid=p2060353.m1438.l2649.
Mit einer 18650 Li-Ion-Zelle kann ich den Sensor dann eventuell eine Zeitlang ohne Netz betreiben.
« Letzte Änderung: 26 November 2020, 14:39:12 von JWRu »
ZBox; RasPi 3B; RasPi Zero W; Homematic; Z-Wave; EnOcean, Shelly; DuoFern; Oregon-Sensoren; TFA-Sensoren; Steuerung Viessmann-Heizung; Arduinos für Strom-, Wasser-, Gaszähler, Rauchmelder und FI-Schutzschalter

Offline sbiermann

  • Full Member
  • ***
  • Beiträge: 442
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #1 am: 27 November 2020, 07:50:17 »
Bei unserer TTN-Freiburg Gruppe (The Thinksnetwork) wird ebenfalls fleißig an CO2-Ampeln in verschiedensten Ausführungen gearbeitet. Ich selber verwende den SCD-30, der ist echt Klasse, aber teuer und derzeit wohl nur schwer verfügbar. Die Kollegen verwenden jetzt im Moment den MH-Z19B oder SCD-30 und einer produziert derzeit eine Kleinserie (ca. 30€ pro Stück), dessen Bilder habe ich hier angehängt.

Als Spannungsversorgung wird bei der Kleinserie eine 18650 Zelle eingesetzt. Nach dem was ich gehört habe reicht die aus um locker ein 6 Stunden Meeting hin zu bekommen. Ich vermute mal man muss die alle 1-2 Tage aufladen, wenn das Gerät permanent laufen soll. Bei mir wird einfach ein USB Netzteil vom Handy verwendet, für meinen Einsatzzweck im Klassenzimmer ausreichend.

Einen Unterschied zu deinen Projekt gibt es noch, wir verwenden den ESP32 in verschiedenen Versionen. Dieser ist gefühlt wesentlich stromsparender als der ESP8266.

Offline JWRu

  • Full Member
  • ***
  • Beiträge: 378
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #2 am: 27 November 2020, 12:09:36 »
Das sieht ja super aus - eine richtige Massenproduktion.
ZBox; RasPi 3B; RasPi Zero W; Homematic; Z-Wave; EnOcean, Shelly; DuoFern; Oregon-Sensoren; TFA-Sensoren; Steuerung Viessmann-Heizung; Arduinos für Strom-, Wasser-, Gaszähler, Rauchmelder und FI-Schutzschalter

Offline mumpitzstuff

  • Developer
  • Hero Member
  • ****
  • Beiträge: 1922
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #3 am: 27 November 2020, 12:44:59 »
Wie sind denn eure Erfahrungen bzgl. der Kalibrierung? Ich meine nicht die Anfangskalibrierung, sondern eher wie sich der Sensor nach einem längerem Betrieb verhält z.B. im Büro. Der MH-Z19 hat eine Autokalibrierung, die man ab- oder anschalten kann. Was ist hier sinnvoll?
Der Hintergrund meiner Frage ist, das ich eine solche Ampel im Büro aufstellen möchte und dort nicht alle paar Tage das Ding für x Minuten aus dem Fenster hängen und neu kalibrieren kann (beim MH-Z19 braucht das 20min). Im Büro ist auch am Wochenende niemand da, also 2 Tage pro Woche wird dort niemand lüften können.

PS: Den SCD30 hätte ich gern gekauft (SCD40 gibts wohl auch bald), aber 40-50€ für den Sensor sind entschieden zu viel (selbst aus China).
« Letzte Änderung: 27 November 2020, 12:47:32 von mumpitzstuff »

Offline buec65

  • Jr. Member
  • **
  • Beiträge: 94
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #4 am: 27 November 2020, 14:27:28 »
@sbiermann besteht die Möglichkeit so ein Teil oder einen Sensor zu kaufen?

Online Frank_Huber

  • Hero Member
  • *****
  • Beiträge: 4173
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #5 am: 27 November 2020, 14:37:16 »
@sbiermann besteht die Möglichkeit so ein Teil oder einen Sensor zu kaufen?
+1 für einKomplettmodul. ;-)

Offline herrmannj

  • Global Moderator
  • Hero Member
  • ****
  • Beiträge: 5730
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #6 am: 27 November 2020, 14:53:16 »
@mumpitzstuff

Hängt von der Anwendung ab.

Einfache Anwendungen, wie die derzeit in Mode gekommenen Luftampeln, ABC durchaus eingeschaltet lassen. Man bekommt dann aber ein "Schätzeisen" mit Abweichungen von (bis zu) einigen hundert ppm.
 "Ernsthafte" Anwendungen, wenn der Co2 Sensor zB Beispiel Teil einer Regelung ist,  -> abschalten!

Der MH-Z19B erwartet _mindestens_ 20 Min Clean Air, unter stabilen Bedingungen. Bei stationär im Innenraum betriebenen Device ist das, speziell im Winter praktisch unmöglich. Mindestens jedoch absolut unpraktikabel.

Die ABC eines MH-Z19B nimmt den jeweils tiefsten Wert im Zeitraum von 24h als 400ppm Referenz. Im Fall einer Lüftungsanlage mit einen sportlichen Zielwert von 600ppm führt das zu einer positiven Rückkopplung. Je nachdem wie die Regelung ausgelegt ist kannst Du davon ausgehen, dass sich der Fehler innerhalb von Tagen auf mehrere hundert ppm aufaddiert.

Warum muss überhaupt geeicht werden?

Der MH-Z19B ist ein NDIR Sensor. Co2 absorbiert (ist undurchlässig) bei speziellen Wellenlängen (4.26 µm / IR). In dem Sensor befindet sich eine IR Quelle (hier "normale" Glühlampe), ein Filter für 4.26 µm und dahinter ein Lichtsensor. Bei konstanter Strahlungsenergie kommt also weniger Licht beim Sensor an, je mehr Co2 Moleküle in der Messkammer sind.

Diese Messung wird beeinflusst von der Temperatur und dem Luftdruck (-> Dichte / Umwelt) sowie von Bauteiltoleranzen (statisch/dynamisch / Maschine). Die Temperatur wird beim MH-Z19B intern kompensiert. Bei konstanter Höhe über NN läßt sich die Luftdruckänderung (Wetter) vernachlässigen, oder wer mag kann die rausrechnen.

Bleiben: Luftdruck in Abhängigkeit der Höhe des Einsatzortes, produktionsbedingte Toleranzen und Alterung der Bauteile. Die ersten beiden werden mit der _sorgfältigen_ einmaligen Kalibrierung bei Inbetriebnahme ausreichen neutralisiert.

Der Einfluss der Alterung ist leider deutlich aufwendiger zu kompensieren. Das Hauptelement hier ist die Glühlampe. Glühlampen altern da sich einzelne Atome vom erhitzen Glühfaden ablösen. Einige Fallen auf den Faden zurück, andere lagern sich auf der Innenseite des Glaskolbens ab und verdunkeln ihn. In der Summe kommt über die Zeit also "weniger Licht" aus der Lampe. Der Sensor misst also einen höheren Co2 Anteil als tatsächlich vorhanden.

Zumindest ich habe keine soliden Quellen gefunden um den Effekt ausreichend genau zu bestimmen. Für Halogenlampen habe ich Daten gesehen, nach denen sich die Helligkeit nach ca 10k Betriebsstunden halbiert. Dies entspräche ca 14 Monaten Dauerbetrieb.

Dies kann man imho jedoch _nicht_ auf den MH-Z19B übertragen. Erstens ist unklar ob diese Zahlen überhaupt valide sind. Selbst wenn: der MH-Z19B pulst seine Lampe ja nur und diese Zeit teil sich in "Faden wird geheizt" und "ist heiß". Da ohnehin IR Licht benötigt wird, vermute(!) ich, dass die Lampe mit deutlicher Unterspannung (gemessen an Ihren Specs) betrieben wird, sprich deutlich "kälter" als das Material vertragen würde.

Persönlich eiche ich etwa jährlich einmal (oder so;)). "Gefühlt" würde ich einen Drift im Bereich von unter 10% jährlich erwarten, bei 5% Messgenauigkeit kein "Big Deal".

Winsen (der Hersteller) bietet ja im Datenblatt an, Fragen zum Betrieb mit abgeschalteter ABC zu beantworten. Mir hat bisher die Zeit (Muße) dazu gefehlt. Im Prinzip müsste man wissen welchen Drift man, sagen wir bei 800ppm, statistisch im Jahr zu erwarten hat. Diesen dann rausrechnen. Würde vermutlich den besten Co2 Sensor ergeben, den man in diesem Preisbereich (exklusive Laborgeräte) bekommen kann.

Falls jemand diese Daten bekommt kann ich die zur Kompensation verwenden. Die Lib würde ich dann zur Verfügung stellen.

vg
Joerg
smartVisu mit fronthem, einiges an HM, RFXTRX, Oregon, CUL, Homeeasy, ganz viele LED + Diverse
Informativ Informativ x 3 Liste anzeigen

Offline JWRu

  • Full Member
  • ***
  • Beiträge: 378
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #7 am: 27 November 2020, 15:04:30 »
Ich habe die automatische Kalibrierung abgeschaltet.
Der Sensor zeigt einigermaßen vernünftige Messwerte. Bei "verbrauchter" Luft bis ca. 1500 ppm. Wenn man kräftig lüftet, geht er wieder runter auf ca. 400 ppm
Allerdings habe ich keine Langzeiterfahrung damit.
Für den Preis kann man keine Wunder erwarten - es ist halt kein URAS.
ZBox; RasPi 3B; RasPi Zero W; Homematic; Z-Wave; EnOcean, Shelly; DuoFern; Oregon-Sensoren; TFA-Sensoren; Steuerung Viessmann-Heizung; Arduinos für Strom-, Wasser-, Gaszähler, Rauchmelder und FI-Schutzschalter

Offline MadMax-FHEM

  • Hero Member
  • *****
  • Beiträge: 9884
  • NIVEAu ist keine Creme...
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #8 am: 27 November 2020, 15:39:49 »
Interessanter Thread :)

Aktuell habe ich 4 (verteilt auf 2 Wohnungen) CO2-Messer per USB an einem PI (ZeroW) und per eigener Software in fhem.
(TFA Dostmann AirCO2ntrol Mini)

Allerdings gibt es die wohl nicht mehr zu kaufen :-\

Daher habe ich mich auch ein wenig mit CO2 etc. beschäftigt und bin darüber "gestolpert": https://www.youtube.com/watch?v=FL0L-nic9Vw

Dort werden auch diverse Sensoren verglichen...

Gruß, Joachim
FHEM PI3B+ Buster: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)
FHEM PI3 Stretch (Test)

Offline mumpitzstuff

  • Developer
  • Hero Member
  • ****
  • Beiträge: 1922
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #9 am: 27 November 2020, 22:27:13 »
Wow vielen Dank für die ausführliche Erklärung. Ich werde dann die Kalibrierung auch abschalten. Hatte schon vermutet, das es damit zu Problemen kommen würde. Wenn ich das Ding dann nur alle paar Monate für 20 Minuten aus dem Fenster hängen muss, kann ich damit leben. Auf jeden Fall werde ich den Prozess gleich im Programm hinterlegen, um es dann per Knopfdruck aktivieren zu können und nicht fummeln zu müssen.
Der MH-Z19 hat glaube ich ein automatisches Kalibrierungsintervall von nur 24h wenn ich richtig informiert bin. Beim SCD30 glaube ich was von 1 Woche gelesen zu haben, was für die Anwendung im Büro sicherlich zuträglicher ist.

Offline mumpitzstuff

  • Developer
  • Hero Member
  • ****
  • Beiträge: 1922
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #10 am: 27 November 2020, 22:34:46 »
Interessanter Thread :)

Aktuell habe ich 4 (verteilt auf 2 Wohnungen) CO2-Messer per USB an einem PI (ZeroW) und per eigener Software in fhem.
(TFA Dostmann AirCO2ntrol Mini)

Allerdings gibt es die wohl nicht mehr zu kaufen :-\

Daher habe ich mich auch ein wenig mit CO2 etc. beschäftigt und bin darüber "gestolpert": https://www.youtube.com/watch?v=FL0L-nic9Vw

Dort werden auch diverse Sensoren verglichen...

Gruß, Joachim

Das Video ist super, insbesondere der Sprecher ist cool...

Offline MadMax-FHEM

  • Hero Member
  • *****
  • Beiträge: 9884
  • NIVEAu ist keine Creme...
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #11 am: 27 November 2020, 22:42:08 »
Das Video ist super, insbesondere der Sprecher ist cool...

;)

Und von dem gibt's noch mehr interessante Dinge...

Gruß, Joachim
FHEM PI3B+ Buster: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)
FHEM PI3 Stretch (Test)

Offline JWRu

  • Full Member
  • ***
  • Beiträge: 378
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #12 am: 05 Dezember 2020, 13:33:06 »
Kleines Update: Die Mini-USV mit der 18650 Li-Ion-Zelle funktioniert ganz gut.
Ich kann den Sensor jetzt ein paar Stunden ohne 5V-Versorgung betreiben.
ZBox; RasPi 3B; RasPi Zero W; Homematic; Z-Wave; EnOcean, Shelly; DuoFern; Oregon-Sensoren; TFA-Sensoren; Steuerung Viessmann-Heizung; Arduinos für Strom-, Wasser-, Gaszähler, Rauchmelder und FI-Schutzschalter

Offline Prof. Dr. Peter Henning

  • Developer
  • Hero Member
  • ****
  • Beiträge: 7810
Antw:Luftqualitätssensor mit Wemos D1 mini, MH-Z14A und BME 680
« Antwort #13 am: 24 Dezember 2020, 12:39:20 »
Ich habe das jetzt mit einem MHZ19B umgesetzt,  einem DHT22B für Temperatur und Feuchte, einem CCS811 für den Mief-Faktor, einem 1,8 Zoll OLED Display und der Ausgabe über eine Neopixel LED (derzeit noch als Ring - sieht albern aus und fliegt wieder raus.).

Interaktion mit FHEM über MQTT. Mit einem einfachen Publish /<devicetopic>/commands/calibrate kann derzeit die Kalibrierung des MHZ19B gestartet werden.

Farbskala für die CO2-Pixel à la pahColor. Den CCS811 hatte ich noch herumliegen, ist ziemlich ungenau. Das liegt vermutlich an der blöden Adafruit-Bibliothek dafür.


Anbei der Code, vielleicht kann jemand etwas Nützliches heraussaugen.

LG

pah


/*----------------------------------------------------------------------------------

  HenningHome

  CO2-Sensor mit Display und MQTT

  Code für ESPDuino

  Prof. Dr. Peter A. Henning, December 2020

  ------------------------------------------------------------------------------------*/
//#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include "MHZ19.h"
#include "DHT.h"
#include "Adafruit_CCS811.h"
#include "Adafruit_NeoPixel.h"
#include <SoftwareSerial.h>               // Remove if using HardwareSerial or Arduino package without SoftwareSerial support
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"

/*-- Definitions for WLAN --*/
#define WLAN_SSID       "xxx"
#define WLAN_PASS       "xxx"
WiFiClient client;

/*-- Definitions for MQTT --*/
#define MQTT_SERVER      "192.168.0.yy"
#define MQTT_SERVERPORT  1883             // use 8883 for SSL
#define MQTT_USERNAME    "xxx"
#define MQTT_PW          "yyy"
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, MQTT_SERVERPORT, MQTT_USERNAME, MQTT_PW); // Setup the MQTT client class
Adafruit_MQTT_Publish   dat = Adafruit_MQTT_Publish(  &mqtt, "/DVES_251C52/data");
Adafruit_MQTT_Subscribe cmd = Adafruit_MQTT_Subscribe(&mqtt, "/DVES_251C52/cmd");
Adafruit_MQTT_Subscribe *subscription;

void MQTT_connect();                      // Bug workaround for Arduino 1.6.6

/*-- Definitions for CO2 Sensor MHZ19 --*/
#define RX_PIN 3                          // RxD 3 = Rx NodeMCU, Rx pin which the MHZ19 Tx pin is attached to
#define TX_PIN 1                          // TxD 1 = Tx NodeMCU, Tx pin which the MHZ19 Rx pin is attached to
#define CAL_PIN 12                        // 12 = D6 NodeMCU
#define BAUDRATE 9600                     // Device to MH-Z19 Serial baudrate (should not be changed)
SoftwareSerial mySerial(RX_PIN, TX_PIN);  // (Uno example) create device to MH-Z19 serial
//HardwareSerial mySerial(1);             // (ESP32 Example) create device to MH-Z19 serial
MHZ19 myMHZ19;

/*-- Definitions for humidity sensor DHT22 */
#define DHTPIN 2                          // 2 = D4 NodeMCU
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);

/*-- Definitions of CCS811*/
Adafruit_CCS811 ccs;

/*-- Definitions for OLED display --*/
#define I2C_ADDRESS 0x3C                  // 0X3C+SA0 - 0x3C or 0x3D
SSD1306AsciiWire oled;
uint8_t col[5];                           // Columns for data values.
uint8_t rows;                             // Rows per line.

/*-- Definitions for NeoPixel Display */
#define LED_PIN   14                      //14 = D5 NodeMCU       
#define LED_COUNT 24
Adafruit_NeoPixel pixel(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

/*-- Timing --*/
unsigned long getDataTimer = 0;
unsigned long getMQTTTimer = 0;

/*-- Data values */
int CO2;
int8_t Temp;
float Hum;
float Temp2;
int Tvoc;
char payload[50];
String payloadstr;
String cmdstr;

void setup()
{
  //Setup MHZ19
  pinMode(CAL_PIN, OUTPUT);
  digitalWrite(CAL_PIN, HIGH);
  mySerial.begin(BAUDRATE);
  myMHZ19.begin(mySerial);
  myMHZ19.autoCalibration(false);         // Turn auto calibration OFF (ON autoCalibration())
  delay(1000);

  //Setup DHT22
  dht.begin();

  //Setup CCS811
  ccs.begin();

  //Setup OLED display
  Wire.begin();
  Wire.setClock(400000L);
  oled.begin(&Adafruit128x64, I2C_ADDRESS);
  oled.setFont(Verdana12_bold);
  oled.clear();
  col[0] = oled.fieldWidth(strlen("CO2 "));  // Calculate columns for values.
  col[1] = oled.fieldWidth(strlen("T   "));
  col[2] = oled.fieldWidth(strlen("rH  "));
  col[3] = oled.fieldWidth(strlen("TVOC"));
  col[4] = oled.fieldWidth(strlen("TVOC 0 ppb"));
  rows = oled.fontRows();
  delay(10);

  //Setup NeoPixel display
  pixel.begin();           // INITIALIZE NeoPixel strip object (REQUIRED)
  pixel.show();            // Turn OFF all pixels ASAP
  pixel.setBrightness(10); // Set BRIGHTNESS to about 1/5 (max = 255)

  //Setup WLAN
  oled.println("Connecting to ");
  oled.println(WLAN_SSID);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    oled.print(".");
  }
  oled.println("connected");
  oled.println(WiFi.localIP());

  // Setup MQTT subscription for commands
  mqtt.subscribe(&cmd);

  //Clean OLED display
  delay(3000);
  oled.clear();
  oled.println("T   ");
  oled.println("rH  ");
  oled.println("CO2 ");
  oled.println("TVOC");
}

void calibrate()
{
  digitalWrite(CAL_PIN, LOW);
  delay(7000);
  digitalWrite(CAL_PIN, HIGH);
}

int co2R;
int co2G;
int co2B;
float u;
int limhum;
int limtvoc;

void CO2color()
{
  const int startR =   0;
  const int startG = 255;
  const int startB =  0;

  const int mid1R = 125;
  const int mid1G = 100;
  const int mid1B = 65;

  const int mid2R = 255;
  const int mid2G = 255;
  const int mid2B =  60;

  const int mid3R = 160;
  const int mid3G = 128;
  const int mid3B =  10;

  const int endR = 255;
  const int endG =   0;
  const int endB =   0;

  if ( CO2 <= 400 ) {
    co2R = startR;
    co2G = startG;
    co2B = startB;
  } else if ( CO2 >= 1250 ) {
    co2R = endR;
    co2G = endG;
    co2B = endB;
  } else if ( CO2 < 825 ) {
    u = ((float)CO2 - 400) / (825. - 400.);
    co2R = int(sq(1 - u) * startR + 2 * u * (1 - u) * mid1R + sq(u) * mid2R);
    co2G = int(sq(1 - u) * startG + 2 * u * (1 - u) * mid1G + sq(u) * mid2G);
    co2B = int(sq(1 - u) * startB + 2 * u * (1 - u) * mid1B + sq(u) * mid2B);
    //Hum = u;
    //Tvoc = co2R;
    co2R = 128;
    co2G = 255;
    co2B = 0;
  } else {
    u = ((float)CO2 - 825) / (1250. - 825.);
    co2R = int(sq(1 - u) * mid2R + 2 * u * (1 - u) * mid3R + sq(u) * endR);
    co2G = int(sq(1 - u) * mid2G + 2 * u * (1 - u) * mid3G + sq(u) * endG);
    co2B = int(sq(1 - u) * mid2B + 2 * u * (1 - u) * mid3B + sq(u) * endB);
    //Hum=u;
   //Tvoc = co2R;
    co2R = 255;
    co2G = 255;
    co2B = 0;
  }
}

void MQTT_read()
{
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();
  while ((subscription = mqtt.readSubscription(500))) {
    if (subscription == &cmd) {
      oled.clearField(0, 3 * rows + rows / 4, 16);
      oled.print("Cmd ");
      oled.println((char *)cmd.lastread);
      cmdstr = (char *)cmd.lastread;
      if (  cmdstr == "calibrate") {
        calibrate();
      }
    }
  }
}

void MQTT_write()
{
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();

  payloadstr = "{\"CO2\":" + (String)CO2 + ",\"temperature\":" + (String)Temp2 +
               ",\"humidity\":" + (String)Hum + ",\"TVOC\":" + (String)Tvoc + "}";
  payloadstr.toCharArray(payload, payloadstr.length() + 1);

  oled.clearField(col[4], 3 * rows + rows / 4, 3);
  if (! dat.publish(payload))
    oled.println("ERR");
  else {
    oled.println("TX");
  }

}

void loop()
{
  /*-- Get data and display */
  if (millis() - getDataTimer >= 2000)
  {
    /* note: getCO2() default is command "CO2 Unlimited". This returns the correct CO2 reading even
      if below background CO2 levels or above range (useful to validate sensor). You can use the
      usual documented command with getCO2(false) */

    CO2 = myMHZ19.getCO2();                             // Request CO2 (as ppm)
    //Temp = myMHZ19.getTemperature();                  // Request Temperature (as Celsius)
    Hum = dht.readHumidity();                           // Request humidity from DHT22
    Temp2 = dht.readTemperature();                      // Request Tvoc from CCS8811
    if (ccs.available()) {
      if (!ccs.readData()) {
        Tvoc = ccs.getTVOC();
      }
    }

    //Output to OLED display
    oled.clearField(col[1], rows / 4, 4);
    oled.print((String)Temp2 + " C");
    oled.clearField(col[2], rows + rows / 4, 4);
    oled.print((String)Hum + " %");
    oled.clearField(col[0], 2 * rows + rows / 4, 8);
    oled.print((String)CO2 + " ppm");
    oled.clearField(0, 3 * rows + rows / 4, 5);
    oled.print("TVOC");
    oled.clearField(col[3], 3 * rows + rows / 4, 8);
    oled.print((String)Tvoc + " ppb");


    // Output to NeoPixel display
    CO2color();
    for (int i = 0; i < 12; i++) {
      pixel.setPixelColor(i, pixel.Color(co2R,   co2G,   co2B));
    }
    if(Hum> 70){
      limhum=2;
      co2R=0;
      co2G=0;
      co2B=255;
    } else if (Hum> 60){
      limhum=1;
      co2R=0;
      co2G=0;
      co2B=150;
    } else if (Hum> 50){
      limhum=0;
      co2R=0;
      co2G=0;
      co2B=50;
    } else if (Hum>40){
      limhum=1;
      co2R=75;
      co2G=0;
      co2B=150;
    } else {
      limhum=2;
      co2R=255;
      co2G=0;
      co2B=255;
    }

    for (int i = 15-limhum; i<16+limhum; i++) {
      pixel.setPixelColor(i, pixel.Color(co2R,   co2G,   co2B));
    }
    if(Tvoc> 500){
      limhum=2;
      co2R=255;
      co2G=0;
      co2B=255;
    } else if (Tvoc>100){
      limhum=1;
      co2R=100;
      co2G=100;
      co2B=0;
    } else {
      limhum=0;
      co2R=50;
      co2G=00;
      co2B=50;
      }
    for (int i = 20-limhum; i<21+limhum; i++) {
       pixel.setPixelColor(i, pixel.Color(co2R,   co2G,   co2B));
    }
    pixel.show();

    MQTT_read();

    getDataTimer = millis();
  }

  /*-- publish to MQTT */
  if (millis() - getMQTTTimer >= 30000)
  {
    MQTT_write();
    getMQTTTimer = millis();
  }
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }
  oled.clearField(0, 3 * rows + rows / 4, 16);
  oled.print("Connecting MQTT ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    oled.clearField(0, 3 * rows + rows / 4, 16);
    oled.println(mqtt.connectErrorString(ret));
    oled.println("Retrying MQTT in 5s");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
    retries--;
    if (retries == 0) {
      oled.clearField(0, 3 * rows + rows / 4, 16);
      oled.println("MQTT dead, reset !");
      // basically die and wait for WDT to reset me
      while (1);
    }
  }
  oled.clearField(0, 3 * rows + rows / 4, 16);
  oled.println("MQTT connected");
  delay(1000);
}