Elly Tse
Published © MIT

CO2 monitoring with Wio terminal

Wio Terminal with a CO2 sensor to measure the indoor air quality and remind to ventilate the room.

BeginnerFull instructions provided1 hour479
CO2 monitoring with Wio terminal

Things used in this project

Hardware components

SenseCAP K1100 - The Sensor Prototype Kit with LoRa® and AI, Supports SenseCraft
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Connect sensor and Wio Terminal

Connect Grove VOC and eCO2 Gas Sensor (SGP30) with Wio Terminal

Code

Code

ActionScript
/****************************************************************************
**                                                                         **
** Name:        WioTerminal_CO2_traffic_light                              **
** Author:      Achim Kern                                                 **
** Interpreter: Arduino IDE 1.8.13                                         **
** Licence:     Freeware                                                   **
** Function:    measure air quality CO2 and show actual state on TFT       **
**                                                                         **
** Notes:       based on idea from SEEED STUDIO and LCARS SmartHome        **
**                                                                         **
** History:                                                                **
**                                                                         **
** 1.00        - 23.10.2020 - initial release                              **
**                          - GROVE SCD30                                  **
**                          - Buzzer                                       **
**                          - 3 Buttons                                    **
**                          - 5 status bmp pictures                        **
**                                                                         **
****************************************************************************/

/*
 * WIO Terminal TFT Display
 * 
 */
   // TFT library
   #include"TFT_eSPI.h"
   // TFT display
   TFT_eSPI tft;
   // Control Pin of LCD
   #define LCD_BACKLIGHT (72Ul)
   bool tft_backlight = true;

/*
 * libraries
 * 
 */
   #include <Arduino.h>
   // SD card library
   #include "Seeed_FS.h" 
   // image processing library
   #include "RawImage.h"
   // smooth fonts library
   #include "Free_Fonts.h"       

 /*
 * GROVE CO2 & Temperature & Humidity Sensor (SCD30)
 * -------------------------------------------------
 * The Grove - CO2 & Temperature & Humidity Sensor (SCD30) is a high precision carbon dioxide sensor, 
 * which is based on Sensirion SCD30. The measuring range of this sensor is 0 ppm-40'000 ppm, 
 * and the measurement accuracy can reach to (30 ppm + 3%) between 400ppm to 10'000ppm.
 * In addition to the Non-Dispersive Infrared(NDIR) measurement technology for CO2 detection, 
 * the SCD30 integrates Sensirion humidity and temperature sensors on the same sensor module. 
 * 
 */

   #include "SCD30.h"
   int grove_scd30_co2   = 0;
   int grove_scd30_temp  = 0;
   int grove_scd30_humi  = 0;
   int scd30_temp_offset = -4;
   int scd30_humi_offset = 13;
   
   /*-----------------------------------------*/
   /* Function void sensor_co2()              */
   /*                                         */
   /* TASK    : read out air sensor data      */
   /* UPDATE  : 23.10.2020                    */
   /*-----------------------------------------*/   
   void sensor_co2()
   {
     // read the sensor
     float result[3] = {0};
       
     if (scd30.isAvailable()) 
     {
      scd30.getCarbonDioxideConcentration(result);
      grove_scd30_co2  = result[0];
      grove_scd30_temp = result[1];
      grove_scd30_humi = result[2];
      // serial print of all sensor values
      Serial.print(F("[?] GROVE SCD30 CO2 --> "));
      Serial.print("SCD30-CO2:"); Serial.print(grove_scd30_co2); 
      Serial.print("  SCD30-T:"); Serial.print(grove_scd30_temp+scd30_temp_offset);  
      Serial.print("  SCD30-H:"); Serial.println(grove_scd30_humi+scd30_humi_offset);
      // display co2 pictures on TFT
      const char* list[] = {"co2-low-rgb565.bmp", "co2-mid1-rgb565.bmp", "co2-mid2-rgb565.bmp", "co2-high1-rgb565.bmp", "co2-high2-rgb565.bmp"};
      // rgb traffic light (planned)
      int r,g,b;
      uint8_t cnt = 0;
      if (grove_scd30_co2<700)   { r=0;   g=127; b=0; cnt=0; }
      if (grove_scd30_co2>=700)  { r=0;   g=127; b=0; cnt=1; }
      if (grove_scd30_co2>=1000) { r=127; g=127; b=0; cnt=2; }
      if (grove_scd30_co2>=1300) { r=127; g=0;   b=0; cnt=3; }
      if (grove_scd30_co2>=1500) { r=127; g=0;   b=0; cnt=4; } 
      drawImage<uint16_t>(list[cnt],0,0); 
      tft.setTextColor(TFT_BLACK,TFT_WHITE);
      tft.setFreeFont(FMB18);
      tft.drawNumber(grove_scd30_co2,80,190);                 
     }
   }
 
