Testing Sensirion's STC31 CO2 sensor

Testing Sensirion's STC31 CO2 sensor

Yoctopuce has been offering a CO2 sensor for some time now. The current model, the Yocto-C02-V2, has a measure range of 0-40000ppm, or 0-4%. This is sufficient for most common applications. But what if you need to measure higher concentrations? Sensirion now offers the STC31 sensor with a measure range from 0 to 100%.

The STC31 sensor

Compared to the SCD30, with which the Yocto-C02-V2 is equipped, the STC31 is a tiny 3x3.5mm. While it can measure up to 100%, its accuracy is, at best, only 5000ppm (0.5%), compared with around 30ppm for the SCD30.

We ordered ours from sparkfun under reference SPX-18385. It comes soldered onto a small 26x26mm PCB. This PCB also contains an SHTC3 temperature and humidity sensor. Both sensors have an I2C interface.

The sparkfun SPX-18385
The sparkfun SPX-18385

To test our sensor, we of course used a Yocto-I2C. The presence of Qwiic connectors on the board makes connection trivial.

Connecting to a Yocto-I2C ?  Dead simple
Connecting to a Yocto-I2C ? Dead simple


The most practical way of interrogating the sensor is to write a job which, when executed by the Yocto-I2C, handles the I2C requests and presents the values in the same way as a standard Yoctopuce sensor, making integration using the Yoctopuce API trivial.

A rather simplistic method

The sensor's 7-bit address is 0x29. Like all Sensirion sensors, the STC31 uses numerous CRCs, but this time they can be disabled using command 0x3768, which makes programming much easier. Next, you need to configure the operating mode, which you can choose between:

  • Command 0x3615000: 0..100% in nitrogen
  • Command 0x3615001: 0...100% in air
  • Command 0x3615002: 0...25% in nitrogen
  • Command 0x3615003: 0..25% in air

Optionally, auto-calibration can be activated with command 0x3FEF .

We can therefore write a job containing the following initialization task:

assert !isset($started) writeLine {S}523768{P} expect 29:{A}{A}{A} writeLine {S}5236150003{P} expect 29:{A}{A}{A}{A}{A} writeLine {S}523FEF{P} expect 29:{A}{A}{A} compute $started=1

For reading, we add a periodic task (period=1000ms) to the job, which uses the command 0x3639 to start the measure, waits 75ms, reads the result as a 16-bit unsigned integer (WORD) and stores it in the variable $CO2RAW. Finally, we convert $CO2RAW into a percentage using the formula 100*($CO2RAW-16384)/32768 and assign the result to the Yocto-I2C's genericSensor1.

assert isset($started) writeLine {S}523639{P} wait 75 writeLine {S}53xx{A}xx{N}{P} expect 29:{A}($CO2RAW:WORD) compute $1=100*($CO2RAW-16384)/32768

To see if it works and to check that the sensor is somewhat credible, you can of course use VirtualHub, but you'll get a much better overview with Yocto-Visualization.

Testing with Yocto-Visualization. There's something weird
Testing with Yocto-Visualization. There's something weird

On the surface, it works. The measured value of 0.2% is clearly high compared with the expected 0.05% (500ppm), but given the 0.5% accuracy announced in the STC31 datasheet, we're right on target. On the other hand, knowing that humans emit some CO2 through the skin, if we try to raise the CO2 level by placing our finger on the sensor, the measured value collapses and even falls into the negative range.

On closer inspection, it transpires that the sensor is highly sensitive to humidity. That's why it's more than advisable to supply a humidity sensor that can be used to compensate for ambient humidity. Now it's clear why there's a humidity sensor on the sparkfun board.

A method that takes humidity into account

The SHTC3 sensor, whose 7-bit address is 0x70, requires no specific initialization. To interrogate the sensor

  • wake up the sensor using command 0x3517
  • Wait 1 ms
  • Start a measure using command 0x7866
  • Wait 15 ms
  • Read temperature and humidity as two 16-bit integers stored in the variables $TEMPRAW and $HUMRAW; note that the third byte is a CRC.

Optionally, you can convert $TEMPRAW to ░C and $HUMRAW to percentage using the formulas -45+175*$TEMPRAW/65536 and 100*$HUMRAW/65536, then assign the result to genericSensors 2 and 3 of the Yocto-I2C.

To inject the humidity value into the STC31 sensor, use the command 0x3624 followed by the value of $HUMRAW. The rest of the task is identical. The measure task thus becomes:

assert isset($started) writeLine {S}E03517{P} wait 1 expect 70:{A}{A}{A} writeLine {S}E07866{P} expect 70:{A}{A}{A} wait 15 writeLine {S}E1{A}xx{A}xx{A}xx{A}xx{A}xx{N}{P} expect 70:{A}($TEMPRAW:WORD)($CRC:BYTE)($HUMRAW:WORD).* compute $2=-45+175*$TEMPRAW/65536 compute $3=100*$HUMRAW/65536 writeVar {S}523624($HUMRAW:WORD){P} expect 29:{A}{A}{A}{A}{A} writeLine {S}52362F03C1{P} expect 29:{A}{A}{A}{A}{A} writeLine {S}523639{P} wait 75 writeLine {S}53xx{A}xx{N}{P} expect 29:{A}($CO2RAW:WORD) compute $1=100*($CO2RAW-16384)/32768

This time, when we place our finger on the sensor, the sudden change in humidity continues to disturb the sensor, but we can see that the compensation has an effect on the measure. Even if this compensation is not perfect, the compensated measure remains within the 0.5% limit announced in the datasheet.

With humidity compensation, it's somewhat better
With humidity compensation, it's somewhat better


Sensirion's STC31 sensor may be of interest to you if you need to measure high concentrations of CO2, but accuracy is not a top priority. However, it is clearly not interchangeable with the SCD30 found on the Yocto-C02-V2, as the two sensors have a measure range and accuracy that have nothing in common. What's more, it's apparently essential to couple the STC31 with a humidity sensor, otherwise its accuracy is likely to be significantly affected. Sensirion's STC31 sensor can easily be interrogated with a Yocto-I2C. If you'd like to try it yourself, you can download the job we wrote for this test.

Add a comment No comment yet Back to blog

Yoctopuce, get your stuff connected.