├── .clang-format ├── .github └── workflows │ ├── arduino_quality_check.yml │ └── github_release.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── exampleScd41SingleShot │ └── exampleScd41SingleShot.ino └── exampleUsage │ └── exampleUsage.ino ├── images ├── Arduino-Mega-2560-Rev3-i2c-pinout-3.3V.png ├── Arduino-Micro-i2c-pinout-3.3V.png ├── Arduino-Nano-i2c-pinout-3.3V.png ├── Arduino-Uno-Rev3-i2c-pinout-3.3V.png ├── SCD41.png ├── SCD41_pinout.png └── esp32-devkitc-i2c-pinout-3.3V.png ├── keywords.txt ├── library.properties ├── metadata.yml └── src ├── SensirionI2cScd4x.cpp └── SensirionI2cScd4x.h /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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.0.0] - 2025-1-30 9 | 10 | ### Added 11 | 12 | - All commands according to data sheet 13 | 14 | ### Changed 15 | 16 | Breaking changes 17 | - The file and class name has changed from SensirionI2CScd4x to SensirionI2cScd4x (the "c" in I2c is now in lower case) 18 | - begin(TwoWire& i2cBus, uint8_t i2cAddress); now takes a second argument for the i2c address 19 | - all methods that have been named xxxTicks are now named xxxRaw 20 | - getDataReadyFlag has been renamed to getDataReadyStatus 21 | - get/setAmbientPressure(uint32_t& aAmbientPressure); now takes the ambient pressure in Pa as uint32_t 22 | - getSerialNumber now returns uint64_t 23 | 24 | 25 | ## [0.4.0] - 2023-03-06 26 | 27 | ### Added 28 | - Methods for ASC (Automatic Self Calibration) 29 | 30 | ### Breaking changes 31 | - Renamed getDataReadyStatus to getDataReadyFlag 32 | 33 | ### Fixed 34 | - RH/T conversion now uses correct constant: `2^16 - 1` 35 | - Fixed a few comments 36 | - Initializing the output variables to zero 37 | 38 | ## [0.3.1] - 2021-04-30 39 | 40 | ### Changed 41 | 42 | * Increase timing for single shot from 1350ms to 5000ms 43 | * Increase timing for self test from 5500ms to 10000ms 44 | 45 | 46 | ## [0.3.0] - 2021-03-01 47 | 48 | ### Added 49 | - Convenience interfaces taking care of unit conversion to and from ticks. 50 | 51 | ### Fixed 52 | - wake-up interface handles missing ACK from sensor on wake up. 53 | 54 | 55 | ## [0.2.0] - 2021-02-10 56 | 57 | ### Changed 58 | 59 | * Updated Sensirion Core library version from 0.4.0 to 0.4.2. This includes the 60 | renaming of the library header file from `SensirionCoreArduinoLibrary.h` to 61 | `SensirionCore.h`. 62 | * Define `SCD4X_I2C_ADDRESS` as hex instead of dec (unchanged value). 63 | 64 | ### Added 65 | 66 | * Added warning about limited EEPROM write cycles. 67 | 68 | ## [0.1.0] - 2021-02-05 69 | 70 | Initial release 71 | 72 | 73 | [Unreleased]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/1.0.0...HEAD 74 | [1.0.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.4.0...1.0.0 75 | [0.4.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.3.1...0.4.0 76 | [0.3.1]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.3.0...0.3.1 77 | [0.3.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.2.0...0.3.0 78 | [0.2.0]: https://github.com/Sensirion/arduino-i2c-scd4x/compare/0.1.0...0.2.0 79 | [0.1.0]: https://github.com/Sensirion/arduino-i2c-scd4x/releases/tag/0.1.0 -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 24 | The following instructions and examples use a *SCD41*. 25 | 26 | 27 | 28 | ## Installation of the library 29 | 30 | This library can be installed using the Arduino Library manager: 31 | Start the [Arduino IDE](http://www.arduino.cc/en/main/software) and open 32 | the Library Manager via 33 | 34 | `Sketch` ➔ `Include Library` ➔ `Manage Libraries...` 35 | 36 | Search for the `Sensirion I2C SCD4X` library in the `Filter 37 | your search...` field and install it by clicking the `install` button. 38 | 39 | If you cannot find it in the library manager, download the latest release as .zip file 40 | and add it to your [Arduino IDE](http://www.arduino.cc/en/main/software) via 41 | 42 | `Sketch` ➔ `Include Library` ➔ `Add .ZIP Library...` 43 | 44 | Don't forget to **install the dependencies** listed below the same way via library 45 | manager or `Add .ZIP Library` 46 | 47 | #### Dependencies 48 | * [Sensirion Core](https://github.com/Sensirion/arduino-core) 49 | 50 | ## Connect the sensor 51 | 52 | Use the following pin description to connect your SCD4X to the standard I²C bus of your Arduino board: 53 | 54 | 55 | 56 | | *Pin* | *Cable Color* | *Name* | *Description* | *Comments* | 57 | |-------|---------------|:------:|----------------|------------| 58 | | 1 | yellow | SCL | I2C: Serial clock input | 59 | | 2 | black | GND | Ground | 60 | | 3 | red | VDD | Supply Voltage | 2.4V to 5.5V 61 | | 4 | green | SDA | I2C: Serial data input / output | 62 | 63 | 64 | 65 | 66 | The recommended voltage is 3.3V. 67 | 68 | ### Board specific wiring 69 | You will find pinout schematics for recommended board models below: 70 | 71 | 72 | 73 |
Arduino Uno 74 |

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

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

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

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

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

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

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

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

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

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