/*
 * Timer
 * Generally, you should use "unsigned long" for variables that hold time.
 * The value will quickly become too large for an int to store
 */
   // this timer is used to update tft display
   unsigned long previousMillis = 0;
   // send every 10 minutes
   // unsigned long interval = 600000;  
   // send every 3 minutes
   // unsigned long interval = 180000;
   // send every 30 seconds
   unsigned long interval = 30000;   
   unsigned long counter  = 0; 

/*-------------------------------------------------------------------------------*/
/* Function void tft_display_status_bar(String room_name, long int room_bg)      */
/*                                                                               */
/* TASK    : show tft display status bar                                         */
/* UPDATE  : 22.09.2020                                                          */
/*-------------------------------------------------------------------------------*/
void tft_display_status_bar(String room_name, long int room_bg) 
{
  tft.fillRect(0,0,320,50,room_bg);
  tft.setFreeFont(FMB18);
  tft.setTextColor(TFT_WHITE);
  tft.setCursor((320 - tft.textWidth(room_name)) / 2, 32);
  tft.print(room_name); 
}

/*-------------------------------------------------------------------------------*/
/* Function void tft_display_room_screen(String room_name, long int room_bg)     */
/*                                                                               */
/* TASK    : show tft display room screen                                        */
/* UPDATE  : 22.09.2020                                                          */
/*-------------------------------------------------------------------------------*/
void tft_display_room_screen(String room_name, long int room_bg) 
{
  tft.fillScreen(TFT_WHITE);
  tft.fillRect(0,0,320,50,room_bg);
  tft.setFreeFont(FMB18);
  tft.setTextColor(TFT_WHITE);
  tft.setCursor((320 - tft.textWidth(room_name)) / 2, 32);
  tft.print(room_name);
  // drawing verticle line
  tft.drawFastVLine(150,50,190,TFT_DARKGREEN);
  // drawing horizontal line
  tft.drawFastHLine(0,140,320,TFT_DARKGREEN);  
}

/*-------------------------------------------------------------------------------*/
/* Function void tft_display_sensor_temperature(void)                            */
/*                                                                               */
/* TASK    : show tft display sensor temperature                                 */
/* UPDATE  : 22.09.2020                                                          */
/*-------------------------------------------------------------------------------*/
void tft_display_sensor_temperature(int temperature) 
{
  // setting the temperature
  tft.setTextColor(TFT_BLACK);
  tft.setFreeFont(FMB9);
  tft.drawString("Temperature",15,65);
  tft.setTextColor(TFT_BLACK);
  tft.setFreeFont(FMB18);
  tft.drawNumber(temperature,50,95);
  //tft.drawNumber(21,50,95);
  tft.setFreeFont(FMB12);
  tft.drawString("C",100,95);  
}

/*-------------------------------------------------------------------------------*/
/* Function void tft_display_sensor_humidity(int humidity)                       */
/*                                                                               */
/* TASK    : show tft display sensor humidity                                    */
/* UPDATE  : 22.09.2020                                                          */
/*-------------------------------------------------------------------------------*/
void tft_display_sensor_humidity(int humidity) 
{
  // setting the humidity
  tft.setTextColor(TFT_BLACK);
  tft.setFreeFont(FMB9);
  tft.drawString("Humidity",30,160);
  tft.setTextColor(TFT_BLACK);
  tft.setFreeFont(FMB18);
  tft.drawNumber(humidity,50,190);
  tft.setFreeFont(FMB12);
  tft.drawString("%",100,190);
}

