Back to

CO2 Sensor: Integrating the SCD41

@victor @oscgonfer @pral2a1
Today I started out working on integrating the I2C 8 Channel Mux UNit.

I soon discovered, during the code analysis stage; that this is going to be a complex task and it will not be done overnight.

  • So, I thought to start on something easier; so I can become familiar with the code layout and learn a few things. I decided to work on integrating the SCD41 sensor; which is intended as a replacement for the SCD30 CO2 sensor. This is needed in some use-case scenarios (like mine) when SCD30 will not fit inside the SCS V3 enclosure. SCD41 is tiny, and mine is mounted in one of the enclosure ports designed for Gas Sensors. @oscgonfer made provision for it in my instance of the SCS V3 system because we thought about it right from the start.

  • This journey starts with cloning of the code for SCD30; because (I reason) the two sensors are functionally similar and come from the same manufacturer; likely to work in a similar way. However they do have slightly different operational commands.

  • In particular I have come across three cases where I need to seek the advice from the system designers:

  1. Time Interval between readings:
    The code for SCD30 has functionality to set the time interval between readings. This is an important measure used to limit power consumption in the SCS system (and SCK too). The existing code allows any time interval to be set, and this is supported by SCD30.
    The SCD41 on the other hand does not have this feature. The ‘normal’ startPeriodicMeasurement command sets the device to make a new reading every 5 seconds. Each Reading can be read directly from the device only once, because the buffer is emptied when a reading is taken.
    There is a ‘startLowPowerPeriodicMeasurement’ command; which conducts measurements every 30 seconds. The documentation indicates this feature is used to reduce power consumption and self-heating of the device. I assume this is to be avoided because it may make readings inaccurate.
    So, there are two choices for automated periodic measurements: 5 second intervals or 30 second intervals.
    There is a further choice: Instead of regular Periodic Measurements determined by the device; you can instead request spot measurements.
    There is a further issue: either startPeriodicMeasurement or startLowPowerPeriodicMeasurement can only be run when periodic measurements are stopped. When the stopPeriodicMeasurement command is sent;

" the sensor will only respond to other commands after waiting 500 ms after issuing the stop_periodic_measurement command."

  • In fact the supporting library for this has a ‘delay(500)’ command within the stopPeriodicMeasurement function; thus stopping the entire SCS for that (thankfully short) period of time (if this is not set to some other value). You can turn this off; but then it is possible to send the device a command and get no response. Not very friendly.
  • So I will of course navigate my way around these issues as best I can; but you should be aware there are they exist. It seems likely I will set up the integration code to utilise the spot/Single shot measurement mode rather than automated periodic measurements. In that way the device can be made to operate in a manner that is sympathetic to the existing SCS system.
  1. Pressure Compensation
    The device also has the feature where Altitude OR Pressure compensation can be incorporated into the measurements.

The SCD4x features on-chip signal compensation to counteract pressure and temperature effects. Feeding the SCD4x with the pressure or altitude enables highest accuracy of the CO2 output signal across a large pressure range. Setting the temperature offset improves the accuracy of the relative humidity and temperature output signal. Note that the temperature offset does not impact the accuracy of the CO2 output.

  • I think temperature offset for this device should be zero because it is mounted exposed to air outside the case a couple of cm away from the Dallas Temperature probe. I suppose it’s possible to compare the SCD41 temperature reading with the Dallas Reading; assume the latter is more accurate and calculate an appropriate offset to load into the SCD41. This could then inform the RH reading.

