Arduino Project

Mar 2020

Arduino CO2 Data Logger


Gas Intrusion Test

The project involved measuring gas intrusion within a compartment from an adjacent compartment.   I chose CO2 gas for the test because it was relatively safe, readily available, and good sensors exist to measure the concentration in air.   And most important, it has density properties similar to the actual gas of interest.  

I rented an expensive handheld device to perform the gas intrusion test, just to be sure I would get the best results.   However, it was unknown if the project would involve multiple iterations, so it was desirable to have an alternative and less costly means to measure the CO2 concentration.   I purchased the SCD30 CO2 Humidity and Temperature Sensor from Sparkfun (P/N SEN-15112) and integrated it into the Adafruit Data Logging Shield on top of an Arduino Uno.  

SCD30 CO2 Sensor

The SCD30 performance in comparison to the expensive hand-held meter was excellent.   Communication with the device is over I2C using the Wire library, making programming very easy.  

Below is the Arduino code I used to read the SCD30 and record the measurements to an SD Card using the Adafruit Data Logging Shield.  

sensor_CO2_SCD30_AF1141ArduinoDataLoggerShield.ino

/*
  Adafruit Arduino Data Logging Shield
  Product #1141
  https://www.adafruit.com/product/1141
  https://learn.adafruit.com/adafruit-data-logger-shield?view=all
  http://arduino.cc/en/Reference/SD

  Sparkfun CO2 Sensor (+ temp & humidity)
  Product #SEN-15112
  https://www.sparkfun.com/products/15112
  https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library

*/

// Arduino Uno
// Make sure Arduino IDE board = "Arduino Uno"
const byte pin_LED = 13;

//////////////////////////////////////////////////////////////////////////////
// The optimal balance between current consumption and response time is 15 seconds.
// Read the CO2 sensor every 5000 ms (5 seconds)
const unsigned long timerInterval = 5000;  
unsigned long timerLast = 0;  // timer
char isoDateTime[16];
float CO2ppm = 0.0;
float TempC = 0.0;
float HumidityPct = 0.0;
//////////////////////////////////////////////////////////////////////////////
//  AF DataLogger + RTC (product 2922)
//  RTC hardware is PCF8523
//  Library: "RTClib" by Adafruit (a fork of JeeLab's RTC library)
//  https://github.com/adafruit/RTClib

// Date and time functions using a PCF8523 RTC connected via I2C and Wire lib
#include "RTClib.h"
// I am specifically differentiating between the DataLogger RTC and the one 
// that comes with the GPS.
RTC_PCF8523 DataLoggerRTC;
//NOTE:  The RTC date/time is set to UTC
//////////////////////////////////////////////////////////////////////////////
//  microSD card
#include <SPI.h>
#include <SD.h>
// Revise the pin # below to match your mSD card hardware for Card Detect
// or chip select (CS), or 'chip/slave select' (SS). 
const int SDchipSelect = 10;  
File sdfile;
char filename[14];
//////////////////////////////////////////////////////////////////////////////
// Sensirion SCD30 CO2 sensor
// Detect 400 to 10000 PPM with an accuracy of ±(30ppm+3%). 
// https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
#include <Wire.h>
#include "SparkFun_SCD30_Arduino_Library.h"
SCD30 airSensor;
const byte pinRDY = 8;   // Data ready pin.  High when data is ready for read-out.
//const byte pinSCL = A5;  // I2C Arduino UNO
//const byte pinSDA = A4;  // I2C Arduino UNO
//////////////////////////////////////////////////////////////////////////////


