Debugging Occasional Incorrect SCD30 Results
Currently, I am working with a Sensirion SCD30 CO2 and RH/T Sensor Module for both work and Senior Design. The module includes an IR led detection chamber for measuring CO2 as well as a SHT** relative humidity and temperature sensor for calibration of the CO2. This improves integration and is a pretty good deal if one needs both in their system.
Sensirion Library
Sensirion is nice enough to have invested engineering time into creating open source libraries for their sensors located on Github. After quickly porting the sensirion_hw_i2c_implementation.c
library functions to my microcontroller and soldering to the carrier board I designed, I had the sensor running with accurate results. However, although the results were accurate every once in a while there would be erroneous result.
Bad Result
As seen below each of the values in the erroneous result are all the same, and once debugged in Eclipse Embedded CDT (go open source!) it can be seen that all the results are 0xFF
which corresponds to MAX_VALUE
. However, what is most interesting is that the checksum bytes are correct for 0xFFFF
chunks.
At this point it appears that the SCD30 is intentionally sending 0xFF
results. Time to dive in a little deeper....
Timing Analysis
The demo I am currently running is directly from the Git repo above. It configures the SCD30 to operate with an internal measurement interval of two seconds and then samples every two seconds to fetch the result.
1 scd30_set_measurement_interval(interval_in_seconds);
2 sensirion_sleep_usec(20000u);
3 scd30_start_periodic_measurement(0);
4 sensirion_sleep_usec(interval_in_seconds * 1000000u);
5
6 while (1) {
7 /* Measure co2, temperature and relative humidity and store into
8 * variables.
9 */
10 err = scd30_read_measurement(&co2_ppm, &temperature,
11 &relative_humidity);
12 if (err != STATUS_OK) {
13 SEGGER_RTT_printf(0, "error reading measurement\n");
14
15 } else {
16 SEGGER_RTT_printf(0, "measured co2 concentration: %6f ppm\r\n"
17 "\t measured temperature: %6f degreeCelsius\r\n"
18 "\t measured humidity: %6f %%RH\r\n", co2_ppm, temperature,
19 relative_humidity);
20 }
21
22 sensirion_sleep_usec(interval_in_seconds * 1000000u);
23 }
24
25 scd30_stop_periodic_measurement();
This makes me wonder. Could it be a race condition, where a sample is occurring while I try and fetch? Time to check the I2C bus with the Saleae logic analyzer.
I2C Waveform
Checking the general timing, it can be seen below that the microcontroller is indeed waiting two seconds before performing another query from the module. Interestingly enough the ~80mA
current spikes can be seen on the 5V
rail when the module internally performs an acquisition.
Looking further at the timing of these spikes, each of the I2C queries occur at a different delta from the end of the internal acquisition. The erroneous sample occurs at Δt1, Δt2 and Δt3. Where Δt1 > Δt2 > Δt3. So to me it seems unlikely that Δt2 would be the erroneous sample.
I2C Up Close
Below is a zoomed in waveform of each of the above I2C queries in order.
From the above waveforms, the general I2C read operation can be observed. Notably I2C clock stretching can be seen operating correctly in the 4th frame of each image. I see no reason for the erroneous 0xFF
results due to bad I2C connections or power supply issues.
Conclusion
From the above, I am led to believe that the sensor will return a sea of 0xFF
if a sample is somehow stale or other random issue internal to the module. Luckily, coding around this will be quite easy as the results are clearly out of expected range.
Extra
Turns out that the data sheet specifically states to:
Make sure that the measurement is completed by reading the data ready status bit before read out.
Yet Sensirion's own demo does not follow this note. Is it time for a pull request? (Edit: turns out it was, #44)