Pressure Compensation:

  • I consider momentarily whether it is worth the trouble to:
    (a) detect if GPS is enabled && altitude measurement is available
    (b) loading latest Altitude into the altitude compensation register.
  • and/or
    (c) detect if air pressure reading is available (always yes for SCS & SCK)
    (d) load Pressure compensation.
  • NOTE: Pressure compensation overrides Altitude compensation in the SCD41. This decision is easy. Because we always have pressure readings in the SCK/SCS, the current pressure can be loaded into the SCD41 ongoing. We ignore Altitude.
  1. Self-Recalibration:
    The SCD41 device can perform automatic self recalibration and specified conditions.:
    To realise high initial and long-term accuracy, the SCD4x includes two field calibration features. Forced recalibration (FRC) enables restoring highest accuracy with the assistance of a CO2 reference value immediately. Typically, FRC is applied to compensate for drifts originating from the sensor assembly process or other extensive stresses. Automatic self-calibration (ASC) ensures highest long-term stability of the SCD4x without the need of manual action steps from the user. The automatic self calibration algorithm assumes that the sensor is exposed to the atmospheric CO2 concentration of 400 ppm at least once per week`.
  • Now, I do not think the above assumption (bold type) is entirely valid. I think (sadly) it has been a while since global baseline atmospheric CO2 concentration was that low. I think it’s around 420 ppm now. Thanks to all of us for that. I am still considering how to handle this feature. It you wish to provide me any guidance please feel free to get back in touch. I note that the existing firmware performs an automatic reset in the small hours of each day. Perhaps this is the time such automatic recalibration could be done. Your thoughts ?
  • One possibility is to provide a UI command where the user enters a CO2 PPM value at the console to be used to recalibrate the sensor. Such value would be obtained from data published by the UN Climate Change body or NASA or somebody.
  • Even better; perhaps FABLABSBCN could send to devices via MQTT back channel such CO2 Global data from time to time whenever it gets published. I think the SCD30 must incur the same issue, and I think I have seen it discussed by some other SCK users. Is this even feasible ?>

Anyway; as said above, I would like some guidance and feedback from you guys on this topic, I am working on it after I type this. I will of course make some default decisions in order to keep moving, but they can be changed. In the case of this sensor, I think it will become part of the mainstream for SCS/SCK due to the size consideration coupled with its specified accuracy being better than SCD30.


Hi Bryn,

I have pushed a WIP branch for the SCD41 sensor. This is mostly based on Victor’s implementation of SCD30 sensor and the changes to adapt it to the library by sparkfun in: GitHub - sparkfun/SparkFun_SCD4x_Arduino_Library: An Arduino library for the Sensirion SCD4x (SCD40 and SCD41) family of CO2 sensors

Currently it is returning temperature and humidity data, but not valid CO2 data. The library has a readMeasurement() function, so I assume this should be used to get fresh data. We can look at it together and build on what was pushed to the firmware branch.


1 Like

Hi @oscgonfer Many Thanks.

I have of course; already taken a copy of SCD30 code and turned it into SCD4x code on my local copy.
This is contained within sckaux {.h;.cpp}
At this moment in time I am looking at the altitude/pressure compensation thing.

I checked documentation for both SCD30 and SCD41
SCD30 want the pressure value sent in for PC in millibars. The reading from {OneSensor *pressureSensor } is multiplied by 10 before being sent to the library function setAmbientPressure;
SCD41 wants the pressure value sent in for PC in Pascals. There is a factor of 100 converting mbar to Pa.

I checked the documentation for pressure sensor MPL3115A2 and found that it delivers readings in units of KPa; which is same units sent back to SmartCitizen website.
The existing code for SCD30 multiplies the *pressureSensor values by 10 which is correct for converting KPa to Millibars.
For SCD41; I will need to multiply the KPa values by 1000 to get Pa

Concerning SCD30 (incorrect CO2 data measurements); yes I can look at that; as I have one of those you sent me earlier; I can hang it off the system and compare value alongside the SCD41 values; it will be interesting. I’ll examine the code(s) and see what I can find; and I am happy to work with you on it.
And we have a natural check of the readings; which are expected to be something over 400 ppm.
It’s a good thing because I do not have a source of CO2 with known concentration.
And I can check the RH and Temp values against the Urban kit sensors values too.

This all depends on me getting the I2C Mux working; which changes are also under way (at the same time as SCD41). There is also a change to sensors.h (of course); plus sckbase.
Its all one build. I am chasing down errors and typos right now.

when I get the chance I will look at git hub and try to get GitKraken to get me a copy of that Branch. But of course; to my knowledge; I do not have permission to write back into your repo. (I can Pull but not Push) If you are thinking about granting it to me; then the first thing to know is my Github username is ‘bryn51’. :slight_smile:

I will definitely need to work with you (maybe in real time) in teacher:student mode concerning uploading my code base into the Repo without screwing things up.

1 Like

Yesterday I managed to build and upload my modified code into SCK data board for the first time. So it’s another little milestone.

This code has: Support for SCD41 and TCA9548A I2C mux.

The USB UI works (some commands) but the system is not performing periodic measurements and sending them. So there are a few bugs and I will need to use a debugging process. Lots of sprintf statements. It’s tempting to try out the Ice debugger but that is fraught because it runs on windows. It’s a last resort.

1 Like


Where are you putting all this? Is it on a github? :smiley:


It is in my own repo ‘bryn51’. it is accessible publicly.

Here is the link:My Repo

I publish there only when I think I nailed it; but I am often wrong.
The code for PM2 wind-rain is kinda stable. But the code for data board is constantly changing; therefore not being updated.

I can update it more frequently if I know someone is working with me on it. But I get no response when I send you stuff.


Maybe let’s try to put issues (by categories) on the data board and we can have a look from time to time.

I will (when time permits); after you have responded to my collaboration requests on github; pick through ‘issues’ embodied in the text of this message; create github issues and assign them to you.