martes, 29 de diciembre de 2020

Control de qualitat de l'aire amb arduino i sensors CO2

 5 anys!!!!! ufff .. ja ni sabia entrar en aquest blog! ;)

En aquest 5 anys han passat prou coses i una de les més importants és que he canviat de centre de treball. Professionalment he guanyat molt ja que ara estic centrat en impartir docència a Cicles Formatius, cosa que em plena molt (abans era invent rere invent a vore què feia per innovar amb els nanos .. que també estava guai .. peò açò mola més ;)

Be, al lio ... farà un parell de mesos vaig començar a veure aparatets d'aquests que medeixen la qualitat de l'aire però que valen prou diners ... i mirant mirant vaig veure que amb un sensor i un arduino es podia fer alguna cosa i vaig pensar .. vaig a fer-ho.

Com que tinc un Arduino Uno,  em vaig posar a buscar sensors de CO2 i vaig trobar uns molt baratets .. i els vaig comprar. 


Prototip amb arduino uno i sensors MQ135

Únicament es necessita connectar la terra y Vcc al sensor i el pin de dades a una entrada analògica del arduino UNO. Com aquest esquema.


Aquest és el sensor MQ135. Té un problema important ... la calibració. Vaig usar la llibreria MQ135 
https://github.com/GeorgK/MQ135

Però els valors que donava no eren molt coherents ... vaig buscar i buscar i hi havia que, en el codi, variar una constant denominada RZERO

/// Calibration resistance at atmospheric CO2 level
#define RZERO 7300

I per a trobar el valor hi havia que posar aquesta constant a 1 i deixar 24 hores funcionant el sensor. Per consola es treia el valor del rzero .. i quan passaven eixes 24h el valor que es retornava (a poc a poc anava ajustant-se i tendint a un valor concret) era el valor que hi havia que posar en la constant. Compilar de nou i ja funcionava bé el sensor.

Codi utilitzat per trobar el valor de RZERO:


void setup()
{
pinMode(ANALOGPIN, INPUT);
Serial.begin(115200);
float rzero = gasSensor.getRZero();
delay(3000);
Serial.print("MQ135 RZERO Calibration Value : ");
Serial.println(rzero);
}

void loop() {

float val = analogRead(ANALOGPIN);
float res= gasSensor.getResistance();
float ppm = gasSensor.getPPM();
float rzero = gasSensor.getRZero();
delay(3000);
Serial.print(" VAL: ");
Serial.print(val);
Serial.print(" RES: ");
Serial.print(res);
Serial.print("RZERO : ");
Serial.println(rzero);
}



Dades al núvol

El següent pas era que en lloc de tindre un arduino UNO, fer-ho amb un altre controlador que tinguera connectivitat (el meu cunyat XAU va ser el que em va donar la idea) .. i vaig comprar aquest controlador ESP32 amb mòdul WIFI.


Tot igual que amb el UNO però ara les dades en lloc de mostrar-les al Serial o a un LED, podia enviar-les al núvol. I així ho vaig fer .. vaig programar un petit php que obté les dades i les emmagatzema en una base de dades (després veurem el resultat)

https.begin(*client,peticion);
int httpCode = https.GET();
Serial.print("GET http code: ");
Serial.println(httpCode);
if(httpCode != HTTP_CODE_OK) {
    Serial.print("error en la peticion: ");
    Serial.println(peticion);
    String resa = "[HTTP] GET... failed, error: ";
    resa = resa + https.errorToString(httpCode).c_str();
    Serial.println(resa);
}
else{
    Serial.print("OK : ");
    Serial.println(peticion);
}
https.end();


Més o menys la petició https es realitza amb aquest codi.

Les medicions les fa cada 5 segons i treia una mitjana cada minut i això és el que s'envia a la petició HTTPS per a que ho emmagatzeme a la base de dades.

Problema de medicions

Vaig començar a controlar les medicions i variaven moltíssim ... de vegades 450ppm (lo normal són 400 a l'aire lliure) i d'altres donava 30000... UUUppppsss! Algo no funciona massa bé ... 

Buscant buscant .. el sensor alimenta a 5V i aquest ESP32 va a 3.3V però té una eixida de 5V (que és la que li connecte a l'alimentació del sensor). Potser això està fent que els valors no siguen bons. Vaig intentar posar una resistència entre sensor i Arduino, i res. 


Vaig posar un convertidor de senyal de 5v a 3.3v (comprat per amazon) El que fa aquest aparatet és que alimentes a una banda 5v a altra 3.3v i les senyals a 5v les converteix a 3.3 i viceversa.

 

Res de res ... que no prenia valors bons ... Vaig canviar de tàctica .. vaig buscar
els sensors de qualitat d'aire comercials i vaig veure que utilitzaven un sensor
de gas digital .. i vaig veure que hi havien un poc més cars que els MQ135
però que hi havia que provar-ho ...

Ale ... que Amazon ha tingut un client VIP per fer aquest projecte :D

Aquests són els sensors CCS811 que al final he usat en lloc dels MQ135: 


La diferència és que les dades venen pels pins SDA i SCL. El Vcc va a 3.3v,
el GND a terra i per a que funcione, el WAK també a terra. I el prototip  va
quedar així:


El programa per a capturar les dades a arduino és aquest:

void connectWifi(){
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
int cont =0;
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
cont++;

if(cont==60) break;
}
if(cont==60){
disconnectWifi();
Serial.println("NOT CONNECTED");
}
else
Serial.println(" CONNECTED");
}
void disconnectWifi(){
//disconnect WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}

void setup()
{
Serial.begin(9600);
Serial.println("CCS811...");

if(!ccs.begin()){
Serial.println("Failed to start sensor! Please check your wiring.");
}else{

// Sets the mode to read data each second
Serial.println("DRIVE MODE TO 1 sec.");

ccs.setDriveMode(CCS811_DRIVE_MODE_1SEC);

// Wait for the sensor to be ready
Serial.println("Waiting for the sensor to be ready ...");

while(!ccs.available());
temp = ccs.calculateTemperature();
ccs.setTempOffset(temp - 25.0);

Serial.println("CCS811 Sensor OK");
delay(3000);
connectWifi();

}

}

void loop()
{
Serial.print("Mem free: ");
Serial.println(ESP.getFreeHeap());
muestras++;

if(ccs.available()){
if(!ccs.readData()){
ppm = ccs.geteCO2();
Serial.print("CO2: ");
Serial.print(ppm);
Serial.print(" ppm, TVOC: ");
temp = ccs.getTVOC();
Serial.println(temp);
}
else{
Serial.println("ERROR!");
}
}

media_valor += ppm;
if(muestras == MAX_MUESTRAS){

media_valor= media_valor / MAX_MUESTRAS;
// Si la media de los valores tomados en 1 minuto cada 5 seg supera la zona
// crítica en 3 muestras, se reinicia el sensor
if(media_valor > ZONACRITICA){
valores_criticos++;
}
else{
valores_criticos=0;
}


if(valores_criticos == MAX_VALORES_CRITICOS){
// Se resetea el ESP32 y se esperan 10 segundos
ESP.restart();
delay(10000);
}
else{
// Se calcula la media de MAX_MUESTRAS tomadas
// Se envia


client = new WiFiClientSecure;
if(client) {
client -> setCACert(rootCACertificate);
{

peticion = "https://localhost/mtm_sensor.php?aula=";
peticion = peticion + AULA;
peticion = peticion + "&valor=";
peticion = peticion + media_valor;

https.begin(*client,peticion);
httpCode = https.GET();
Serial.print("GET http code: ");
Serial.println(httpCode);
if(httpCode != HTTP_CODE_OK) {
Serial.print("error en la peticion: ");
Serial.println(peticion);
resa = "[HTTP] GET... failed, error: ";
resa = resa + https.errorToString(httpCode).c_str();
Serial.println(resa);

https.end();
// Si no conecta bien, desconectamos wifi y volvemos a conectar
delay(3000);
disconnectWifi();
delay(5000);
connectWifi();
}
else{
Serial.print("OK : ");
Serial.println(peticion);
https.end();
}
}
client->stop();
delete client;
} else {
Serial.println("Unable to create client");
disconnectWifi();
delay(5000);
connectWifi();
}
media_valor = 0;
muestras=0;

}
}
delay(5000);
}

Què fa aquest codi?

Quan s'encén posa en marxa el sensor i conecta la Wifi amb un SSID i passwd.
Cada 5 segons agafa mostra de valor i cada minut calcula la mitjana dels valors.
Aquesta mitjana és la que s'envia al servidor i s'emmagatzema les dades corresponents.
A més del valor del sensor, un num. d'aula i un timestamp.

Errors detectats i com s'han solucionat?

ESP32 sense memòria.


Com que creava les variables locals a la funció LOOP i aquesta funció es crida contínuament,
el ESP32 arribava un moment que es quedava sense memòria. El que s'ha fet és
posar TOTES les variables GLOBALS i així no es gasta la memòria. A més el client
HTTP s'elimina cada vegada perque es crea cada volta que és necessària una petició
HTTP.

Petició errònia

De vegades la petició HTTP no funciona correctament (no m'ho pregunteu que no ho sé).
El que faig és desconectar la wifi, esperar i tornar a connectar. Així si hi ha algún problema,
reconnecta i au!.

Valors estranys.... coses de sensors.

De sobte va tot bé i el sensor munta cap a 2000ppm ... on anem????? ... solució un poc
"chapuzas" però que vull acabar amb açò ja ... tinc un valor màxim ZONACRITICA que
la tinc a 1600 i si 3 valors consecutius donen per damunt d'aquest valor, reinicie
el ESP32 .. no se perquè se li va la olla .. però al fer açò si supera els 1600 durant 3
medicions seguides, reinicia i torna a medir bé.

Disseny final:

Com ja funcionava, el vaig fer un poc millor (sols un poc) perquè no vaig voler posar-me amb la creació
de la placa PCB... així ha quedat:




I els resultats:

Vaig fer una pagineta amb el CanvasJS que trau les gràfiques i aquestes són d'aquest tipus:


A més, vaig fer altra pagineta que indicava la mitjana del valor dels últims 5 minuts.
Pinta un semàfor del color verd, groc o vermell depenent del valor deCO2 ppm de l'aire.

És aquesta:


CAL OBRIR FINESTREEEEESSSS !!!!!


Bé i açò és tot .. per si a algú li serveix d'algo .. per a fer algún projectet o alguna coseta ..
Aci teniu els codis:


Un saludet a tots i cuide-vos molt!

Antonio Coloma.