SCD41 CO2 sensor

The Sensirion SCD41 measures real CO2 in ppm, with temperature and humidity, over I2C at 0x62, one reading every five seconds.

Aliases: SCD40, SCD4x, Adafruit SCD-41, Qwiic CO2 sensor. It is the sensor of classroom air monitors and ventilation alarms: unlike the eCO2 figure a gas sensor estimates, it counts CO2 molecules by the sound they make when a light pulse warms them.

Pins

Pin What it does
VIN Supply, 2.4 to 5.5 V.
GND Ground.
SCL I2C clock. The board carries the pull-ups.
SDA I2C data.

Properties

co2 (ppm), temperature (degrees C, -10 to 60) and humidity (%RH) are the room; the three sliders change them while the circuit runs.

Reading it on an Arduino

With Sensirion's library, which Mokxi ships as SensirionI2cScd4x.h:

#include <Wire.h>
#include <SensirionI2cScd4x.h>
SensirionI2cScd4x sensor;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  sensor.begin(Wire, SCD41_I2C_ADDR_62);
  sensor.stopPeriodicMeasurement();
  sensor.startPeriodicMeasurement();
}

void loop() {
  bool ready = false;
  sensor.getDataReadyStatus(ready);
  if (ready) {
    uint16_t co2;
    float t, rh;
    sensor.readMeasurement(co2, t, rh);
    Serial.println(co2);
  }
  delay(1000);
}

How it talks

Commands are sixteen bits, high byte first. A command with an argument carries a word and its CRC; one with a response is followed by a read of words, each with its CRC.

  • Power on takes 30 ms before the address is acknowledged.
  • start_periodic_measurement (0x21B1) gives the first result 5 s later and then one every 5 s. get_data_ready_status (0xE4B8) says when; read_measurement (0xEC05) returns CO2, temperature and humidity, and a read with no new result is refused.
  • stop_periodic_measurement (0x3F86) takes 500 ms, during which nothing answers. While a measurement runs only reading, data ready, stop and the ambient pressure are taken.
  • measure_single_shot (0x219D, 5 s) and the humidity-only shot (0x2196, 50 ms) are the SCD41's; power_down and wake_up put it to sleep and back.
  • The temperature offset, altitude, ambient pressure, self-calibration and the forced recalibration are held and read back; persist_settings keeps them for reinit.
  • T = -45 + 175 x S / 65536 and RH = 100 x S / 65536.

What it does not model

The photoacoustic measurement's noise, and the effect of altitude, pressure and the temperature offset on the result: a reading is the slider's CO2 plus any forced recalibration's correction.

Common mistakes

Asking for anything but a reading while a periodic measurement runs: send stopPeriodicMeasurement() and wait 500 ms first. Reading faster than every 5 s: the second read in a period has nothing new and is refused.