CO₂ Sensor¶
Written by Grace Lo & Rachel Yan
A previous version describing the python implementation of the sensor can be found here.
The CO₂ recording procedure was developed and tested on the Sensirion SCD30 CO₂ Sensor Module for measuring carbon dioxide (CO₂) concentration, temperature, and humidity. It is a digital sensor communicating over I²C and is pre-calibrated under controlled environmental conditions. It is also connected to a npn transistor acting as a semiconductor switch used to powercycle the sensor in the case of communication errors. According to manufacturer specifications, the sensor requires 10 minutes of warm-up time to allow for thermal stabilization when it is first turned on (see "set temperature offset" from the Low Power Mode Guide). On initial startup, the flux chamber accounts for this by beginning the sequence with airing out the chamber for an extended period of time, which gives time for the sensor elements to warm-up and prepare for measurements. However, if the CO₂ Sensor Module experiences an error during the logging period, the device will power cycle and immediately continue reading data. It is up to the discretion of the user to remove data points affected by the sensor reset. The basic CO₂ sensor functionality is borrowed from the Sensirion embedded-i2c-scd30 library. The data is provided in standard units (eg. ppm for CO₂, °C for temperature, and %RH for humidity) with specifications provided below.
Sensor | Range | Accuracy |
---|---|---|
CO₂ | 0 - 40,000 ppm | ±30 ppm + 3 %MV |
Temperature | 0 - 100 %RH | ±3 %RH |
Humidity | -40 - 70 °C | ±(0.4°C + 0.023 × (T [°C] – 25°C)) |
Pin Connections¶

Pin connections from the CO₂ sensor → Pico are as follows:
- VIN → 3V3(OUT) (pin 36)
- GND → npn → GND
- npn → 1kΩ → GPIO11 (pin 15)
- SCL → I2C0_SCL (pin 17)
- SDA → I2C0_SDA (pin 16)
- SEL → floating for selecting I2C
Code¶
All code is in the CornellFluxChamber Github repository. The code for interfacing with the CO₂ sensor is incorporated into flux_chamber.c
.
Includes¶
The first lines of code in the C source file include header files. Don't forget to link these in the CMakeLists.txt file!
The following files are required to interface with the CO₂ sensor and are provided by the Sensirion embedded-i2c-scd30 library (refer to the parent library for a description about each header file).
#include <stdio.h>
#include "scd30_i2c.h"
#include "sensirion_common.h"
#include "sensirion_i2c_hal.h"
Initializations¶
First the npn transistor is switched on to power the CO₂ sensor.
gpio_init(11);
gpio_set_dir(11, GPIO_OUT);
gpio_put(11, 1);
The C source file initializes the CO₂ sensor's I²C Hardware Abstraction Layer (HAL) and configures the driver with the I²C address of the sensor, which sets up the communication interface between the sensor and the Pico.
int16_t error = NO_ERROR;
sensirion_i2c_hal_init();
init_driver(SCD30_I2C_ADDR_61);
Then it ensures the sensor starts in a clear and ready state by terminating all previous reading loops, sending a reset signal, and verifying the communication responsiveness of the sensor one last time, before setting up the sensor with the periodic measurement.
scd30_stop_periodic_measurement();
scd30_soft_reset();
sensirion_i2c_hal_sleep_usec(2000000);
uint8_t major = 0;
uint8_t minor = 0;
error = scd30_read_firmware_version(&major, &minor);
if (error != NO_ERROR) {
printf("error executing read_firmware_version(): %i\n", error);
}
printf("firmware version major: %u minor: %u\n", major, minor);
error = scd30_start_periodic_measurement(0);
if (error != NO_ERROR) {
printf("error executing start_periodic_measurement(): %i\n", error);
}
Read & Error Protection¶
The CO₂ sensor module is read using scd30_blocking_read_measurement_data(&co2,&temp, &hum)
and directly written to the file.
As a digital sensor sending multi-byte packets of data, the SCD30 is susceptible to errors due to incomplete I²C transactions and failed validation from dropped bytes. Furthermore, with a default sampling interval of 2 seconds (see 1st footnote from the datasheet), sampling the sensor at its highest allowable frequency can lead to occasional measurement delays.
During long term testing, there were 2 types of sensor read errors: a recoverable version and a less common hanging version. When scd30_blocking_read_measurement_data
returns with an error, the first type skips over the entire measurement iteration and continues on the next cycle. However, the second type causes future read requests to hang, blocking the entire program sequence. To prevent system hangs, 2 levels of recovery were established to ensure autonomous function of the flux chamber.
Sensor Level Recovery: The first is using the npn transistor semiconductor switch to power cycle the sensor every time it encounters a read error, which is wrapped in a
recover_scd30
helper function. This function isolates the sensor and does not impact any of the other subsystems (eg. methane sensor) connected to the Pico. The sensor recovery procedure consists of toggling the GPIO controlling the transistor to switch off power to the sensor, then reinitializing it and continuing with recording data. However, due to the sensor warm-up period, invalid data immediately after powering up the CO₂ sensor may cause more data loss than necessary. Future optimizations can differentiate between recoverable and hanging errors to limit the number of sensor power cycles.System Level Recovery: The second is a system level power cycle using the RP2040's built in
watchdog
timer. If there is a hang for more than 8 seconds, the RP2040 will reset the whole system. The downside to this is that the script will restart from the beginning, returning to the original start time and airing out the chamber. To maintain time continuity, the time set to air out the chamber can be added to the last recorded time to connect data across a Pico power cycle.
Sensor Testing¶
The CO₂ sensor was tested by manipulating CO₂ concentrations, temperature, and humidity through a series of covering the sensor with a hand and blowing on it. Current lab testing has shown CO₂ readings ranging from approximately 300 ppm to over 6000 ppm, temperature ranging from 24-28 °C, and 45-70% relative humidity.
The example provided below shows a series of data measurements, 5 seconds apart, while altering the sensor's immediate environmental conditions.
