├── .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
--------------------------------------------------------------------------------