├── images ├── SCD41.png ├── SCD41_pinout.png ├── Arduino-Micro-i2c-pinout-3.3V.png ├── Arduino-Nano-i2c-pinout-3.3V.png ├── esp32-devkitc-i2c-pinout-3.3V.png ├── Arduino-Uno-Rev3-i2c-pinout-3.3V.png └── Arduino-Mega-2560-Rev3-i2c-pinout-3.3V.png ├── .gitignore ├── metadata.yml ├── .github └── workflows │ ├── github_release.yml │ └── arduino_quality_check.yml ├── library.properties ├── .clang-format ├── LICENSE ├── keywords.txt ├── CHANGELOG.md ├── examples ├── exampleScd41SingleShot │ └── exampleScd41SingleShot.ino └── exampleUsage │ └── exampleUsage.ino ├── README.md └── src ├── SensirionI2cScd4x.cpp └── SensirionI2cScd4x.h /images/SCD41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/SCD41.png -------------------------------------------------------------------------------- /images/SCD41_pinout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/SCD41_pinout.png -------------------------------------------------------------------------------- /images/Arduino-Micro-i2c-pinout-3.3V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/Arduino-Micro-i2c-pinout-3.3V.png -------------------------------------------------------------------------------- /images/Arduino-Nano-i2c-pinout-3.3V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/Arduino-Nano-i2c-pinout-3.3V.png -------------------------------------------------------------------------------- /images/esp32-devkitc-i2c-pinout-3.3V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/esp32-devkitc-i2c-pinout-3.3V.png -------------------------------------------------------------------------------- /images/Arduino-Uno-Rev3-i2c-pinout-3.3V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/Arduino-Uno-Rev3-i2c-pinout-3.3V.png -------------------------------------------------------------------------------- /images/Arduino-Mega-2560-Rev3-i2c-pinout-3.3V.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sensirion/arduino-i2c-scd4x/HEAD/images/Arduino-Mega-2560-Rev3-i2c-pinout-3.3V.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | app 2 | build 3 | arduino-core 4 | hardware 5 | build 6 | test-bin 7 | *.iml 8 | .idea 9 | .DS_Store 10 | .directory 11 | avr-toolchain-*.zip 12 | manifest.mf 13 | nbbuild.xml 14 | nbproject 15 | -------------------------------------------------------------------------------- /metadata.yml: -------------------------------------------------------------------------------- 1 | # driver generation metadata 2 | generator_version: 1.1.2 3 | model_version: '2.0' 4 | dg_status: released 5 | is_manually_modified: false 6 | first_generated: '2021-01-21 10:54' 7 | last_generated: '2025-01-29 12:04' 8 | -------------------------------------------------------------------------------- /.github/workflows/github_release.yml: -------------------------------------------------------------------------------- 1 | name: Github Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | github-release: 10 | uses: sensirion/.github/.github/workflows/driver.common.github_release.yml@main 11 | secrets: inherit 12 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Sensirion I2C SCD4x 2 | version=1.1.0 3 | author=Sensirion 4 | maintainer=Sensirion 5 | sentence=Library for the SCD4X sensor family by Sensirion 6 | paragraph=Enables you to use the SCD4X sensor family via I2C. 7 | url=https://github.com/Sensirion/arduino-i2c-scd4x 8 | category=Sensors 9 | architectures=* 10 | depends=Sensirion Core 11 | includes=SensirionI2cScd4x.h 12 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: LLVM 4 | IndentWidth: 4 5 | AlignAfterOpenBracket: Align 6 | AllowShortBlocksOnASingleLine: false 7 | AllowShortCaseLabelsOnASingleLine: false 8 | AllowShortFunctionsOnASingleLine: false 9 | IndentCaseLabels: true 10 | SpacesBeforeTrailingComments: 2 11 | PointerAlignment: Left 12 | AlignEscapedNewlines: Left 13 | ForEachMacros: ['TEST_GROUP', 'TEST'] 14 | ... 15 | -------------------------------------------------------------------------------- /.github/workflows/arduino_quality_check.yml: -------------------------------------------------------------------------------- 1 | name: Quality check 2 | 3 | on: 4 | push: 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | arduino-quality: 11 | uses: sensirion/.github/.github/workflows/driver.arduino.check.yml@main 12 | with: 13 | expect-arduino-examples: true 14 | # change to "update" once you published the driver on Arduino library registry 15 | lint-lib-manager-check: update 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2025, Sensirion AG 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | SensirionI2cScd4x KEYWORD1 10 | 11 | ####################################### 12 | # Methods and Functions (KEYWORD2) 13 | ####################################### 14 | 15 | readMeasurement KEYWORD2 16 | setTemperatureOffset KEYWORD2 17 | getTemperatureOffset KEYWORD2 18 | setAmbientPressure KEYWORD2 19 | getAmbientPressure KEYWORD2 20 | getDataReadyStatus KEYWORD2 21 | getSensorVariant KEYWORD2 22 | measureAndReadSingleShot KEYWORD2 23 | startPeriodicMeasurement KEYWORD2 24 | readMeasurementRaw KEYWORD2 25 | stopPeriodicMeasurement KEYWORD2 26 | setTemperatureOffsetRaw KEYWORD2 27 | getTemperatureOffsetRaw KEYWORD2 28 | setSensorAltitude KEYWORD2 29 | getSensorAltitude KEYWORD2 30 | setAmbientPressureRaw KEYWORD2 31 | getAmbientPressureRaw KEYWORD2 32 | performForcedRecalibration KEYWORD2 33 | setAutomaticSelfCalibrationEnabled KEYWORD2 34 | getAutomaticSelfCalibrationEnabled KEYWORD2 35 | setAutomaticSelfCalibrationTarget KEYWORD2 36 | getAutomaticSelfCalibrationTarget KEYWORD2 37 | startLowPowerPeriodicMeasurement KEYWORD2 38 | getDataReadyStatusRaw KEYWORD2 39 | persistSettings KEYWORD2 40 | getSerialNumber KEYWORD2 41 | performSelfTest KEYWORD2 42 | performFactoryReset KEYWORD2 43 | reinit KEYWORD2 44 | getSensorVariantRaw KEYWORD2 45 | measureSingleShot KEYWORD2 46 | measureSingleShotRhtOnly KEYWORD2 47 | powerDown KEYWORD2 48 | wakeUp KEYWORD2 49 | setAutomaticSelfCalibrationInitialPeriod KEYWORD2 50 | getAutomaticSelfCalibrationInitialPeriod KEYWORD2 51 | setAutomaticSelfCalibrationStandardPeriod KEYWORD2 52 | getAutomaticSelfCalibrationStandardPeriod KEYWORD2 53 | signalTemperature KEYWORD2 54 | signalRelativeHumidity KEYWORD2 55 | signalCo2Concentration KEYWORD2 56 | signalTemperatureOffset KEYWORD2 57 | signalAmbientPressure KEYWORD2 58 | 59 | ####################################### 60 | # Instances (KEYWORD2) 61 | ####################################### 62 | 63 | sensor KEYWORD2 64 | 65 | ####################################### 66 | # Constants (LITERAL1) 67 | ####################################### -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 4 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 5 | 6 | ## [Unreleased] 7 | 8 | ## [1.1.0] - 2025-7-1 9 | 10 | ### Fixed 11 | 12 | - Wrong computation of sensor variant in method getSensorVariant 13 | 14 | 15 | ## [1.0.0] - 2025-1-30 16 | 17 | ### Added 18 | 19 | - All commands according to data sheet 20 | 21 | ### Changed 22 | 23 | Breaking changes 24 | - The file and class name has changed from SensirionI2CScd4x to SensirionI2cScd4x (the "c" in I2c is now in lower case) 25 | - begin(TwoWire& i2cBus, uint8_t i2cAddress); now takes a second argument for the i2c address 26 | - all methods that have been named xxxTicks are now named xxxRaw 27 | - getDataReadyFlag has been renamed to getDataReadyStatus 28 | - get/setAmbientPressure(uint32_t& aAmbientPressure); now takes the ambient pressure in Pa as uint32_t 29 | - getSerialNumber now returns uint64_t 30 | 31 | 32 | ## [0.4.0] - 2023-03-06 33 | 34 | ### Added 35 | - Methods for ASC (Automatic Self Calibration) 36 | 37 | ### Breaking changes 38 | - Renamed getDataReadyStatus to getDataReadyFlag 39 | 40 | ### Fixed 41 | - RH/T conversion now uses correct constant: `2^16 - 1` 42 | - Fixed a few comments 43 | - Initializing the output variables to zero 44 | 45 | ## [0.3.1] - 2021-04-30 46 | 47 | ### Changed 48 | 49 | * Increase timing for single shot from 1350ms to 5000ms 50 | * Increase timing for self test from 5500ms to 10000ms 51 | 52 | 53 | ## [0.3.0] - 2021-03-01 54 | 55 | ### Added 56 | - Convenience interfaces taking care of unit conversion to and from ticks. 57 | 58 | ### Fixed 59 | - wake-up interface handles missing ACK from sensor on wake up. 60 | 61 | 62 | ## [0.2.0] - 2021-02-10 63 | 64 | ### Changed 65 | 66 | * Updated Sensirion Core library version from 0.4.0 to 0.4.2. This includes the 67 | renaming of the library header file from `SensirionCoreArduinoLibrary.h` to 68 | `SensirionCore.h`. 69 | * Define `SCD4X_I2C_ADDRESS` as hex instead of dec (unchanged value). 70 | 71 | ### Added 72 | 73 | * Added warning about limited EEPROM write cycles. 74 | 75 | ## [0.1.0] - 2021-02-05 76 | 77 | Initial release 78 | 79 | 80 | [Unreleased]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/1.0.0...HEAD 81 | [1.0.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.4.0...1.0.0 82 | [0.4.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.3.1...0.4.0 83 | [0.3.1]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.3.0...0.3.1 84 | [0.3.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.2.0...0.3.0 85 | [0.2.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.1.0...0.2.0 86 | [0.1.0]: https://github.com/Sensirion/arduino-i2c-scd4x/releases/tag/0.1.0 -------------------------------------------------------------------------------- /examples/exampleScd41SingleShot/exampleScd41SingleShot.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS AUTOMATICALLY GENERATED 3 | * 4 | * Generator: sensirion-driver-generator 1.1.2 5 | * Product: scd4x 6 | * Model-Version: 2.0 7 | */ 8 | /* 9 | * Copyright (c) 2025, Sensirion AG 10 | * All rights reserved. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * * Redistributions of source code must retain the above copyright notice, this 16 | * list of conditions and the following disclaimer. 17 | * 18 | * * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * * Neither the name of Sensirion AG nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 30 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 | * POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | #include 39 | #include 40 | #include 41 | 42 | // macro definitions 43 | // make sure that we use the proper definition of NO_ERROR 44 | #ifdef NO_ERROR 45 | #undef NO_ERROR 46 | #endif 47 | #define NO_ERROR 0 48 | 49 | SensirionI2cScd4x sensor; 50 | 51 | static char errorMessage[64]; 52 | static int16_t error; 53 | 54 | void PrintUint64(uint64_t& value) { 55 | Serial.print("0x"); 56 | Serial.print((uint32_t)(value >> 32), HEX); 57 | Serial.print((uint32_t)(value & 0xFFFFFFFF), HEX); 58 | } 59 | 60 | void setup() { 61 | 62 | Serial.begin(115200); 63 | while (!Serial) { 64 | delay(100); 65 | } 66 | Wire.begin(); 67 | sensor.begin(Wire, SCD41_I2C_ADDR_62); 68 | 69 | uint64_t serialNumber = 0; 70 | delay(30); 71 | // Ensure sensor is in clean state 72 | error = sensor.wakeUp(); 73 | if (error != NO_ERROR) { 74 | Serial.print("Error trying to execute wakeUp(): "); 75 | errorToString(error, errorMessage, sizeof errorMessage); 76 | Serial.println(errorMessage); 77 | } 78 | error = sensor.stopPeriodicMeasurement(); 79 | if (error != NO_ERROR) { 80 | Serial.print("Error trying to execute stopPeriodicMeasurement(): "); 81 | errorToString(error, errorMessage, sizeof errorMessage); 82 | Serial.println(errorMessage); 83 | } 84 | error = sensor.reinit(); 85 | if (error != NO_ERROR) { 86 | Serial.print("Error trying to execute reinit(): "); 87 | errorToString(error, errorMessage, sizeof errorMessage); 88 | Serial.println(errorMessage); 89 | } 90 | // Read out information about the sensor 91 | error = sensor.getSerialNumber(serialNumber); 92 | if (error != NO_ERROR) { 93 | Serial.print("Error trying to execute getSerialNumber(): "); 94 | errorToString(error, errorMessage, sizeof errorMessage); 95 | Serial.println(errorMessage); 96 | return; 97 | } 98 | Serial.print("serial number: 0x"); 99 | PrintUint64(serialNumber); 100 | Serial.println(); 101 | // 102 | // If temperature offset and/or sensor altitude compensation 103 | // is required, you should call the respective functions here. 104 | // Check out the header file for the function definitions. 105 | } 106 | 107 | void loop() { 108 | 109 | uint16_t co2Concentration = 0; 110 | float temperature = 0.0; 111 | float relativeHumidity = 0.0; 112 | // 113 | // Wake the sensor up from sleep mode. 114 | // 115 | error = sensor.wakeUp(); 116 | if (error != NO_ERROR) { 117 | Serial.print("Error trying to execute wakeUp(): "); 118 | errorToString(error, errorMessage, sizeof errorMessage); 119 | Serial.println(errorMessage); 120 | return; 121 | } 122 | // 123 | // Ignore first measurement after wake up. 124 | // 125 | error = sensor.measureSingleShot(); 126 | if (error != NO_ERROR) { 127 | Serial.print("Error trying to execute measureSingleShot(): "); 128 | errorToString(error, errorMessage, sizeof errorMessage); 129 | Serial.println(errorMessage); 130 | return; 131 | } 132 | // 133 | // Perform single shot measurement and read data. 134 | // 135 | error = sensor.measureAndReadSingleShot(co2Concentration, temperature, 136 | relativeHumidity); 137 | if (error != NO_ERROR) { 138 | Serial.print("Error trying to execute measureAndReadSingleShot(): "); 139 | errorToString(error, errorMessage, sizeof errorMessage); 140 | Serial.println(errorMessage); 141 | return; 142 | } 143 | // 144 | // Print results in physical units. 145 | // 146 | Serial.print("CO2 concentration [ppm]: "); 147 | Serial.print(co2Concentration); 148 | Serial.println(); 149 | Serial.print("Temperature [°C]: "); 150 | Serial.print(temperature); 151 | Serial.println(); 152 | Serial.print("Relative Humidity [RH]: "); 153 | Serial.print(relativeHumidity); 154 | Serial.println(); 155 | Serial.print("sleep for 5 minutes until next measurement is due"); 156 | Serial.println(); 157 | delay(300000); 158 | } 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sensirion I²C SCD4X Arduino Library 2 | 3 | This is the Sensirion SCD4X library for Arduino allowing you to 4 | communicate with a sensor of the SCD4X family over I²C. 5 | 6 | 7 | 8 | Click [here](https://sensirion.com/products/catalog/SEK-SCD41) to learn more about the Sensirion SCD4X sensor family. 9 | 10 | 11 | Not all sensors of this driver family support all measurements. 12 | In case a measurement is not supported by all sensors, the products that 13 | support it are listed in the API description. 14 | 15 | 16 | 17 | ## Supported sensor types 18 | 19 | | Sensor name | I²C Addresses | 20 | | ------------- | -------------- | 21 | |[SCD40](https://sensirion.com/products/catalog/SCD40)| **0x62**| 22 | |[SCD41](https://sensirion.com/products/catalog/SCD41)| **0x62**| 23 | |[SCD43](https://sensirion.com/products/catalog/SCD43)| **0x62**| 24 | 25 | The following instructions and examples use a *SCD41*. 26 | 27 | 28 | 29 | ## Installation of the library 30 | 31 | This library can be installed using the Arduino Library manager: 32 | Start the [Arduino IDE](http://www.arduino.cc/en/main/software) and open 33 | the Library Manager via 34 | 35 | `Sketch` ➔ `Include Library` ➔ `Manage Libraries...` 36 | 37 | Search for the `Sensirion I2C SCD4X` library in the `Filter 38 | your search...` field and install it by clicking the `install` button. 39 | 40 | If you cannot find it in the library manager, download the latest release as .zip file 41 | and add it to your [Arduino IDE](http://www.arduino.cc/en/main/software) via 42 | 43 | `Sketch` ➔ `Include Library` ➔ `Add .ZIP Library...` 44 | 45 | Don't forget to **install the dependencies** listed below the same way via library 46 | manager or `Add .ZIP Library` 47 | 48 | #### Dependencies 49 | * [Sensirion Core](https://github.com/Sensirion/arduino-core) 50 | 51 | ## Connect the sensor 52 | 53 | Use the following pin description to connect your SCD4X to the standard I²C bus of your Arduino board: 54 | 55 | 56 | 57 | | *Pin* | *Cable Color* | *Name* | *Description* | *Comments* | 58 | |-------|---------------|:------:|----------------|------------| 59 | | 1 | yellow | SCL | I2C: Serial clock input | 60 | | 2 | black | GND | Ground | 61 | | 3 | red | VDD | Supply Voltage | 2.4V to 5.5V 62 | | 4 | green | SDA | I2C: Serial data input / output | 63 | 64 | 65 | 66 | 67 | The recommended voltage is 3.3V. 68 | 69 | ### Board specific wiring 70 | You will find pinout schematics for recommended board models below: 71 | 72 | 73 | 74 |
Arduino Uno 75 |

