Compartir

Una vez ensamblado el medidor de CO2 la siguiente fase es la de programación. Para programar este medidor utilizaremos el entorno de desarrollo de Arduino IDE. Además necesitaremos un cable USB tipo B para: la alimentación del medidor de CO2 mediante el cable USB, la comunicación desde el ordenador hacia el microcontrolador Arduino y la comunicación desde el Arduino hacia el ordenador a través del puerto serie para enviar datos de lecturas del medidor.

Antes de meternos de lleno en la programación daremos algunos datos del sensor de CO2. El sensor de CO2 MH-Z14A es un sensor que mide la calidad del aire. Utiliza una tecnología llamada NDIR (Non-Dispersive InfraRed sensor) que detecta la concentración de este gas empleando radiación infrarroja y observa cuanto ha absorbido el gas. Tiene una tensión de funcionamiento entre 4.5 y 5.5 V. Dispone de salida PWM y voltaje analógico. Mide desde 0 a 5.000 ppm (partes por millón), es ligero, preciso y tiene una vida útil de más de 5 años. Tiene cinco comandos de funcionamiento de los que solamente hemos necesitado dos: calibración (código 87) y concentración de gas (código 86).

¿Y Cómo funciona el sensor?

Básicamente lo que está haciendo es comunicarse con Arduino constantemente a través del pin TX y RX para el envío y recepción de datos. El dato que se envía es un array de 9 Bytes, donde el Byte0 es el Byte de inicio y tiene valor 0xFF, el Byte1 es un Byte reservado, el Byte2 es el Byte de comando y tomará un valor 0x86 si se envía un comando de lectura de concentración de gas o 0x87 si se envía el comando para la calibración. El resto de los Bytes, se enviarán con valor cero (0x00) salvo el último Byte que será de checksum (0x79). Una vez realizada la lectura, el sensor devuelve otro array de Bytes donde: el Byte0 es el Byte de inicio con valor 0xFF, el Byte1 será el Byte del comando ejecutado: 0x86 para la lectura o 0x87 para la calibración. El valor de concentración se devuelven en dos Bytes: el Byte alto (HIGH) y el Byte bajo (LOW) que corresponde con los Byte2 y Byte3 respectivamente. El resto de Bytes devolverán valor cero (0x00) salvo el último que será de checksum o suma de verificación para detectar posibles errores.

Ya tenemos los valores, pero ¿Cuál es el valor de la medición?

Los valores retornados del medidor se da en en notación hexadecimal y en dos Bytes. De esta forma, tenemos un valor en hexadecimal para el Byte alto (HIGH) y otro valor en hexadecimal para el Byte bajo (LOW). A continuación, se pasan los valores del Byte2 y Byte3 de hexadecimal a decimal y se calcula el valor de CO2 utilizando la fórmula:

CO2 = HIGH * 256 + LOW

Veamos todo con un ejemplo:

  1. Se envía un comando de lectura de concentración de CO2
    • FF 01 86 00 00 00 00 00 79
  2. Valor devuelto:
    • FF 86 02 20 00 00 00 00 58
  3. El valor del Byte alto (HIGH) es 02 (hexadecimal) y el valor del Byte bajo (LOW) es 20 (hexadecimal)
  4. Se convierten los valores de hexadecimal a decimal. HIGH sigue siendo 2 y LOW pasa a ser 32.
  5. Se aplica la fórmula anterior:
    • CO2 = 2 * 256 + 32 = 544 ppm
  6. El resultado se da en ppm (partes por millón)

Ya está un poco más claro, vamos a codificar:

En primer lugar, necesitaremos hacer algunas configuraciones previas:

  1. Descargar las librerías ELEGOO_GFX y ELEGOO_TFTLCD: https://drive.google.com/file/d/1vY-jdkMxjX-JYY_zgUtOtBXokfY8FG5e/view?usp=sharing
  2. Descomprimir los archivos descargados.
  3. Copiar las carpetas generadas a carpeta Arduino/libraries
  4. En Arduino IDE, seleccionar la placa Arduino Mega. Para ello clic en Herramientas -> Placa -> Arduino Mega or Mega 2560