void setup() {  
  Serial.begin(9600);
  while (!Serial) {
    delay(1);
  }

  //////////////////////////////////////////////////////////////////////////////
  // Sensirion SCD30 CO2 sensor
  Wire.begin();
  if (airSensor.begin() == false) {
    Serial.println("ERROR - CO2 sensor not detected.  Check wiring");
    while (1) { blinkERR(pin_LED);}
  }
  pinMode(pinRDY, INPUT);
  
  //////////////////////////////////////////////////////////////////////////////
  // Initialize the DataLogger RTC
  if (! DataLoggerRTC.begin()) {
    Serial.println("ERROR - Couldn't find DataLogger RTC");
    while (1) { blinkERR(pin_LED);}
  }
  if (! DataLoggerRTC.initialized()) {
    Serial.println("ERROR - DataLogger RTC (PCF8523) is not running!");
    while (1) { blinkERR(pin_LED);}
  } else {
    Serial.println("DataLogger RTC (PCF8523) is running");
  }
  //////////////////////////////////////////////////////////////////////////////
  // see if the SD card is present and can be initialized: 
  if (!SD.begin(SDchipSelect)) {
    Serial.println("SD card failed, or not present");
    while (1) { blinkERR(pin_LED);}
  }
  Serial.println("SD card initialized."); 
  // Create a filename reference to a file that doesn't exist 'ANALOG00.TXT'..'ANALOG99.TXT'
  //char filename[14];
  strcpy(filename, "/ANALOG00.TXT");
  for (uint8_t i = 0; i < 100; i++) {
    filename[7] = '0' + i/10;
    filename[8] = '0' + i%10;
    // create if does not exist, do not open existing, write, sync after write
    if (SD.exists(filename)){
      Serial.print("File '");
      Serial.print(filename);
      Serial.println("' already exists");
    } else {
      Serial.print("New file will be '");
      Serial.print(filename);
      Serial.println("'");
      break;
    }
  }
  // Open file on SD Card for writing
  sdfile = SD.open(filename, FILE_WRITE);
  if (! sdfile) {
    Serial.print("ERROR - unable to create '");
    Serial.print(filename); Serial.println("'");
    while (1) { blinkERR(pin_LED);}
  }
  //////////////////////////////////////////////////////////////////////////////

  Serial.println("Setup complete\n");
  Serial.println("Date/Time; CO2ppm; TempC; Humidity%");
  sdfile.println("Date/Time; CO2ppm; TempC; Humidity%");
} // setup()


void loop() {
  
  if (timerLast > millis())  timerLast = millis();
  if ((millis() - timerLast) > timerInterval) {
    // toggle LED to show write activity
    digitalWrite(pin_LED, HIGH);
    DateTime now = DataLoggerRTC.now();
    //float f = fGetLgSignedRandFloat();
    CO2ppm = airSensor.getCO2();
    TempC = airSensor.getTemperature();
    HumidityPct = airSensor.getHumidity();
    sprintf(isoDateTime, "%4d%02d%02dT%02d%02d%02d%Z",
        now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
    Serial.print(isoDateTime);
    Serial.print(";");
    Serial.print(CO2ppm);
    Serial.print(";");
    Serial.print(TempC);
    Serial.print(";");
    Serial.println(HumidityPct);
    // Write to the SD card..
    sdfile.print(isoDateTime);  // 20200216T150644Z
    sdfile.print(";");
    sdfile.print(CO2ppm);
    sdfile.print(";");
    sdfile.print(TempC);
    sdfile.print(";");
    sdfile.println(HumidityPct);
    // Execute a flush() to insure it is written since no sdfile.close() will be issued.
    sdfile.flush();
    digitalWrite(pin_LED, LOW);
    timerLast = millis();
  }
  
  yield();
  
} // loop()


void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
} // blinkLED()

void blinkERR(byte ledPIN){
  // S-O-S
  const int S = 150, O = 300;
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(O);
    digitalWrite(ledPIN, LOW);
    delay(O);
  }    
  delay(200);
  for(int i = 3; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(S);
    digitalWrite(ledPIN, LOW);
    delay(S);
  }    
  delay(200);
} // blinkERR()



////////////////////////////////////////////////////////////////////////////////////////////

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.