Sensor de CO2 MH-Z19 que proporciona valores diferentes utilizando UART y PWM

0

Tengo un sensor de CO2 MH-Z19, según el hoja de datos Puedo obtener valores de PPM a través de UART y PWM. La diferencia entre ambos métodos es el límite en la concentración de CO2, donde UART I debería tener lecturas entre 0-5000ppm, y a través de PWM debería tener lecturas entre 0-2000ppm.

Solo quería saber si puedo tener las mismas lecturas utilizando ambos métodos, pero después de haberlo intentado, me he dado cuenta de que tengo valores diferentes.

Estoy usando el siguiente código para probar:

#include <SoftwareSerial.h>
SoftwareSerial mySerial(A0, A1); // RX, TX

byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
char response[9]; 

#define pwmPin 10
int prevVal = LOW;
long th, tl, h, l, ppm, ppm2 = 0.0;



void setup() {
    Serial.begin(9600); 
    mySerial.begin(9600); 
    pinMode(pwmPin, INPUT);
}

void loop(){
    mySerial.write(cmd,9);
    mySerial.readBytes(response, 9);
    int responseHigh = (int) response[2];
    int responseLow = (int) response[3];
    ppm = (256*responseHigh)+responseLow;


    //CO2 via pwm
    do {
        th = pulseIn(pwmPin, HIGH, 1004000) / 1000.0;
        tl = 1004 - th;
        ppm2 = 2000 * (th-2)/(th+tl-4);
    } while (ppm2 < 0.0);

    Serial.println(ppm);
    Serial.println(ppm2);
    Serial.println("-----------");
    delay(5000);
}

Las lecturas que estoy obteniendo son las falsas:

643
356
-----------
643
356
-----------

¿Alguna idea de lo que podría estar pasando aquí? ¿Debo sospechar que el sensor está dañado? O bien, estoy haciendo algo mal?

Gracias y saludos cordiales.

BTW: La prueba se realiza con un Arduino Pro Mini ATMega 328, 3.3v, 8Mhz. El sensor está siendo alimentado por 5v desde un NodeMCU (lolin).

    
pregunta crosvera

5 respuestas

1

Primero, gracias a @ChrisStratton por su corrección en los tipos de datos.

He cambiado mi código a lo siguiente:

#include <SoftwareSerial.h>
#define pwmPin 10
SoftwareSerial mySerial(A0, A1); // RX, TX

byte cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
unsigned char response[9]; 
unsigned long th, tl,ppm, ppm2, ppm3 = 0;



void setup() {
  Serial.begin(9600); 
  mySerial.begin(9600); 
  pinMode(pwmPin, INPUT);
}

void loop() {
  mySerial.write(cmd,9);
  mySerial.readBytes(response, 9);
  unsigned int responseHigh = (unsigned int) response[2];
  unsigned int responseLow = (unsigned int) response[3];
  ppm = (256*responseHigh)+responseLow;


  //CO2 via pwm
  do {
    th = pulseIn(pwmPin, HIGH, 1004000) / 1000;
    tl = 1004 - th;
    ppm2 = 2000 * (th-2)/(th+tl-4);
    ppm3 = 5000 * (th-2)/(th+tl-4);
  } while (th == 0);

  Serial.println(ppm);
  Serial.println(th);
  Serial.println(ppm2);
  Serial.println(ppm3);
  Serial.println("-----------");
  delay(5000);
}

Además del cambio en los tipos de datos, agregué una segunda forma de calcular la concentración de CO2 usando PWM ( ppm3 en el código), ahora usando 5000 ppm como límite de concentración, usando esto obtengo lecturas muy cercanas a las Lo obtuve de UART.

Estoy obteniendo las siguientes lecturas:

459  <- ppm (UART)
93   <- Milliseconds UART is HIGH
182  <- ppm2 (PWM) with 2000ppm as limit
455  <- ppm3 (PWM) with 5000ppm as limit
-----------
460
93
182
455
-----------
460
93
182
455
-----------

Parece que la hoja de datos , solo refuerzos el uso de PWM con un rango de 0-2000ppm como ejemplo, y no excluye el rango de 0-5000.

    
respondido por el crosvera
2
char response[9]; 

En muchos sistemas, incluido un Arduino, un no especificado char es un tipo firmado .

int responseHigh = (int) response[2];
int responseLow = (int) response[3];
ppm = (256*responseHigh)+responseLow;

Por lo tanto, cuando lo convierte en un int , cualquier valor con el conjunto de bits más alto se considerará negativo y el signo extendido a un valor negativo en el ancho entero entero. Entonces, por ejemplo, un char de 0x80 de 8 bits se convierte en un int 0xff81 de 16 bits. Probablemente esto no sea lo que desea, ya que realmente reducirá en lugar de aumentar el valor establecido por los bits superiores.

En cambio, como sus valores no están firmados, debe declarar su matriz como unsigned char o convertir los valores en un unsigned char antes que implícitamente o explícitamente los emita a un tipo más amplio.

Tenga en cuenta que incluso si el valor global estuviera firmado, probablemente querría interpretar los 8 bits inferiores como un modificador sin signo de los 8 bits superiores firmados.

Sin embargo, no es evidente de inmediato que este cambio resuelva la discrepancia en sus lecturas; en realidad puede aumentar.

  

th = pulseIn (pwmPin, HIGH, 1004000) / 1000.0;

No parece haber ninguna razón sólida para utilizar una constante de punto flotante al dividir un tipo entero para producir un resultado entero.

Probablemente debería imprimir los tiempos de PWM y ver si tienen sentido, en comparación con las mediciones de alcance.

Lo ideal sería que tuvieras algún otro sensor de referencia que pudieras usar para determinar qué valor es más creíble.

    
respondido por el Chris Stratton
1

Los estados de la hoja de datos, esa precisión de tiempo es del 5%. La modificación del código para medir el intervalo real de PWM produce exactamente el mismo resultado que la lectura de UART.

 void setup() {
  Serial.begin(9600); 
  mySerial.begin(9600); 
  pinMode(pwmPin, INPUT);
  th = pulseIn(pwmPin, HIGH, 3000000); // use microseconds
  tl = pulseIn(pwmPin, LOW, 3000000);
  tpwm = th + tl; // actual pulse width
  Serial.print("PWM-time: ");
  Serial.print(tpwm);
  Serial.println(" us");
  p1 = tpwm/502; // start pulse width
  p2 = tpwm/251; // start and end pulse width combined
}

y el cálculo

  th = pulseIn(pwmPin, HIGH, 3000000);
  ppm3 = 5000 * (th-p1)/(tpwm-p2);

Si tienes una versión de 0-2000 ppm, multiplica por 2000 en lugar de 5000. También puedes medir el ancho de pulso en cada ronda, si el tiempo varía con el tiempo.

    
respondido por el Ilpo
0

Las lecturas de UART al menos tienen sentido. Las concentraciones normales de CO2 en espacios abiertos deben ser de alrededor de 400 ppm, y una sala puede estar alrededor de 650 ppm.

Al calcular ppm2, ya que definió tl = 1004-th, creo que (th + tl-4) siempre será 1000, por lo que puede usar ppm2 = 2 (th-2)

Estoy seguro de que hay dos versiones de este sensor. Una lectura de hasta 2000 ppm y la otra de hasta 5000 ppm. Además, creo que hay una versión B del sensor que usa el comando 0x99 para cambiar el rango.

Sería una buena idea calibrar el sensor. Hazlo al aire libre sin que nadie se cierre.

También, use la suma de comprobación para verificar posibles errores de respuesta.

Sólo mis 2 centavos.

    
respondido por el Guillermo
-1

Cuando conecte el sensor a Arduino, notará que conecte TX de sensor con RX de Arduino y RX de sensor con TX de Arduino.

Mis 2 centavos.

    
respondido por el Roman

Lea otras preguntas en las etiquetas