Estructura del código:

El código fuente tiene dos partes principales: función de configuración setup() y función cíclica loop()

  • En la función setup() se inicializa el puerto serie y se llama a la función configuracion_setup(). Esta función se encarga de establecer los parámetros de la pantalla TFT y de mostrar la interfaz del medidor mostrando cada uno de los textos y dibujos que aparecerán por pantalla.
  • En la función loop() se llama la la función obtener_medida() que lee y calcula el valor de CO2 con la fórmula que se ha explicado antes. A continuación se muestra el dato por pantalla en formato de número entero y por último se dibuja un círculo verde si el valor leído va de 380 a 500, un círculo amarillo si va de 500 a 800 y rojo si es mayor a 800.

Para calibrar el medidor hay que introducir una línea en la función setup() que ejecute el comando de calibración. Esta línea es la siguiente y se puede ver en el código ya que está comentada: Serial1.write(zero,9);

Una vez hecho esto, se sitúa el medidor de CO2 al aire libre durante al menos 10 minutos. Durante este tiempo, la cifra que da el medidor no es correcta. A continuación, se elimina esta línea de código o se comenta y se vuelve a cargar el código, obteniendo una lectura correcta con una precisión de ± 50 ppm.

En clase se dedicó una sesión en la que se aportó al alumnado el código fuente y las librerías, se conectó el medidor de CO2 al equipo mediante un cable USB y se cargó el código fuente. El alumnado de 4º ESO B de la asignatura de Tecnología, participante en este proyecto, hizo algunas modificaciones en el código fuente para que apareciera un texto su nombre en la pantalla y un texto del curso al cual va destinado este medidor, se hizo la calibración y salimos al patio con unas Power Bank para alimentar el dispositivo mientras se estaba calibrando.

A continuación, dejamos el código fuente (aunque hay que depurarlo y mejorarlo)

/***************************************************
*  Proyecto CO2 con el sensor MH-Z14a que tiene un 
*  rango de 0-5000 PPM y es de tipo NDIR
* ****************************************************
*  Pantalla gráfica táctil TFT de Elegoo de 2.8" 
* ****************************************************/

#include <Elegoo_GFX.h>    // Core graphics library
#include <Elegoo_TFTLCD.h> // Hardware-specific library

#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

Elegoo_TFTLCD tft(LCD_CS, LCD_CD, LCD_WR, LCD_RD, LCD_RESET);