76 | 77 | | *SCD4X* | *SCD4X Pin* | *Cable Color* | *Board Pin* | 78 | | :---: | --- | --- | --- | 79 | | SCL | 1 | yellow | D19/SCL | 80 | | GND | 2 | black | GND | 81 | | VDD | 3 | red | 3.3V | 82 | | SDA | 4 | green | D18/SDA | 83 | 84 | 85 | 86 | 87 |

88 |
89 | 90 | 91 | 92 | 93 |
Arduino Nano 94 |

95 | 96 | | *SCD4X* | *SCD4X Pin* | *Cable Color* | *Board Pin* | 97 | | :---: | --- | --- | --- | 98 | | SCL | 1 | yellow | A5 | 99 | | GND | 2 | black | GND | 100 | | VDD | 3 | red | 3.3V | 101 | | SDA | 4 | green | A4 | 102 | 103 | 104 | 105 | 106 |

107 |
108 | 109 | 110 | 111 | 112 |
Arduino Micro 113 |

114 | 115 | | *SCD4X* | *SCD4X Pin* | *Cable Color* | *Board Pin* | 116 | | :---: | --- | --- | --- | 117 | | SCL | 1 | yellow | ~D3/SCL | 118 | | GND | 2 | black | GND | 119 | | VDD | 3 | red | 3.3V | 120 | | SDA | 4 | green | D2/SDA | 121 | 122 | 123 | 124 | 125 |

126 |
127 | 128 | 129 | 130 | 131 |
Arduino Mega 2560 132 |

133 | 134 | | *SCD4X* | *SCD4X Pin* | *Cable Color* | *Board Pin* | 135 | | :---: | --- | --- | --- | 136 | | SCL | 1 | yellow | D21/SCL | 137 | | GND | 2 | black | GND | 138 | | VDD | 3 | red | 3.3V | 139 | | SDA | 4 | green | D20/SDA | 140 | 141 | 142 | 143 | 144 |

145 |
146 | 147 | 148 | 149 | 150 |
ESP32 DevKitC 151 |

152 | 153 | | *SCD4X* | *SCD4X Pin* | *Cable Color* | *Board Pin* | 154 | | :---: | --- | --- | --- | 155 | | SCL | 1 | yellow | GPIO 22 | 156 | | GND | 2 | black | GND | 157 | | VDD | 3 | red | 3V3 | 158 | | SDA | 4 | green | GPIO 21 | 159 | 160 | 161 | 162 | 163 |

164 |
165 | 166 | 167 | 168 | ## Quick Start 169 | 170 | 1. Install the libraries and dependencies according to [Installation of the library](#installation-of-the-library) 171 | 172 | 2. Connect the SCD4X sensor to your Arduino as explained in [Connect the sensor](#connect-the-sensor) 173 | 174 | 3. Open the `exampleUsage` sample project within the Arduino IDE: 175 | 176 | `File` ➔ `Examples` ➔ `Sensirion I2C SCD4X` ➔ `exampleUsage` 177 | 178 | 179 | The provided example is working with a SCD41, I²C address 0x62. 180 | In order to use the code with another product or I²C address you need to change it in the code of `examples/exampleUsage`. 181 | You find the list with pre-defined addresses in `src/SensirionI2CSCD4X.h`. 182 | 183 | 184 | 5. Click the `Upload` button in the Arduino IDE or `Sketch` ➔ `Upload` 185 | 186 | 4. When the upload process has finished, open the `Serial Monitor` or `Serial 187 | Plotter` via the `Tools` menu to observe the measurement values. Note that 188 | the `Baud Rate` in the used tool has to be set to `115200 baud`. 189 | 190 | ## Contributing 191 | 192 | **Contributions are welcome!** 193 | 194 | This Sensirion library uses 195 | [`clang-format`](https://releases.llvm.org/download.html) to standardize the 196 | formatting of all our `.cpp` and `.h` files. Make sure your contributions are 197 | formatted accordingly: 198 | 199 | The `-i` flag will apply the format changes to the files listed. 200 | 201 | ```bash 202 | clang-format -i src/*.cpp src/*.h 203 | ``` 204 | 205 | Note that differences from this formatting will result in a failed build until 206 | they are fixed. 207 | : 208 | 209 | ## License 210 | 211 | See [LICENSE](LICENSE). -------------------------------------------------------------------------------- /examples/exampleUsage/exampleUsage.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS AUTOMATICALLY GENERATED 3 | * 4 | * Generator: sensirion-driver-generator 1.1.2 5 | * Product: scd4x 6 | * Model-Version: 2.0 7 | */ 8 | /* 9 | * Copyright (c) 2025, Sensirion AG 10 | * All rights reserved. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * * Redistributions of source code must retain the above copyright notice, this 16 | * list of conditions and the following disclaimer. 17 | * 18 | * * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * * Neither the name of Sensirion AG nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 30 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 | * POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | #include 39 | #include 40 | #include 41 | 42 | // macro definitions 43 | // make sure that we use the proper definition of NO_ERROR 44 | #ifdef NO_ERROR 45 | #undef NO_ERROR 46 | #endif 47 | #define NO_ERROR 0 48 | 49 | SensirionI2cScd4x sensor; 50 | 51 | static char errorMessage[64]; 52 | static int16_t error; 53 | 54 | void PrintUint64(uint64_t& value) { 55 | Serial.print("0x"); 56 | Serial.print((uint32_t)(value >> 32), HEX); 57 | Serial.print((uint32_t)(value & 0xFFFFFFFF), HEX); 58 | } 59 | 60 | void setup() { 61 | 62 | Serial.begin(115200); 63 | while (!Serial) { 64 | delay(100); 65 | } 66 | Wire.begin(); 67 | sensor.begin(Wire, SCD41_I2C_ADDR_62); 68 | 69 | uint64_t serialNumber = 0; 70 | delay(30); 71 | // Ensure sensor is in clean state 72 | error = sensor.wakeUp(); 73 | if (error != NO_ERROR) { 74 | Serial.print("Error trying to execute wakeUp(): "); 75 | errorToString(error, errorMessage, sizeof errorMessage); 76 | Serial.println(errorMessage); 77 | } 78 | error = sensor.stopPeriodicMeasurement(); 79 | if (error != NO_ERROR) { 80 | Serial.print("Error trying to execute stopPeriodicMeasurement(): "); 81 | errorToString(error, errorMessage, sizeof errorMessage); 82 | Serial.println(errorMessage); 83 | } 84 | error = sensor.reinit(); 85 | if (error != NO_ERROR) { 86 | Serial.print("Error trying to execute reinit(): "); 87 | errorToString(error, errorMessage, sizeof errorMessage); 88 | Serial.println(errorMessage); 89 | } 90 | // Read out information about the sensor 91 | error = sensor.getSerialNumber(serialNumber); 92 | if (error != NO_ERROR) { 93 | Serial.print("Error trying to execute getSerialNumber(): "); 94 | errorToString(error, errorMessage, sizeof errorMessage); 95 | Serial.println(errorMessage); 96 | return; 97 | } 98 | Serial.print("serial number: "); 99 | PrintUint64(serialNumber); 100 | Serial.println(); 101 | // 102 | // If temperature offset and/or sensor altitude compensation 103 | // is required, you should call the respective functions here. 104 | // Check out the header file for the function definitions. 105 | // Start periodic measurements (5sec interval) 106 | error = sensor.startPeriodicMeasurement(); 107 | if (error != NO_ERROR) { 108 | Serial.print("Error trying to execute startPeriodicMeasurement(): "); 109 | errorToString(error, errorMessage, sizeof errorMessage); 110 | Serial.println(errorMessage); 111 | return; 112 | } 113 | // 114 | // If low-power mode is required, switch to the low power 115 | // measurement function instead of the standard measurement 116 | // function above. Check out the header file for the definition. 117 | // For SCD41, you can also check out the single shot measurement example. 118 | // 119 | } 120 | 121 | void loop() { 122 | 123 | bool dataReady = false; 124 | uint16_t co2Concentration = 0; 125 | float temperature = 0.0; 126 | float relativeHumidity = 0.0; 127 | // 128 | // Slow down the sampling to 0.2Hz. 129 | // 130 | delay(5000); 131 | error = sensor.getDataReadyStatus(dataReady); 132 | if (error != NO_ERROR) { 133 | Serial.print("Error trying to execute getDataReadyStatus(): "); 134 | errorToString(error, errorMessage, sizeof errorMessage); 135 | Serial.println(errorMessage); 136 | return; 137 | } 138 | while (!dataReady) { 139 | delay(100); 140 | error = sensor.getDataReadyStatus(dataReady); 141 | if (error != NO_ERROR) { 142 | Serial.print("Error trying to execute getDataReadyStatus(): "); 143 | errorToString(error, errorMessage, sizeof errorMessage); 144 | Serial.println(errorMessage); 145 | return; 146 | } 147 | } 148 | // 149 | // If ambient pressure compenstation during measurement 150 | // is required, you should call the respective functions here. 151 | // Check out the header file for the function definition. 152 | error = 153 | sensor.readMeasurement(co2Concentration, temperature, relativeHumidity); 154 | if (error != NO_ERROR) { 155 | Serial.print("Error trying to execute readMeasurement(): "); 156 | errorToString(error, errorMessage, sizeof errorMessage); 157 | Serial.println(errorMessage); 158 | return; 159 | } 160 | // 161 | // Print results in physical units. 162 | Serial.print("CO2 concentration [ppm]: "); 163 | Serial.print(co2Concentration); 164 | Serial.println(); 165 | Serial.print("Temperature [°C]: "); 166 | Serial.print(temperature); 167 | Serial.println(); 168 | Serial.print("Relative Humidity [RH]: "); 169 | Serial.print(relativeHumidity); 170 | Serial.println(); 171 | } 172 | -------------------------------------------------------------------------------- /src/SensirionI2cScd4x.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS AUTOMATICALLY GENERATED 3 | * 4 | * Generator: sensirion-driver-generator 1.1.2 5 | * Product: scd4x 6 | * Model-Version: 2.0 7 | */ 8 | /* 9 | * Copyright (c) 2025, Sensirion AG 10 | * All rights reserved. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * * Redistributions of source code must retain the above copyright notice, this 16 | * list of conditions and the following disclaimer. 17 | * 18 | * * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * * Neither the name of Sensirion AG nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 30 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 | * POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #include "SensirionI2cScd4x.h" 40 | #include 41 | 42 | // make sure that we use the proper definition of NO_ERROR 43 | #ifdef NO_ERROR 44 | #undef NO_ERROR 45 | #endif 46 | #define NO_ERROR 0 47 | 48 | #define ROUND(x) ((int32_t)((x) + 0.5)) 49 | 50 | static uint8_t communication_buffer[9] = {0}; 51 | 52 | SensirionI2cScd4x::SensirionI2cScd4x() { 53 | } 54 | 55 | float SensirionI2cScd4x::signalTemperature(uint16_t rawTemperature) { 56 | float temperature = 0.0; 57 | temperature = -45.0 + ((175.0 * rawTemperature) / 65535.0); 58 | return temperature; 59 | } 60 | 61 | float SensirionI2cScd4x::signalRelativeHumidity(uint16_t rawRelativeHumidity) { 62 | float relativeHumidity = 0.0; 63 | relativeHumidity = (100.0 * rawRelativeHumidity) / 65535.0; 64 | return relativeHumidity; 65 | } 66 | 67 | uint16_t 68 | SensirionI2cScd4x::signalCo2Concentration(uint16_t rawCo2Concentration) { 69 | uint16_t co2Concentration = 0; 70 | co2Concentration = rawCo2Concentration; 71 | return co2Concentration; 72 | } 73 | 74 | float SensirionI2cScd4x::signalTemperatureOffset( 75 | uint16_t rawTemperatureOffset) { 76 | float temperatureOffset = 0.0; 77 | temperatureOffset = (175 * rawTemperatureOffset) / 65535.0; 78 | return temperatureOffset; 79 | } 80 | 81 | uint32_t SensirionI2cScd4x::signalAmbientPressure(uint16_t rawAmbientPressure) { 82 | uint32_t ambientPressure = 0; 83 | ambientPressure = (uint32_t)rawAmbientPressure * 100; 84 | return ambientPressure; 85 | } 86 | 87 | int16_t SensirionI2cScd4x::readMeasurement(uint16_t& aCo2Concentration, 88 | float& aTemperature, 89 | float& aRelativeHumidity) { 90 | uint16_t rawCo2Concentration = 0; 91 | uint16_t rawTemperature = 0; 92 | uint16_t rawRelativeHumidity = 0; 93 | int16_t localError = 0; 94 | localError = readMeasurementRaw(rawCo2Concentration, rawTemperature, 95 | rawRelativeHumidity); 96 | if (localError != NO_ERROR) { 97 | return localError; 98 | } 99 | aCo2Concentration = 100 | SensirionI2cScd4x::signalCo2Concentration(rawCo2Concentration); 101 | aTemperature = SensirionI2cScd4x::signalTemperature(rawTemperature); 102 | aRelativeHumidity = 103 | SensirionI2cScd4x::signalRelativeHumidity(rawRelativeHumidity); 104 | return localError; 105 | } 106 | 107 | int16_t SensirionI2cScd4x::setTemperatureOffset(float temperatureOffset) { 108 | int16_t localError = 0; 109 | uint16_t rawTemperatureOffset = 110 | (uint16_t)ROUND((temperatureOffset * 65535.0) / 175.0); 111 | localError = setTemperatureOffsetRaw(rawTemperatureOffset); 112 | if (localError != NO_ERROR) { 113 | return localError; 114 | } 115 | return localError; 116 | } 117 | 118 | int16_t SensirionI2cScd4x::getTemperatureOffset(float& aTemperatureOffset) { 119 | uint16_t rawTemperatureOffset = 0; 120 | int16_t localError = 0; 121 | localError = getTemperatureOffsetRaw(rawTemperatureOffset); 122 | if (localError != NO_ERROR) { 123 | return localError; 124 | } 125 | aTemperatureOffset = 126 | SensirionI2cScd4x::signalTemperatureOffset(rawTemperatureOffset); 127 | 128 | return localError; 129 | } 130 | 131 | int16_t SensirionI2cScd4x::setAmbientPressure(uint32_t ambientPressure) { 132 | int16_t localError = 0; 133 | uint16_t rawAmbientPressure = (uint16_t)ROUND(ambientPressure / 100.0); 134 | localError = setAmbientPressureRaw(rawAmbientPressure); 135 | if (localError != NO_ERROR) { 136 | return localError; 137 | } 138 | return localError; 139 | } 140 | 141 | int16_t SensirionI2cScd4x::getAmbientPressure(uint32_t& aAmbientPressure) { 142 | uint16_t rawAmbientPressure = 0; 143 | int16_t localError = 0; 144 | localError = getAmbientPressureRaw(rawAmbientPressure); 145 | if (localError != NO_ERROR) { 146 | return localError; 147 | } 148 | aAmbientPressure = 149 | SensirionI2cScd4x::signalAmbientPressure(rawAmbientPressure); 150 | 151 | return localError; 152 | } 153 | 154 | int16_t SensirionI2cScd4x::getDataReadyStatus(bool& arg0) { 155 | uint16_t dataReadyStatus = 0; 156 | int16_t localError = 0; 157 | localError = getDataReadyStatusRaw(dataReadyStatus); 158 | if (localError != NO_ERROR) { 159 | return localError; 160 | } 161 | arg0 = (dataReadyStatus & 2047) != 0; 162 | ; 163 | return localError; 164 | } 165 | 166 | int16_t 167 | SensirionI2cScd4x::getSensorVariant(SCD4xSensorVariant& aSensorVariant) { 168 | SCD4xSensorVariant retVal = SCD4X_SENSOR_VARIANT_MASK; 169 | uint16_t rawSensorVariant = 0; 170 | uint16_t mySensorVariant = 0; 171 | int16_t localError = 0; 172 | retVal = SCD4X_SENSOR_VARIANT_MASK; 173 | uint16_t mask = (uint16_t)(retVal); 174 | localError = getSensorVariantRaw(rawSensorVariant); 175 | if (localError != NO_ERROR) { 176 | return localError; 177 | } 178 | mySensorVariant = (uint16_t)(rawSensorVariant & mask); 179 | if (mySensorVariant == (uint16_t)(SCD4X_SENSOR_VARIANT_SCD40)) { 180 | retVal = SCD4X_SENSOR_VARIANT_SCD40; 181 | } else if (mySensorVariant == (uint16_t)(SCD4X_SENSOR_VARIANT_SCD41)) { 182 | retVal = SCD4X_SENSOR_VARIANT_SCD41; 183 | } else if (mySensorVariant == (uint16_t)(SCD4X_SENSOR_VARIANT_SCD42)) { 184 | retVal = SCD4X_SENSOR_VARIANT_SCD42; 185 | } else if (mySensorVariant == (uint16_t)(SCD4X_SENSOR_VARIANT_SCD43)) { 186 | retVal = SCD4X_SENSOR_VARIANT_SCD43; 187 | } 188 | aSensorVariant = retVal; 189 | return localError; 190 | } 191 | 192 | int16_t SensirionI2cScd4x::measureAndReadSingleShot(uint16_t& aCo2Concentration, 193 | float& aTemperature, 194 | float& aRelativeHumidity) { 195 | bool dataReady = false; 196 | int16_t localError = 0; 197 | localError = measureSingleShot(); 198 | if (localError != NO_ERROR) { 199 | return localError; 200 | } 201 | localError = getDataReadyStatus(dataReady); 202 | if (localError != NO_ERROR) { 203 | return localError; 204 | } 205 | while (!dataReady) { 206 | delay(100); 207 | localError = getDataReadyStatus(dataReady); 208 | if (localError != NO_ERROR) { 209 | return localError; 210 | } 211 | } 212 | localError = 213 | readMeasurement(aCo2Concentration, aTemperature, aRelativeHumidity); 214 | return localError; 215 | } 216 | 217 | int16_t SensirionI2cScd4x::startPeriodicMeasurement() { 218 | int16_t localError = NO_ERROR; 219 | uint8_t* buffer_ptr = communication_buffer; 220 | SensirionI2CTxFrame txFrame = 221 | SensirionI2CTxFrame::createWithUInt16Command(0x21b1, buffer_ptr, 2); 222 | localError = 223 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 224 | if (localError != NO_ERROR) { 225 | return localError; 226 | } 227 | return localError; 228 | } 229 | 230 | int16_t SensirionI2cScd4x::readMeasurementRaw(uint16_t& co2Concentration, 231 | uint16_t& temperature, 232 | uint16_t& relativeHumidity) { 233 | int16_t localError = NO_ERROR; 234 | uint8_t* buffer_ptr = communication_buffer; 235 | SensirionI2CTxFrame txFrame = 236 | SensirionI2CTxFrame::createWithUInt16Command(0xec05, buffer_ptr, 9); 237 | localError = 238 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 239 | if (localError != NO_ERROR) { 240 | return localError; 241 | } 242 | delay(1); 243 | SensirionI2CRxFrame rxFrame(buffer_ptr, 9); 244 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 9, 245 | rxFrame, *_i2cBus); 246 | if (localError != NO_ERROR) { 247 | return localError; 248 | } 249 | localError |= rxFrame.getUInt16(co2Concentration); 250 | localError |= rxFrame.getUInt16(temperature); 251 | localError |= rxFrame.getUInt16(relativeHumidity); 252 | return localError; 253 | } 254 | 255 | int16_t SensirionI2cScd4x::stopPeriodicMeasurement() { 256 | int16_t localError = NO_ERROR; 257 | uint8_t* buffer_ptr = communication_buffer; 258 | SensirionI2CTxFrame txFrame = 259 | SensirionI2CTxFrame::createWithUInt16Command(0x3f86, buffer_ptr, 2); 260 | localError = 261 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 262 | if (localError != NO_ERROR) { 263 | return localError; 264 | } 265 | delay(500); 266 | return localError; 267 | } 268 | 269 | int16_t SensirionI2cScd4x::setTemperatureOffsetRaw(uint16_t offsetTemperature) { 270 | int16_t localError = NO_ERROR; 271 | uint8_t* buffer_ptr = communication_buffer; 272 | SensirionI2CTxFrame txFrame = 273 | SensirionI2CTxFrame::createWithUInt16Command(0x241d, buffer_ptr, 5); 274 | localError |= txFrame.addUInt16(offsetTemperature); 275 | if (localError != NO_ERROR) { 276 | return localError; 277 | } 278 | localError = 279 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 280 | if (localError != NO_ERROR) { 281 | return localError; 282 | } 283 | delay(1); 284 | return localError; 285 | } 286 | 287 | int16_t 288 | SensirionI2cScd4x::getTemperatureOffsetRaw(uint16_t& offsetTemperature) { 289 | int16_t localError = NO_ERROR; 290 | uint8_t* buffer_ptr = communication_buffer; 291 | SensirionI2CTxFrame txFrame = 292 | SensirionI2CTxFrame::createWithUInt16Command(0x2318, buffer_ptr, 3); 293 | localError = 294 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 295 | if (localError != NO_ERROR) { 296 | return localError; 297 | } 298 | delay(1); 299 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 300 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 301 | rxFrame, *_i2cBus); 302 | if (localError != NO_ERROR) { 303 | return localError; 304 | } 305 | localError |= rxFrame.getUInt16(offsetTemperature); 306 | return localError; 307 | } 308 | 309 | int16_t SensirionI2cScd4x::setSensorAltitude(uint16_t sensorAltitude) { 310 | int16_t localError = NO_ERROR; 311 | uint8_t* buffer_ptr = communication_buffer; 312 | SensirionI2CTxFrame txFrame = 313 | SensirionI2CTxFrame::createWithUInt16Command(0x2427, buffer_ptr, 5); 314 | localError |= txFrame.addUInt16(sensorAltitude); 315 | if (localError != NO_ERROR) { 316 | return localError; 317 | } 318 | localError = 319 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 320 | if (localError != NO_ERROR) { 321 | return localError; 322 | } 323 | delay(1); 324 | return localError; 325 | } 326 | 327 | int16_t SensirionI2cScd4x::getSensorAltitude(uint16_t& sensorAltitude) { 328 | int16_t localError = NO_ERROR; 329 | uint8_t* buffer_ptr = communication_buffer; 330 | SensirionI2CTxFrame txFrame = 331 | SensirionI2CTxFrame::createWithUInt16Command(0x2322, buffer_ptr, 3); 332 | localError = 333 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 334 | if (localError != NO_ERROR) { 335 | return localError; 336 | } 337 | delay(1); 338 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 339 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 340 | rxFrame, *_i2cBus); 341 | if (localError != NO_ERROR) { 342 | return localError; 343 | } 344 | localError |= rxFrame.getUInt16(sensorAltitude); 345 | return localError; 346 | } 347 | 348 | int16_t SensirionI2cScd4x::setAmbientPressureRaw(uint16_t ambientPressure) { 349 | int16_t localError = NO_ERROR; 350 | uint8_t* buffer_ptr = communication_buffer; 351 | SensirionI2CTxFrame txFrame = 352 | SensirionI2CTxFrame::createWithUInt16Command(0xe000, buffer_ptr, 5); 353 | localError |= txFrame.addUInt16(ambientPressure); 354 | if (localError != NO_ERROR) { 355 | return localError; 356 | } 357 | localError = 358 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 359 | if (localError != NO_ERROR) { 360 | return localError; 361 | } 362 | delay(1); 363 | return localError; 364 | } 365 | 366 | int16_t SensirionI2cScd4x::getAmbientPressureRaw(uint16_t& ambientPressure) { 367 | int16_t localError = NO_ERROR; 368 | uint8_t* buffer_ptr = communication_buffer; 369 | SensirionI2CTxFrame txFrame = 370 | SensirionI2CTxFrame::createWithUInt16Command(0xe000, buffer_ptr, 3); 371 | localError = 372 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 373 | if (localError != NO_ERROR) { 374 | return localError; 375 | } 376 | delay(1); 377 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 378 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 379 | rxFrame, *_i2cBus); 380 | if (localError != NO_ERROR) { 381 | return localError; 382 | } 383 | localError |= rxFrame.getUInt16(ambientPressure); 384 | return localError; 385 | } 386 | 387 | int16_t 388 | SensirionI2cScd4x::performForcedRecalibration(uint16_t targetCO2Concentration, 389 | uint16_t& frcCorrection) { 390 | int16_t localError = NO_ERROR; 391 | uint8_t* buffer_ptr = communication_buffer; 392 | SensirionI2CTxFrame txFrame = 393 | SensirionI2CTxFrame::createWithUInt16Command(0x362f, buffer_ptr, 5); 394 | localError |= txFrame.addUInt16(targetCO2Concentration); 395 | if (localError != NO_ERROR) { 396 | return localError; 397 | } 398 | localError = 399 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 400 | if (localError != NO_ERROR) { 401 | return localError; 402 | } 403 | delay(400); 404 | SensirionI2CRxFrame rxFrame(buffer_ptr, 5); 405 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 406 | rxFrame, *_i2cBus); 407 | if (localError != NO_ERROR) { 408 | return localError; 409 | } 410 | localError |= rxFrame.getUInt16(frcCorrection); 411 | return localError; 412 | } 413 | 414 | int16_t 415 | SensirionI2cScd4x::setAutomaticSelfCalibrationEnabled(uint16_t ascEnabled) { 416 | int16_t localError = NO_ERROR; 417 | uint8_t* buffer_ptr = communication_buffer; 418 | SensirionI2CTxFrame txFrame = 419 | SensirionI2CTxFrame::createWithUInt16Command(0x2416, buffer_ptr, 5); 420 | localError |= txFrame.addUInt16(ascEnabled); 421 | if (localError != NO_ERROR) { 422 | return localError; 423 | } 424 | localError = 425 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 426 | if (localError != NO_ERROR) { 427 | return localError; 428 | } 429 | delay(1); 430 | return localError; 431 | } 432 | 433 | int16_t 434 | SensirionI2cScd4x::getAutomaticSelfCalibrationEnabled(uint16_t& ascEnabled) { 435 | int16_t localError = NO_ERROR; 436 | uint8_t* buffer_ptr = communication_buffer; 437 | SensirionI2CTxFrame txFrame = 438 | SensirionI2CTxFrame::createWithUInt16Command(0x2313, buffer_ptr, 3); 439 | localError = 440 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 441 | if (localError != NO_ERROR) { 442 | return localError; 443 | } 444 | delay(1); 445 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 446 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 447 | rxFrame, *_i2cBus); 448 | if (localError != NO_ERROR) { 449 | return localError; 450 | } 451 | localError |= rxFrame.getUInt16(ascEnabled); 452 | return localError; 453 | } 454 | 455 | int16_t 456 | SensirionI2cScd4x::setAutomaticSelfCalibrationTarget(uint16_t ascTarget) { 457 | int16_t localError = NO_ERROR; 458 | uint8_t* buffer_ptr = communication_buffer; 459 | SensirionI2CTxFrame txFrame = 460 | SensirionI2CTxFrame::createWithUInt16Command(0x243a, buffer_ptr, 5); 461 | localError |= txFrame.addUInt16(ascTarget); 462 | if (localError != NO_ERROR) { 463 | return localError; 464 | } 465 | localError = 466 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 467 | if (localError != NO_ERROR) { 468 | return localError; 469 | } 470 | delay(1); 471 | return localError; 472 | } 473 | 474 | int16_t 475 | SensirionI2cScd4x::getAutomaticSelfCalibrationTarget(uint16_t& ascTarget) { 476 | int16_t localError = NO_ERROR; 477 | uint8_t* buffer_ptr = communication_buffer; 478 | SensirionI2CTxFrame txFrame = 479 | SensirionI2CTxFrame::createWithUInt16Command(0x233f, buffer_ptr, 3); 480 | localError = 481 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 482 | if (localError != NO_ERROR) { 483 | return localError; 484 | } 485 | delay(1); 486 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 487 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 488 | rxFrame, *_i2cBus); 489 | if (localError != NO_ERROR) { 490 | return localError; 491 | } 492 | localError |= rxFrame.getUInt16(ascTarget); 493 | return localError; 494 | } 495 | 496 | int16_t SensirionI2cScd4x::startLowPowerPeriodicMeasurement() { 497 | int16_t localError = NO_ERROR; 498 | uint8_t* buffer_ptr = communication_buffer; 499 | SensirionI2CTxFrame txFrame = 500 | SensirionI2CTxFrame::createWithUInt16Command(0x21ac, buffer_ptr, 2); 501 | localError = 502 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 503 | if (localError != NO_ERROR) { 504 | return localError; 505 | } 506 | return localError; 507 | } 508 | 509 | int16_t SensirionI2cScd4x::getDataReadyStatusRaw(uint16_t& dataReadyStatus) { 510 | int16_t localError = NO_ERROR; 511 | uint8_t* buffer_ptr = communication_buffer; 512 | SensirionI2CTxFrame txFrame = 513 | SensirionI2CTxFrame::createWithUInt16Command(0xe4b8, buffer_ptr, 3); 514 | localError = 515 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 516 | if (localError != NO_ERROR) { 517 | return localError; 518 | } 519 | delay(1); 520 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 521 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 522 | rxFrame, *_i2cBus); 523 | if (localError != NO_ERROR) { 524 | return localError; 525 | } 526 | localError |= rxFrame.getUInt16(dataReadyStatus); 527 | return localError; 528 | } 529 | 530 | int16_t SensirionI2cScd4x::persistSettings() { 531 | int16_t localError = NO_ERROR; 532 | uint8_t* buffer_ptr = communication_buffer; 533 | SensirionI2CTxFrame txFrame = 534 | SensirionI2CTxFrame::createWithUInt16Command(0x3615, buffer_ptr, 2); 535 | localError = 536 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 537 | if (localError != NO_ERROR) { 538 | return localError; 539 | } 540 | delay(800); 541 | return localError; 542 | } 543 | 544 | int16_t SensirionI2cScd4x::getSerialNumber(uint64_t& serialNumber) { 545 | int16_t localError = NO_ERROR; 546 | uint8_t* buffer_ptr = communication_buffer; 547 | SensirionI2CTxFrame txFrame = 548 | SensirionI2CTxFrame::createWithUInt16Command(0x3682, buffer_ptr, 9); 549 | localError = 550 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 551 | if (localError != NO_ERROR) { 552 | return localError; 553 | } 554 | delay(1); 555 | SensirionI2CRxFrame rxFrame(buffer_ptr, 9); 556 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 9, 557 | rxFrame, *_i2cBus); 558 | if (localError != NO_ERROR) { 559 | return localError; 560 | } 561 | localError |= rxFrame.getInteger(reinterpret_cast(&serialNumber), 562 | LongInteger, 6); 563 | return localError; 564 | } 565 | 566 | int16_t SensirionI2cScd4x::performSelfTest(uint16_t& sensorStatus) { 567 | int16_t localError = NO_ERROR; 568 | uint8_t* buffer_ptr = communication_buffer; 569 | SensirionI2CTxFrame txFrame = 570 | SensirionI2CTxFrame::createWithUInt16Command(0x3639, buffer_ptr, 3); 571 | localError = 572 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 573 | if (localError != NO_ERROR) { 574 | return localError; 575 | } 576 | delay(10000); 577 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 578 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 579 | rxFrame, *_i2cBus); 580 | if (localError != NO_ERROR) { 581 | return localError; 582 | } 583 | localError |= rxFrame.getUInt16(sensorStatus); 584 | return localError; 585 | } 586 | 587 | int16_t SensirionI2cScd4x::performFactoryReset() { 588 | int16_t localError = NO_ERROR; 589 | uint8_t* buffer_ptr = communication_buffer; 590 | SensirionI2CTxFrame txFrame = 591 | SensirionI2CTxFrame::createWithUInt16Command(0x3632, buffer_ptr, 2); 592 | localError = 593 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 594 | if (localError != NO_ERROR) { 595 | return localError; 596 | } 597 | delay(1200); 598 | return localError; 599 | } 600 | 601 | int16_t SensirionI2cScd4x::reinit() { 602 | int16_t localError = NO_ERROR; 603 | uint8_t* buffer_ptr = communication_buffer; 604 | SensirionI2CTxFrame txFrame = 605 | SensirionI2CTxFrame::createWithUInt16Command(0x3646, buffer_ptr, 2); 606 | localError = 607 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 608 | if (localError != NO_ERROR) { 609 | return localError; 610 | } 611 | delay(30); 612 | return localError; 613 | } 614 | 615 | int16_t SensirionI2cScd4x::getSensorVariantRaw(uint16_t& sensorVariant) { 616 | int16_t localError = NO_ERROR; 617 | uint8_t* buffer_ptr = communication_buffer; 618 | SensirionI2CTxFrame txFrame = 619 | SensirionI2CTxFrame::createWithUInt16Command(0x202f, buffer_ptr, 3); 620 | localError = 621 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 622 | if (localError != NO_ERROR) { 623 | return localError; 624 | } 625 | delay(1); 626 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 627 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 628 | rxFrame, *_i2cBus); 629 | if (localError != NO_ERROR) { 630 | return localError; 631 | } 632 | localError |= rxFrame.getUInt16(sensorVariant); 633 | return localError; 634 | } 635 | 636 | int16_t SensirionI2cScd4x::measureSingleShot() { 637 | int16_t localError = NO_ERROR; 638 | uint8_t* buffer_ptr = communication_buffer; 639 | SensirionI2CTxFrame txFrame = 640 | SensirionI2CTxFrame::createWithUInt16Command(0x219d, buffer_ptr, 2); 641 | localError = 642 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 643 | if (localError != NO_ERROR) { 644 | return localError; 645 | } 646 | delay(5000); 647 | return localError; 648 | } 649 | 650 | int16_t SensirionI2cScd4x::measureSingleShotRhtOnly() { 651 | int16_t localError = NO_ERROR; 652 | uint8_t* buffer_ptr = communication_buffer; 653 | SensirionI2CTxFrame txFrame = 654 | SensirionI2CTxFrame::createWithUInt16Command(0x2196, buffer_ptr, 2); 655 | localError = 656 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 657 | if (localError != NO_ERROR) { 658 | return localError; 659 | } 660 | delay(50); 661 | return localError; 662 | } 663 | 664 | int16_t SensirionI2cScd4x::powerDown() { 665 | int16_t localError = NO_ERROR; 666 | uint8_t* buffer_ptr = communication_buffer; 667 | SensirionI2CTxFrame txFrame = 668 | SensirionI2CTxFrame::createWithUInt16Command(0x36e0, buffer_ptr, 2); 669 | localError = 670 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 671 | if (localError != NO_ERROR) { 672 | return localError; 673 | } 674 | delay(1); 675 | return localError; 676 | } 677 | 678 | int16_t SensirionI2cScd4x::wakeUp() { 679 | int16_t localError = NO_ERROR; 680 | uint8_t* buffer_ptr = communication_buffer; 681 | SensirionI2CTxFrame txFrame = 682 | SensirionI2CTxFrame::createWithUInt16Command(0x36f6, buffer_ptr, 2); 683 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 684 | delay(30); 685 | return localError; 686 | } 687 | 688 | int16_t SensirionI2cScd4x::setAutomaticSelfCalibrationInitialPeriod( 689 | uint16_t ascInitialPeriod) { 690 | int16_t localError = NO_ERROR; 691 | uint8_t* buffer_ptr = communication_buffer; 692 | SensirionI2CTxFrame txFrame = 693 | SensirionI2CTxFrame::createWithUInt16Command(0x2445, buffer_ptr, 5); 694 | localError |= txFrame.addUInt16(ascInitialPeriod); 695 | if (localError != NO_ERROR) { 696 | return localError; 697 | } 698 | localError = 699 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 700 | if (localError != NO_ERROR) { 701 | return localError; 702 | } 703 | delay(1); 704 | return localError; 705 | } 706 | 707 | int16_t SensirionI2cScd4x::getAutomaticSelfCalibrationInitialPeriod( 708 | uint16_t& ascInitialPeriod) { 709 | int16_t localError = NO_ERROR; 710 | uint8_t* buffer_ptr = communication_buffer; 711 | SensirionI2CTxFrame txFrame = 712 | SensirionI2CTxFrame::createWithUInt16Command(0x2340, buffer_ptr, 3); 713 | localError = 714 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 715 | if (localError != NO_ERROR) { 716 | return localError; 717 | } 718 | delay(1); 719 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 720 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 721 | rxFrame, *_i2cBus); 722 | if (localError != NO_ERROR) { 723 | return localError; 724 | } 725 | localError |= rxFrame.getUInt16(ascInitialPeriod); 726 | return localError; 727 | } 728 | 729 | int16_t SensirionI2cScd4x::setAutomaticSelfCalibrationStandardPeriod( 730 | uint16_t ascStandardPeriod) { 731 | int16_t localError = NO_ERROR; 732 | uint8_t* buffer_ptr = communication_buffer; 733 | SensirionI2CTxFrame txFrame = 734 | SensirionI2CTxFrame::createWithUInt16Command(0x244e, buffer_ptr, 5); 735 | localError |= txFrame.addUInt16(ascStandardPeriod); 736 | if (localError != NO_ERROR) { 737 | return localError; 738 | } 739 | localError = 740 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 741 | if (localError != NO_ERROR) { 742 | return localError; 743 | } 744 | delay(1); 745 | return localError; 746 | } 747 | 748 | int16_t SensirionI2cScd4x::getAutomaticSelfCalibrationStandardPeriod( 749 | uint16_t& ascStandardPeriod) { 750 | int16_t localError = NO_ERROR; 751 | uint8_t* buffer_ptr = communication_buffer; 752 | SensirionI2CTxFrame txFrame = 753 | SensirionI2CTxFrame::createWithUInt16Command(0x234b, buffer_ptr, 3); 754 | localError = 755 | SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus); 756 | if (localError != NO_ERROR) { 757 | return localError; 758 | } 759 | delay(1); 760 | SensirionI2CRxFrame rxFrame(buffer_ptr, 3); 761 | localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3, 762 | rxFrame, *_i2cBus); 763 | if (localError != NO_ERROR) { 764 | return localError; 765 | } 766 | localError |= rxFrame.getUInt16(ascStandardPeriod); 767 | return localError; 768 | } 769 | 770 | void SensirionI2cScd4x::begin(TwoWire& i2cBus, uint8_t i2cAddress) { 771 | _i2cBus = &i2cBus; 772 | _i2cAddress = i2cAddress; 773 | } 774 | -------------------------------------------------------------------------------- /src/SensirionI2cScd4x.h: -------------------------------------------------------------------------------- 1 | /* 2 | * THIS FILE IS AUTOMATICALLY GENERATED 3 | * 4 | * Generator: sensirion-driver-generator 1.1.2 5 | * Product: scd4x 6 | * Model-Version: 2.0 7 | */ 8 | /* 9 | * Copyright (c) 2025, Sensirion AG 10 | * All rights reserved. 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * * Redistributions of source code must retain the above copyright notice, this 16 | * list of conditions and the following disclaimer. 17 | * 18 | * * Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * * Neither the name of Sensirion AG nor the names of its 23 | * contributors may be used to endorse or promote products derived from 24 | * this software without specific prior written permission. 25 | * 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 27 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 30 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 | * POSSIBILITY OF SUCH DAMAGE. 37 | */ 38 | 39 | #ifndef SENSIRIONI2CSCD4X_H 40 | #define SENSIRIONI2CSCD4X_H 41 | 42 | #include 43 | #include 44 | 45 | #define SCD40_I2C_ADDR_62 0x62 46 | #define SCD41_I2C_ADDR_62 0x62 47 | #define SCD43_I2C_ADDR_62 0x62 48 | 49 | typedef enum { 50 | SCD4X_START_PERIODIC_MEASUREMENT_CMD_ID = 0x21b1, 51 | SCD4X_READ_MEASUREMENT_RAW_CMD_ID = 0xec05, 52 | SCD4X_STOP_PERIODIC_MEASUREMENT_CMD_ID = 0x3f86, 53 | SCD4X_SET_TEMPERATURE_OFFSET_RAW_CMD_ID = 0x241d, 54 | SCD4X_GET_TEMPERATURE_OFFSET_RAW_CMD_ID = 0x2318, 55 | SCD4X_SET_SENSOR_ALTITUDE_CMD_ID = 0x2427, 56 | SCD4X_GET_SENSOR_ALTITUDE_CMD_ID = 0x2322, 57 | SCD4X_SET_AMBIENT_PRESSURE_RAW_CMD_ID = 0xe000, 58 | SCD4X_GET_AMBIENT_PRESSURE_RAW_CMD_ID = 0xe000, 59 | SCD4X_PERFORM_FORCED_RECALIBRATION_CMD_ID = 0x362f, 60 | SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED_CMD_ID = 0x2416, 61 | SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED_CMD_ID = 0x2313, 62 | SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_TARGET_CMD_ID = 0x243a, 63 | SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_TARGET_CMD_ID = 0x233f, 64 | SCD4X_START_LOW_POWER_PERIODIC_MEASUREMENT_CMD_ID = 0x21ac, 65 | SCD4X_GET_DATA_READY_STATUS_RAW_CMD_ID = 0xe4b8, 66 | SCD4X_PERSIST_SETTINGS_CMD_ID = 0x3615, 67 | SCD4X_GET_SERIAL_NUMBER_CMD_ID = 0x3682, 68 | SCD4X_PERFORM_SELF_TEST_CMD_ID = 0x3639, 69 | SCD4X_PERFORM_FACTORY_RESET_CMD_ID = 0x3632, 70 | SCD4X_REINIT_CMD_ID = 0x3646, 71 | SCD4X_GET_SENSOR_VARIANT_RAW_CMD_ID = 0x202f, 72 | SCD4X_MEASURE_SINGLE_SHOT_CMD_ID = 0x219d, 73 | SCD4X_MEASURE_SINGLE_SHOT_RHT_ONLY_CMD_ID = 0x2196, 74 | SCD4X_POWER_DOWN_CMD_ID = 0x36e0, 75 | SCD4X_WAKE_UP_CMD_ID = 0x36f6, 76 | SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_INITIAL_PERIOD_CMD_ID = 0x2445, 77 | SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_INITIAL_PERIOD_CMD_ID = 0x2340, 78 | SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_STANDARD_PERIOD_CMD_ID = 0x244e, 79 | SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_STANDARD_PERIOD_CMD_ID = 0x234b, 80 | } SCD4xCmdId; 81 | 82 | typedef enum { 83 | 84 | SCD4X_SENSOR_VARIANT_MASK = 0x00F000, 85 | SCD4X_SENSOR_VARIANT_SCD40 = 0x000000, 86 | SCD4X_SENSOR_VARIANT_SCD41 = 0x001000, 87 | SCD4X_SENSOR_VARIANT_SCD42 = 0x002000, 88 | SCD4X_SENSOR_VARIANT_SCD43 = 0x005000, 89 | 90 | } SCD4xSensorVariant; 91 | 92 | class SensirionI2cScd4x { 93 | public: 94 | SensirionI2cScd4x(); 95 | /** 96 | * @brief Initializes the SCD4x class. 97 | * 98 | * @param i2cBus Arduino stream object to be used for communication. 99 | */ 100 | void begin(TwoWire& i2cBus, uint8_t i2cAddress); 101 | 102 | /** 103 | * @brief Read CO₂, temperature, and humidity measurements in physical 104 | * units. 105 | * 106 | * Reads the sensor output. The measurement data can only be read out once 107 | * per signal update interval as the buffer is emptied upon read-out. If no 108 | * data is available in the buffer, the sensor returns a NACK. To avoid a 109 | * NACK response, the get_data_ready_status can be issued to check data 110 | * status. The I2C master can abort the read transfer with a NACK followed 111 | * by a STOP condition after any data byte if the user is not interested in 112 | * subsequent data. 113 | * 114 | * @param[out] aCo2Concentration CO₂ concentration in ppm 115 | * @param[out] aTemperature Temperature in °C 116 | * @param[out] aRelativeHumidity Relative humidity in %RH 117 | * 118 | * @return error_code 0 on success, an error code otherwise. 119 | */ 120 | int16_t readMeasurement(uint16_t& aCo2Concentration, float& aTemperature, 121 | float& aRelativeHumidity); 122 | 123 | /** 124 | * @brief Set the temperature compensation offset. 125 | * 126 | * Setting the temperature offset of the SCD4x inside the customer device 127 | * allows the user to optimize the RH and T output signal. The temperature 128 | * offset can depend on several factors such as the SCD4x measurement mode, 129 | * self-heating of close components, the ambient temperature and air flow. 130 | * Thus, the SCD4x temperature offset should be determined after integration 131 | * into the final device and under its typical operating conditions 132 | * (including the operation mode to be used in the application) in thermal 133 | * equilibrium. By default, the temperature offset is set to 4 °C. To save 134 | * the setting to the EEPROM, the persist_settings command may be issued. 135 | * Equation (1) details how the characteristic temperature offset can be 136 | * calculated using the current temperature output of the sensor (TSCD4x), a 137 | * reference temperature value (TReference), and the previous temperature 138 | * offset (Toffset_pervious) obtained using the get_temperature_offset_raw 139 | * command: 140 | * 141 | * Toffset_actual = TSCD4x - TReference + Toffset_pervious. 142 | * 143 | * Recommended temperature offset values are between 0 °C and 20 °C. The 144 | * temperature offset does not impact the accuracy of the CO2 output. 145 | * 146 | * @param[in] temperatureOffset Temperature offset value in °C 147 | * 148 | * @note This command is only available in idle mode. 149 | * 150 | * @return error_code 0 on success, an error code otherwise. 151 | */ 152 | int16_t setTemperatureOffset(float temperatureOffset); 153 | 154 | /** 155 | * @brief Get the temperature compensation offset used by the sensor in °C. 156 | * 157 | * @param[out] aTemperatureOffset Temperature in °C 158 | * 159 | * @note This command is only available in idle mode. 160 | * 161 | * @return error_code 0 on success, an error code otherwise. 162 | */ 163 | int16_t getTemperatureOffset(float& aTemperatureOffset); 164 | 165 | /** 166 | * @brief Set the ambient pressure around the sensor. 167 | * 168 | * The set_ambient_pressure command can be sent during periodic measurements 169 | * to enable continuous pressure compensation. Note that setting an ambient 170 | * pressure overrides any pressure compensation based on a previously set 171 | * sensor altitude. Use of this command is highly recommended for 172 | * applications experiencing significant ambient pressure changes to ensure 173 | * sensor accuracy. Valid input values are between 70000 - 120000 Pa. The 174 | * default value is 101300 Pa. 175 | * 176 | * @param[in] ambientPressure Ambient pressure around the sensor in Pa 177 | * 178 | * @return error_code 0 on success, an error code otherwise. 179 | */ 180 | int16_t setAmbientPressure(uint32_t ambientPressure); 181 | 182 | /** 183 | * @brief Get the ambient pressure around the sensor. 184 | * 185 | * @param[out] aAmbientPressure Pressure in Pa 186 | * 187 | * @return error_code 0 on success, an error code otherwise. 188 | */ 189 | int16_t getAmbientPressure(uint32_t& aAmbientPressure); 190 | 191 | /** 192 | * @brief Read if data is ready. 193 | * 194 | * Polls the sensor for whether data from a periodic or single shot 195 | * measurement is ready to be read out. 196 | * 197 | * @param[out] arg0 198 | * 199 | * @return error_code 0 on success, an error code otherwise. 200 | */ 201 | int16_t getDataReadyStatus(bool& arg0); 202 | 203 | /** 204 | * @brief Reads out the SCD4x sensor variant. 205 | * 206 | * @param[out] aSensorVariant 207 | * 208 | * @note This command is only available in idle mode. 209 | * 210 | * @return error_code 0 on success, an error code otherwise. 211 | */ 212 | int16_t getSensorVariant(SCD4xSensorVariant& aSensorVariant); 213 | 214 | /** 215 | * @brief Start a single shot measurement and read out the data when ready 216 | * 217 | * @param[out] aCo2Concentration CO₂ concentration in ppm 218 | * @param[out] aTemperature Temperature in °C 219 | * @param[out] aRelativeHumidity Relative humidity in %RH 220 | * 221 | * @return error_code 0 on success, an error code otherwise. 222 | */ 223 | int16_t measureAndReadSingleShot(uint16_t& aCo2Concentration, 224 | float& aTemperature, 225 | float& aRelativeHumidity); 226 | 227 | /** 228 | * @brief Start periodic measurement mode. 229 | * 230 | * Starts the periodic measurement mode. The signal update interval is 5 231 | * seconds. 232 | * 233 | * @note This command is only available in idle mode. 234 | * 235 | * @return error_code 0 on success, an error code otherwise. 236 | */ 237 | int16_t startPeriodicMeasurement(); 238 | 239 | /** 240 | * @brief Read CO₂, temperature, and humidity measurements raw values. 241 | * 242 | * Reads the sensor output. The measurement data can only be read out once 243 | * per signal update interval as the buffer is emptied upon read-out. If no 244 | * data is available in the buffer, the sensor returns a NACK. To avoid a 245 | * NACK response, the get_data_ready_status can be issued to check data 246 | * status. The I2C master can abort the read transfer with a NACK followed 247 | * by a STOP condition after any data byte if the user is not interested in 248 | * subsequent data. 249 | * 250 | * @param[out] co2Concentration CO₂ concentration in ppm 251 | * @param[out] temperature Convert to degrees celsius by (175 * value / 252 | * 65535) - 45 253 | * @param[out] relativeHumidity Convert to relative humidity in % by (100 * 254 | * value / 65535) 255 | * 256 | * @return error_code 0 on success, an error code otherwise. 257 | */ 258 | int16_t readMeasurementRaw(uint16_t& co2Concentration, 259 | uint16_t& temperature, 260 | uint16_t& relativeHumidity); 261 | 262 | /** 263 | * @brief Stop periodic measurement to change the sensor configuration or to 264 | * save power. 265 | * 266 | * Command returns a sensor running in periodic measurement mode or low 267 | * power periodic measurement mode back to the idle state, e.g. to then 268 | * allow changing the sensor configuration or to save power. 269 | * 270 | * @return error_code 0 on success, an error code otherwise. 271 | */ 272 | int16_t stopPeriodicMeasurement(); 273 | 274 | /** 275 | * @brief Set the temperature compensation offset (raw value). 276 | * 277 | * Setting the temperature offset of the SCD4x inside the customer device 278 | * allows the user to optimize the RH and T output signal. The temperature 279 | * offset can depend on several factors such as the SCD4x measurement mode, 280 | * self-heating of close components, the ambient temperature and air flow. 281 | * Thus, the SCD4x temperature offset should be determined after integration 282 | * into the final device and under its typical operating conditions 283 | * (including the operation mode to be used in the application) in thermal 284 | * equilibrium. By default, the temperature offset is set to 4 °C. To save 285 | * the setting to the EEPROM, the persist_settings command may be issued. 286 | * Equation (1) details how the characteristic temperature offset can be 287 | * calculated using the current temperature output of the sensor (TSCD4x), a 288 | * reference temperature value (TReference), and the previous temperature 289 | * offset (Toffset_pervious) obtained using the get_temperature_offset_raw 290 | * command: 291 | * 292 | * Toffset_actual = TSCD4x - TReference + Toffset_pervious. 293 | * 294 | * Recommended temperature offset values are between 0 °C and 20 °C. The 295 | * temperature offset does not impact the accuracy of the CO2 output. 296 | * 297 | * @param[in] offsetTemperature Temperature offset. Convert Toffset in °C to 298 | * value by: (Toffset * 65535 / 175) 299 | * 300 | * @note This command is only available in idle mode. 301 | * 302 | * @return error_code 0 on success, an error code otherwise. 303 | * 304 | * Example: 305 | * -------- 306 | * 307 | * @code{.cpp} 308 | * 309 | * int16_t localError = 0; 310 | * localError = sensor.setTemperatureOffsetRaw(1498); 311 | * if (localError != NO_ERROR) { 312 | * return; 313 | * } 314 | * 315 | * @endcode 316 | * 317 | */ 318 | int16_t setTemperatureOffsetRaw(uint16_t offsetTemperature); 319 | 320 | /** 321 | * @brief Get the raw temperature compensation offset used by the sensor. 322 | * 323 | * @param[out] offsetTemperature Convert to °C by (175 * value / 65535) 324 | * 325 | * @note This command is only available in idle mode. 326 | * 327 | * @return error_code 0 on success, an error code otherwise. 328 | */ 329 | int16_t getTemperatureOffsetRaw(uint16_t& offsetTemperature); 330 | 331 | /** 332 | * @brief Set the altitude of the sensor (in meters above sea level). 333 | * 334 | * Typically, the sensor altitude is set once after device installation. To 335 | * save the setting to the EEPROM, the persist_settings command must be 336 | * issued. The default sensor altitude value is set to 0 meters above sea 337 | * level. Note that setting a sensor altitude to the sensor overrides any 338 | * pressure compensation based on a previously set ambient pressure. 339 | * 340 | * @param[in] sensorAltitude Sensor altitude in meters above sea level. 341 | * Valid input values are between 0 - 3000 m. 342 | * 343 | * @note This command is only available in idle mode. 344 | * 345 | * @return error_code 0 on success, an error code otherwise. 346 | * 347 | * Example: 348 | * -------- 349 | * 350 | * @code{.cpp} 351 | * 352 | * int16_t localError = 0; 353 | * localError = sensor.setSensorAltitude(0); 354 | * if (localError != NO_ERROR) { 355 | * return; 356 | * } 357 | * 358 | * @endcode 359 | * 360 | */ 361 | int16_t setSensorAltitude(uint16_t sensorAltitude); 362 | 363 | /** 364 | * @brief Get the sensor altitude used by the sensor. 365 | * 366 | * @param[out] sensorAltitude Sensor altitude used by the sensor in meters 367 | * above sea level. 368 | * 369 | * @note This command is only available in idle mode. 370 | * 371 | * @return error_code 0 on success, an error code otherwise. 372 | */ 373 | int16_t getSensorAltitude(uint16_t& sensorAltitude); 374 | 375 | /** 376 | * @brief Set the raw ambient pressure value. 377 | * 378 | * The set_ambient_pressure command can be sent during periodic measurements 379 | * to enable continuous pressure compensation. Note that setting an ambient 380 | * pressure overrides any pressure compensation based on a previously set 381 | * sensor altitude. Use of this command is highly recommended for 382 | * applications experiencing significant ambient pressure changes to ensure 383 | * sensor accuracy. Valid input values are between 70000 - 120000 Pa. The 384 | * default value is 101300 Pa. 385 | * 386 | * @param[in] ambientPressure Convert ambient_pressure in hPa to Pa by 387 | * ambient_pressure / 100. 388 | * 389 | * @note Available during measurements. 390 | * 391 | * @return error_code 0 on success, an error code otherwise. 392 | * 393 | * Example: 394 | * -------- 395 | * 396 | * @code{.cpp} 397 | * 398 | * int16_t localError = 0; 399 | * localError = sensor.setAmbientPressureRaw(1013); 400 | * if (localError != NO_ERROR) { 401 | * return; 402 | * } 403 | * 404 | * @endcode 405 | * 406 | */ 407 | int16_t setAmbientPressureRaw(uint16_t ambientPressure); 408 | 409 | /** 410 | * @brief Get the ambient pressure around the sensor. 411 | * 412 | * @param[out] ambientPressure Convert to Pa by value = ambient_pressure * 413 | * 100. 414 | * 415 | * @return error_code 0 on success, an error code otherwise. 416 | */ 417 | int16_t getAmbientPressureRaw(uint16_t& ambientPressure); 418 | 419 | /** 420 | * @brief Perform a forced recalibration (FRC) of the CO₂ concentration. 421 | * 422 | * To successfully conduct an accurate FRC, the following steps need to be 423 | * carried out: 424 | * 425 | * 1. Operate the SCD4x in the operation mode later used for normal sensor 426 | * operation (e.g. periodic measurement) for at least 3 minutes in an 427 | * environment with a homogenous and constant CO2 concentration. The sensor 428 | * must be operated at the voltage desired for the application when 429 | * performing the FRC sequence. 2. Issue the stop_periodic_measurement 430 | * command. 3. Issue the perform_forced_recalibration command. 431 | * 432 | * A return value of 0xffff indicates that the FRC has failed because the 433 | * sensor was not operated before sending the command. 434 | * 435 | * @param[in] targetCO2Concentration Target CO₂ concentration in ppm CO₂. 436 | * @param[out] frcCorrection Convert to FRC correction in ppm CO₂ by 437 | * frc_correction - 0x8000. A return value of 0xFFFF indicates that the FRC 438 | * has failed because the sensor was not operated before sending the 439 | * command. 440 | * 441 | * @note This command is only available in idle mode. 442 | * 443 | * @return error_code 0 on success, an error code otherwise. 444 | */ 445 | int16_t performForcedRecalibration(uint16_t targetCO2Concentration, 446 | uint16_t& frcCorrection); 447 | 448 | /** 449 | * @brief Enable or disable automatic self calibration (ASC). 450 | * 451 | * Sets the current state (enabled / disabled) of the ASC. By default, ASC 452 | * is enabled. To save the setting to the EEPROM, the persist_settings 453 | * command must be issued. The ASC enables excellent long-term stability of 454 | * SCD4x without the need for regular user intervention. The algorithm 455 | * leverages the sensor's measurement history and the assumption of exposure 456 | * of the sensor to a known minimum background CO₂ concentration at least 457 | * once over a period of cumulative operation. By default, the ASC algorithm 458 | * assumes that the sensor is exposed to outdoor fresh air at 400 ppm CO₂ 459 | * concentration at least once per week of accumulated operation using one 460 | * of the following measurement modes for at least 4 hours without 461 | * interruption at a time: periodic measurement mode, low power periodic 462 | * measurement mode or single shot mode with a measurement interval of 5 463 | * minutes (SCD41 only). 464 | * 465 | * @param[in] ascEnabled 1 enables ASC, 0 disables ASC. 466 | * 467 | * @note This command is only available in idle mode. 468 | * 469 | * @return error_code 0 on success, an error code otherwise. 470 | * 471 | * Example: 472 | * -------- 473 | * 474 | * @code{.cpp} 475 | * 476 | * int16_t localError = 0; 477 | * localError = sensor.setAutomaticSelfCalibrationEnabled(1); 478 | * if (localError != NO_ERROR) { 479 | * return; 480 | * } 481 | * 482 | * @endcode 483 | * 484 | */ 485 | int16_t setAutomaticSelfCalibrationEnabled(uint16_t ascEnabled); 486 | 487 | /** 488 | * @brief Check if automatic self calibration (ASC) is enabled. 489 | * 490 | * @param[out] ascEnabled 1 if ASC is enabled, 0 if ASC is disabled. 491 | * 492 | * @note This command is only available in idle mode. 493 | * 494 | * @return error_code 0 on success, an error code otherwise. 495 | */ 496 | int16_t getAutomaticSelfCalibrationEnabled(uint16_t& ascEnabled); 497 | 498 | /** 499 | * @brief Set the value of ASC baseline target in ppm. 500 | * 501 | * Sets the value of the ASC baseline target, i.e. the CO₂ concentration in 502 | * ppm which the ASC algorithm will assume as lower-bound background to 503 | * which the SCD4x is exposed to regularly within one ASC period of 504 | * operation. To save the setting to the EEPROM, the persist_settings 505 | * command must be issued subsequently. The factory default value is 400 506 | * ppm. 507 | * 508 | * @param[in] ascTarget ASC baseline value in ppm CO₂ 509 | * 510 | * @note This command is only available in idle mode. 511 | * 512 | * @return error_code 0 on success, an error code otherwise. 513 | * 514 | * Example: 515 | * -------- 516 | * 517 | * @code{.cpp} 518 | * 519 | * int16_t localError = 0; 520 | * localError = sensor.setAutomaticSelfCalibrationTarget(400); 521 | * if (localError != NO_ERROR) { 522 | * return; 523 | * } 524 | * 525 | * @endcode 526 | * 527 | */ 528 | int16_t setAutomaticSelfCalibrationTarget(uint16_t ascTarget); 529 | 530 | /** 531 | * @brief Reads out the ASC baseline target concentration parameter. 532 | * 533 | * @param[out] ascTarget ASC baseline target concentration parameter in ppm 534 | * CO₂. 535 | * 536 | * @note This command is only available in idle mode. 537 | * 538 | * @return error_code 0 on success, an error code otherwise. 539 | */ 540 | int16_t getAutomaticSelfCalibrationTarget(uint16_t& ascTarget); 541 | 542 | /** 543 | * @brief Start a low-power periodic measurement (interval 30 s). 544 | * 545 | * To enable use-cases with a constrained power budget, the SCD4x features a 546 | * low power periodic measurement mode with a signal update interval of 547 | * approximately 30 seconds. The low power periodic measurement mode is 548 | * initiated using the start_low_power_periodic_measurement command and 549 | * read-out in a similar manner as the periodic measurement mode using the 550 | * read_measurement command. To periodically check whether a new measurement 551 | * result is available for read out, the get_data_ready_status command can 552 | * be used to synchronize to the sensor's internal measurement interval as 553 | * an alternative to relying on the ACK/NACK status of the 554 | * read_measurement_command. 555 | * 556 | * @return error_code 0 on success, an error code otherwise. 557 | */ 558 | int16_t startLowPowerPeriodicMeasurement(); 559 | 560 | /** 561 | * @brief Read if data is ready. 562 | * 563 | * Polls the sensor for whether data from a periodic or single shot 564 | * measurement is ready to be read out. 565 | * 566 | * @param[out] dataReadyStatus If one or more of the 11 least significant 567 | * bits are 1, then the data is ready. 568 | * 569 | * @return error_code 0 on success, an error code otherwise. 570 | */ 571 | int16_t getDataReadyStatusRaw(uint16_t& dataReadyStatus); 572 | 573 | /** 574 | * @brief Store volatile sensor settings in the EEPROM. 575 | * 576 | * Configuration settings such as the temperature offset, sensor altitude 577 | * and the ASC enabled/disabled parameters are by default stored in the 578 | * volatile memory (RAM) only. The persist_settings command stores the 579 | * current configuration in the EEPROM of the SCD4x, ensuring the current 580 | * settings persist after power-cycling. To avoid unnecessary wear of the 581 | * EEPROM, the persist_settings command should only be sent following 582 | * configuration changes whose persistence is required. The EEPROM is 583 | * guaranteed to withstand at least 2000 write cycles. Note that field 584 | * calibration history (i.e. FRC and ASC) is automatically stored in a 585 | * separate EEPROM dimensioned for the specified sensor lifetime when 586 | * operated continuously in either periodic measurement mode, low power 587 | * periodic measurement mode or single shot mode with 5 minute measurement 588 | * interval (SCD41 only). 589 | * 590 | * @note This command is only available in idle mode. 591 | * 592 | * @return error_code 0 on success, an error code otherwise. 593 | */ 594 | int16_t persistSettings(); 595 | 596 | /** 597 | * @brief Read the sensor's unique serial number. 598 | * 599 | * Reading out the serial number can be used to identify the chip and to 600 | * verify the presence of the sensor. The get_serial_number command returns 601 | * 3 words, and every word is followed by an 8-bit CRC checksum. Together, 602 | * the 3 words constitute a unique serial number with a length of 48 bits 603 | * (in big endian format). This method takes care of converting the serial 604 | * number from the 3 words to an 64 bit unsigned integer. 605 | * 606 | * @param[out] serialNumber of the sensor as integer. 607 | * 608 | * @note This command is only available in idle mode. 609 | * 610 | * @return error_code 0 on success, an error code otherwise. 611 | */ 612 | int16_t getSerialNumber(uint64_t& serialNumber); 613 | 614 | /** 615 | * @brief Perform self test to assess sensor functionality and power supply. 616 | * 617 | * Can be used as an end-of-line test to check the sensor functionality. 618 | * 619 | * @param[out] sensorStatus If sensor status is equal to 0, no malfunction 620 | * has been detected. 621 | * 622 | * @note This command is only available in idle mode. 623 | * 624 | * @return error_code 0 on success, an error code otherwise. 625 | */ 626 | int16_t performSelfTest(uint16_t& sensorStatus); 627 | 628 | /** 629 | * @brief Perform factory reset to erase the settings stored in the EEPROM. 630 | * 631 | * The perform_factory_reset command resets all configuration settings 632 | * stored in the EEPROM and erases the FRC and ASC algorithm history. 633 | * 634 | * @note This command is only available in idle mode. 635 | * 636 | * @return error_code 0 on success, an error code otherwise. 637 | */ 638 | int16_t performFactoryReset(); 639 | 640 | /** 641 | * @brief Reinitialize the sensor by reloading the settings from the EEPROM. 642 | * 643 | * The reinit command reinitialize the sensor by reloading user settings 644 | * from EEPROM. The sensor must be in the idle state before sending the 645 | * reinit command. If the reinit command does not trigger the desired 646 | * re-initialization, a power-cycle should be applied to the SCD4x. 647 | * 648 | * @note This command is only available in idle mode. 649 | * 650 | * @return error_code 0 on success, an error code otherwise. 651 | */ 652 | int16_t reinit(); 653 | 654 | /** 655 | * @brief Reads out the SCD4x sensor variant. 656 | * 657 | * @param[out] sensorVariant Bits[15…12] = 0000 → SCD40 Bits[15…12] = 0001 → 658 | * SCD41 659 | * 660 | * @note This command is only available in idle mode. 661 | * 662 | * @return error_code 0 on success, an error code otherwise. 663 | */ 664 | int16_t getSensorVariantRaw(uint16_t& sensorVariant); 665 | 666 | /** 667 | * @brief On-demand measurement of the CO₂ concentration, temperature, and 668 | * humidity. 669 | * 670 | * The sensor output is read out by using the read_measurement command. The 671 | * fastest possible sampling interval for single shot measurements is 5 672 | * seconds. The ASC is enabled by default in single shot operation and 673 | * optimized for single shot measurements performed every 5 minutes. For 674 | * more details about single shot measurements and optimization of power 675 | * consumption please refer to the datasheet. 676 | * 677 | * @note This command is only available for SCD41. 678 | * 679 | * @return error_code 0 on success, an error code otherwise. 680 | */ 681 | int16_t measureSingleShot(); 682 | 683 | /** 684 | * @brief On-demand measurement of the temperature and humidity only. 685 | * 686 | * For more details about single shot measurements and optimization of power 687 | * consumption please refer to the datasheet. 688 | * 689 | * @note This command is only available for SCD41. 690 | * 691 | * @return error_code 0 on success, an error code otherwise. 692 | */ 693 | int16_t measureSingleShotRhtOnly(); 694 | 695 | /** 696 | * @brief Put the sensor into sleep mode from idle mode. 697 | * 698 | * Put the sensor from idle to sleep to reduce power consumption. Can be 699 | * used to power down when operating the sensor in power-cycled single shot 700 | * mode. 701 | * 702 | * @note This command is only available in idle mode. Only for SCD41. 703 | * 704 | * @return error_code 0 on success, an error code otherwise. 705 | */ 706 | int16_t powerDown(); 707 | 708 | /** 709 | * @brief Wake up sensor from sleep mode to idle mode. 710 | * 711 | * Wake up the sensor from sleep mode into idle mode. Note that the SCD4x 712 | * does not acknowledge the wake_up command. The sensor's idle state after 713 | * wake up can be verified by reading out the serial number. 714 | * 715 | * @note This command is only available for SCD41. 716 | * 717 | * @return error_code 0 on success, an error code otherwise. 718 | */ 719 | int16_t wakeUp(); 720 | 721 | /** 722 | * @brief Sets the initial period for ASC correction 723 | * 724 | * Sets the duration of the initial period for ASC correction (in hours). By 725 | * default, the initial period for ASC correction is 44 hours. Allowed 726 | * values are integer multiples of 4 hours. A value of 0 results in an 727 | * immediate correction. To save the setting to the EEPROM, the 728 | * persist_settings command must be issued. 729 | * 730 | * For single shot operation, this parameter always assumes a measurement 731 | * interval of 5 minutes, counting the number of single shots to calculate 732 | * elapsed time. If single shot measurements are taken more / less 733 | * frequently than once every 5 minutes, this parameter must be scaled 734 | * accordingly to achieve the intended period in hours (e.g. for a 10-minute 735 | * measurement interval, the scaled parameter value is obtained by 736 | * multiplying the intended period in hours by 0.5). 737 | * 738 | * @param[in] ascInitialPeriod ASC initial period in hours 739 | * 740 | * @note This command is available for SCD41 and only in idle mode. 741 | * 742 | * @return error_code 0 on success, an error code otherwise. 743 | * 744 | * Example: 745 | * -------- 746 | * 747 | * @code{.cpp} 748 | * 749 | * int16_t localError = 0; 750 | * localError = sensor.setAutomaticSelfCalibrationInitialPeriod(44); 751 | * if (localError != NO_ERROR) { 752 | * return; 753 | * } 754 | * 755 | * @endcode 756 | * 757 | */ 758 | int16_t setAutomaticSelfCalibrationInitialPeriod(uint16_t ascInitialPeriod); 759 | 760 | /** 761 | * @brief Read out the initial period for ASC correction 762 | * 763 | * @param[out] ascInitialPeriod ASC initial period in hours 764 | * 765 | * @note This command is only available for SCD41 and only in idle mode. 766 | * 767 | * @return error_code 0 on success, an error code otherwise. 768 | */ 769 | int16_t 770 | getAutomaticSelfCalibrationInitialPeriod(uint16_t& ascInitialPeriod); 771 | 772 | /** 773 | * @brief Sets the standard period for ASC correction. 774 | * 775 | * Sets the standard period for ASC correction (in hours). By default, the 776 | * standard period for ASC correction is 156 hours. Allowed values are 777 | * integer multiples of 4 hours. Note: a value of 0 results in an immediate 778 | * correction. To save the setting to the EEPROM, the persist_settings (see 779 | * Section 3.10.1) command must be issued. 780 | * 781 | * For single shot operation, this parameter always assumes a measurement 782 | * interval of 5 minutes, counting the number of single shots to calculate 783 | * elapsed time. If single shot measurements are taken more / less 784 | * frequently than once every 5 minutes, this parameter must be scaled 785 | * accordingly to achieve the intended period in hours (e.g. for a 10-minute 786 | * measurement interval, the scaled parameter value is obtained by 787 | * multiplying the intended period in hours by 0.5). 788 | * 789 | * @param[in] ascStandardPeriod ASC standard period in hours 790 | * 791 | * @note This command is only available for SCD41 and only in idle mode. 792 | * 793 | * @return error_code 0 on success, an error code otherwise. 794 | * 795 | * Example: 796 | * -------- 797 | * 798 | * @code{.cpp} 799 | * 800 | * int16_t localError = 0; 801 | * localError = sensor.setAutomaticSelfCalibrationStandardPeriod(156); 802 | * if (localError != NO_ERROR) { 803 | * return; 804 | * } 805 | * 806 | * @endcode 807 | * 808 | */ 809 | int16_t 810 | setAutomaticSelfCalibrationStandardPeriod(uint16_t ascStandardPeriod); 811 | 812 | /** 813 | * @brief Get the standard period for ASC correction. 814 | * 815 | * @param[out] ascStandardPeriod ASC standard period in hours 816 | * 817 | * @note This command is only available for SCD41 and only in idle mode. 818 | * 819 | * @return error_code 0 on success, an error code otherwise. 820 | */ 821 | int16_t 822 | getAutomaticSelfCalibrationStandardPeriod(uint16_t& ascStandardPeriod); 823 | 824 | /** 825 | * @brief signalTemperature 826 | * 827 | * @param[in] rawTemperature 828 | * 829 | * @return Temperature in °C 830 | */ 831 | static float signalTemperature(uint16_t rawTemperature); 832 | 833 | /** 834 | * @brief signalRelativeHumidity 835 | * 836 | * @param[in] rawRelativeHumidity 837 | * 838 | * @return Relative humidity in %RH 839 | */ 840 | static float signalRelativeHumidity(uint16_t rawRelativeHumidity); 841 | 842 | /** 843 | * @brief signalCo2Concentration 844 | * 845 | * @param[in] rawCo2Concentration 846 | * 847 | * @return CO₂ concentration in ppm 848 | */ 849 | static uint16_t signalCo2Concentration(uint16_t rawCo2Concentration); 850 | 851 | /** 852 | * @brief signalTemperatureOffset 853 | * 854 | * @param[in] rawTemperatureOffset 855 | * 856 | * @return Temperature in °C 857 | */ 858 | static float signalTemperatureOffset(uint16_t rawTemperatureOffset); 859 | 860 | /** 861 | * @brief signalAmbientPressure 862 | * 863 | * @param[in] rawAmbientPressure 864 | * 865 | * @return Pressure in Pa 866 | */ 867 | static uint32_t signalAmbientPressure(uint16_t rawAmbientPressure); 868 | 869 | private: 870 | TwoWire* _i2cBus = nullptr; 871 | uint8_t _i2cAddress = 0; 872 | }; 873 | 874 | #endif // SENSIRIONI2CSCD4X_H --------------------------------------------------------------------------------