/*-------------------------------------------------------------------------------*/
/* Function void tft_display_sensor_co2(int co2)                                 */
/*                                                                               */
/* TASK    : show tft display sensor co2                                         */
/* UPDATE  : 22.09.2020                                                          */
/*-------------------------------------------------------------------------------*/
void tft_display_sensor_co2(int co2) 
{ 
  // setting the CO2
  tft.setTextColor(TFT_BLACK);
  tft.setFreeFont(FMB9);
  tft.drawString("CO2",220,160);
  tft.setTextColor(TFT_BLACK);
  tft.setFreeFont(FMB18);
  tft.drawNumber(co2,160,190);
  tft.setFreeFont(FMB12);
  tft.drawString("ppm",250,190);
}

/*-------------------------------------------------------------------------------*/
/* Function void setup()                                                         */
/*                                                                               */
/* TASK    : setup all needed requirements                                       */
/* UPDATE  : 23.10.2020                                                          */
/*-------------------------------------------------------------------------------*/ 
void setup() 
{
  Serial.begin(9600);
  delay(3000);
  Serial.println(F(" "));
  Serial.println(F(" "));
  Serial.println(F("Starting..."));
  // WIO_TERMINAL
  Serial.println(F("WioTerminal CO2 Traffic Light Version 1.00"));
  Serial.println(F("GROVE SCD30 connected via I2C"));
  Serial.println(F(" "));

  // 3 Wio Terminal buttons
  pinMode(WIO_KEY_A, INPUT_PULLUP);
  pinMode(WIO_KEY_B, INPUT_PULLUP);
  pinMode(WIO_KEY_C, INPUT_PULLUP);

  // Initialise SD card
  Serial.println(F("[?] SD card initialize.."));
  if (!SD.begin(SDCARD_SS_PIN, SDCARD_SPI)) 
  {
    while (1);
  }
  
  // initialize the TFT display
  tft.begin();
  tft.setRotation(3);

  // initialize the scd30 sensor
  Serial.println(F("[?] GROVE SCD30 CO2 sensor initialize.."));
  Wire.begin();
  scd30.initialize();
  delay(3000);
  
  // read the co2 sensor */
  sensor_co2();  
}

/*-------------------------------------------------------------------------------*/
/* Function void loop()                                                          */
/*                                                                               */
/* TASK    : this runs forever                                                   */
/* UPDATE  : 23.10.2020                                                          */
/*-------------------------------------------------------------------------------*/
void loop() 
{
  // check if some one has pressed a button
  if (digitalRead(WIO_KEY_A) == LOW) 
  {
    Serial.println("[x} A Key pressed");   
    delay(200);
    // display room screen
    tft_display_room_screen("Dining-Room", TFT_BLUE);
    // display temperature sensor
    tft_display_sensor_temperature(grove_scd30_temp+scd30_temp_offset);
    // display humidity sensor
    tft_display_sensor_humidity(grove_scd30_humi+scd30_humi_offset);  
    // display co2 sensor
    tft_display_sensor_co2(grove_scd30_co2);    
    
  }
  else if (digitalRead(WIO_KEY_B) == LOW) 
  {
    Serial.println("[x] B Key pressed");     
    delay(200);
    // read the co2 sensor */
    sensor_co2();
  }
  else if (digitalRead(WIO_KEY_C) == LOW) 
  {
    Serial.println("[x] C Key pressed");
    tft_backlight = !tft_backlight;
     // Turning off the LCD backlight
    if (tft_backlight == false) { digitalWrite(LCD_BACKLIGHT, LOW);  }
     // Turning on the LCD backlight
    if (tft_backlight == true)  { digitalWrite(LCD_BACKLIGHT, HIGH); }
    delay(200);
  }
  
  /* 
   * It is checked whether the time for the transmission interval has already expired
   * If the time difference between the last save and the current time is greater
   * as the interval, the following function is executed.
  */
  if (millis() - previousMillis > interval)
  {
    // correct timer
    previousMillis = millis();
    // read the co2 sensor */
    sensor_co2();
  }
  
}

Credits

Elly Tse

Elly Tse

1 project • 1 follower
Focus on #5G # computing digital transformation # AI Education # Industrial meteorological environmental monitoring # open source# Lora #

Comments