void configuracion_setup()
{  

    #ifdef USE_Elegoo_SHIELD_PINOUT
     // Serial.println(F("Using Elegoo 2.4\" TFT Arduino Shield Pinout"));
    #else
      //Serial.println(F("Using Elegoo 2.4\" TFT Breakout Board Pinout"));
    #endif

  tft.reset();
  uint16_t identifier = tft.readID();
   if(identifier == 0x9325) {
    //Serial.println(F("Found ILI9325 LCD driver"));
  } else if(identifier == 0x9328) {
   // Serial.println(F("Found ILI9328 LCD driver"));
  } else if(identifier == 0x4535) {
   // Serial.println(F("Found LGDP4535 LCD driver"));
  }else if(identifier == 0x7575) {
    //Serial.println(F("Found HX8347G LCD driver"));
  } else if(identifier == 0x9341) {
   // Serial.println(F("Found ILI9341 LCD driver"));
  } else if(identifier == 0x8357) {
    //Serial.println(F("Found HX8357D LCD driver"));
  } else if(identifier==0x0101)
  {     
      identifier=0x9341;
       //Serial.println(F("Found 0x9341 LCD driver"));
  }
  else if(identifier==0x1111)
  {     
      identifier=0x9328;
      //Serial.println(F("Found 0x9328 LCD driver"));
  }
  else {
        identifier=0x9328;  
  }
  
  tft.begin(identifier);  
  tft.fillScreen(BLACK);  
  tft.setCursor(0, 0);  
  tft.setTextColor(BLUE);  tft.setTextSize(3);
  tft.print(" PROYECTO CO");
  
  tft.setCursor(215, 10); 
  tft.setTextSize(2);
  tft.println("2");

  tft.setCursor(25, 30); 
  tft.setTextColor(BLUE);  tft.setTextSize(2);
  tft.println("IES MARIO LOPEZ");

  tft.setTextSize(2);
  tft.setCursor(35, 130); 
  tft.setTextColor(WHITE);  tft.setTextSize(2);
  tft.println("PRECALENTANDO");
  tft.setCursor(60, 160); 
  tft.setTextColor(WHITE);  tft.setTextSize(2);
  tft.println("ESPERE...");
  delay(1000);
  

  tft.setTextColor(BLUE);  tft.setTextSize(2);
  tft.setCursor(0, 280); 
  tft.print("Manuel Jimenez");  
  tft.setCursor(0, 300); 
  tft.print("Depto. Informatica");  

  tft.setTextSize(2);
  tft.setCursor(35, 130); 
  tft.setTextColor(BLACK);  tft.setTextSize(2);
  tft.println("PRECALENTANDO");
  tft.setCursor(60, 160); 
  tft.setTextColor(BLACK);  tft.setTextSize(2);
  tft.println("ESPERE...");

  tft.setTextColor(GREEN);  tft.setTextSize(2);
  tft.setCursor(0, 53); 
  tft.print("380-500");  

  tft.setTextColor(YELLOW);  tft.setTextSize(2);
  tft.setCursor(151, 53); 
  tft.print("500-800"); 

  tft.setTextColor(RED);  tft.setTextSize(2);
  tft.setCursor(70, 240); 
  tft.print("800-5000"); 
  delay(1000);
  
}

unsigned char leerCO2[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};     //Read the gas density command /Don't change the order
unsigned char zero[9]    = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78};     //Read the gas density command /Don't change the order
String cadena="";
String cadena_anterior="";

void setup() {

  //Inicailizamos el puerto serie para la comunicación
  Serial.begin(9600);
  Serial1.begin(9600);
  
  /* 
   * Para calibrar el medidor: 
   * 1. Elimine el comentario de la instrucción Serial1.write(zero,9); 
   * 2. Suba el código fuente a Arduino Mega
   * 3. Coloque el dispositivo al aire libre durante al menos 10 minutos
   * 4. Añada un comentario en la instrucción Serial1.write(zero,9); 
   * 5. Suba de nuevo el código fuente a Arduino Mega
   */
   
  //Serial1.write(zero,9); 
  configuracion_setup();
}

int obtener_medida()
{
  Serial1.write(leerCO2,9);
  

  byte bufferCO2[9];

  if (Serial1.available()>0)
  {
    Serial1.readBytes(bufferCO2,9);
    int CO2 = bufferCO2[2] * 256 + bufferCO2[3];
    return CO2;
  }
}
  
    /*
     * 
     * Código de la versión 1
  for(int i=0;i<9;i++){
      
      if (Serial1.available()>0){
        
        int hi,lo,CO2;
        int ch=Serial1.read();
        
        if(i==2){
          hi=ch;   //lectura Byte alto (HIGH)
        }   
        
        if(i==3){
          lo=ch;   //lectura Byte bajo (LOW)
        }   
        
        if(i==8){
          CO2=hi*256+lo;  //Concentración de CO2
          return(CO2);
        }
     }   
*/

 


void muestro_pantalla(String valor)
{
  /* Estas dos líneas de código hacen que el arduino le envíe
   *  los datos al ordenador a través del puerto serie
   *  para poder capturar los datos por si se quieren llevar
   *  a una hoja de cálculo y representar una gráfica
  */
  Serial.print("CO2, "); // Envío el valor por el puerto serie para 
  Serial.println(valor); // realizar grafica en excel
  
  /* Las siguienes líneas de código muestran el dato en la pantalla TFT */
  tft.fillRect(0,120,240,52,BLACK);
  tft.setTextColor(WHITE);  tft.setTextSize(3);
  tft.setCursor(100, 180); 
  tft.print("PPM");  
  tft.setCursor(100, 93); 
  tft.print("CO2");   
  tft.setCursor(60, 130);   
  tft.setTextSize(5);
  tft.setTextColor(WHITE); 
  tft.print(valor); 
  delay(10000);
    
}

void borrar_circulo()
{
  tft.drawCircle(120, 150, 70, BLACK);
  tft.drawCircle(120, 150, 71, BLACK);
  tft.drawCircle(120, 150, 72, BLACK);
  tft.drawCircle(120, 150, 73, BLACK);
  tft.drawCircle(120, 150, 74, BLACK);

  
  tft.drawCircle(120, 150, 75, BLACK);  
  tft.drawCircle(120, 150, 76, BLACK);
  tft.drawCircle(120, 150, 77, BLACK); 
  tft.drawCircle(120, 150, 78, BLACK);   
}

void pintar_circulo(String color)
{
  borrar_circulo();
  delay(50);
  if(color=="rojo")
  {
  tft.drawCircle(120, 150, 70, RED);
  tft.drawCircle(120, 150, 71, RED);
  tft.drawCircle(120, 150, 72, RED);
  tft.drawCircle(120, 150, 73, RED);
  tft.drawCircle(120, 150, 74, RED);
  tft.drawCircle(120, 150, 75, RED);  
  tft.drawCircle(120, 150, 76, RED);
  tft.drawCircle(120, 150, 77, RED); 
  tft.drawCircle(120, 150, 78, RED);
  }
  
  if(color=="verde")
  {
    tft.drawCircle(120, 150, 70, GREEN);
    tft.drawCircle(120, 150, 71, GREEN);
    tft.drawCircle(120, 150, 72, GREEN);
    tft.drawCircle(120, 150, 73, GREEN);
    tft.drawCircle(120, 150, 74, GREEN);
    tft.drawCircle(120, 150, 75, GREEN);  
    tft.drawCircle(120, 150, 76, GREEN);
    tft.drawCircle(120, 150, 77, GREEN); 
    tft.drawCircle(120, 150, 78, GREEN);
    
    }
 if(color=="amarillo")
  {
    tft.drawCircle(120, 150, 70, YELLOW);
    tft.drawCircle(120, 150, 71, YELLOW);
    tft.drawCircle(120, 150, 72, YELLOW);
    tft.drawCircle(120, 150, 73, YELLOW);
    tft.drawCircle(120, 150, 74, YELLOW);
    tft.drawCircle(120, 150, 75, YELLOW);  
    tft.drawCircle(120, 150, 76, YELLOW);
    tft.drawCircle(120, 150, 77, YELLOW); 
    tft.drawCircle(120, 150, 78, YELLOW);    
    }    
}
 
void loop() {

  /* Variable que almacena la medición */
  int CO=0;
  
  CO=obtener_medida();
  cadena=String(CO);
  if(cadena!=cadena_anterior)
  {
     char dato[5];
     sprintf(dato,"%.4d",cadena.toInt());
     muestro_pantalla(dato);
  }
  
  cadena_anterior=cadena;
 
  if (CO>380){
    if (CO<500){
      pintar_circulo("verde");
    }
    else if (CO<800){
      pintar_circulo("amarillo");
    }
    else 
      pintar_circulo("rojo");
  }
}
Proyecto Medidor CO2 – Parte IV: «Programación»
Etiquetado en:                        

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *