diff --git a/.github/workflows/embedded_quality_check.yml b/.github/workflows/embedded_quality_check.yml index b229e50..495b778 100644 --- a/.github/workflows/embedded_quality_check.yml +++ b/.github/workflows/embedded_quality_check.yml @@ -1,16 +1,10 @@ name: Quality check on: - pull_request: - branches: - - master push: + pull_request: branches: - master - jobs: driver-quality: uses: sensirion/.github/.github/workflows/driver.c.check.yml@main - with: - examples: '["."]' - run-environment: "ubuntu-20.04" diff --git a/.gitignore b/.gitignore index 051cadf..781a364 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -/scd4x_i2c_example_usage -.idea +/example-usage/scd4x_i2c_example_usage +/tests/scd4x_test_hw_i2c +/tests/scd4x_test_sw_i2c diff --git a/CHANGELOG.md b/CHANGELOG.md index 860b34e..cffad92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,32 +1,22 @@ -# Changelog - -All notable changes to this project will be documented in this file. +# CHANGELOG The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] -## [0.2.1] - 2021-04-30 - -### Changed - -* Increase timing for single shot from 1350ms to 5000ms -* Increase timing for self test from 5500ms to 10000ms - - -## [0.2.0] - 2021-03-01 +## [1.0.0] - 2025-1-30 ### Added -- Convenience interfaces taking care of unit conversion to and from ticks. - -### Fixed -- wake-up interface handles missing ACK from sensor on wake up. +- All commands according to data sheet +## [0.1.0] - 2021-2-1 -## [0.1.0] - 2021-01-28 +### Added -Initial release +- Initial version +- Check latest 0.x.x version for changelog prior to version 1.0.0 -[0.2.1]: https://github.com/Sensirion/embedded-i2c-scd4x/compare/0.2.0...0.2.1 -[0.2.0]: https://github.com/Sensirion/embedded-i2c-scd4x/compare/0.1.0...0.2.0 -[0.1.0]: https://github.com/Sensirion/embedded-i2c-scd4x/releases/tag/0.1.0 +[Unreleased]: https://github.com/Sensirion/embedded-i2c-scd4x/compare/1.0.0...HEAD +[1.0.0]: https://github.com/Sensirion/embedded-i2c-scd4x/compare/0.1.0...1.0.0 +[0.1.0]: https://github.com/Sensirion/embedded-i2c-scd4x/releases/tag/0.1.0 \ No newline at end of file diff --git a/LICENSE b/LICENSE index ff20c41..2062766 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2021, Sensirion AG +Copyright (c) 2025, Sensirion AG All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Makefile b/Makefile deleted file mode 100644 index 7852ac8..0000000 --- a/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -common_sources = sensirion_config.h sensirion_common.h sensirion_common.c -i2c_sources = sensirion_i2c_hal.h sensirion_i2c.h sensirion_i2c.c -scd4x_sources = scd4x_i2c.h scd4x_i2c.c - -i2c_implementation ?= sensirion_i2c_hal.c - -CFLAGS = -Os -Wall -fstrict-aliasing -Wstrict-aliasing=1 -Wsign-conversion -fPIC -I. - -ifdef CI - CFLAGS += -Werror -endif - -.PHONY: all clean - -all: scd4x_i2c_example_usage - -scd4x_i2c_example_usage: clean - $(CC) $(CFLAGS) -o $@ ${scd4x_sources} ${i2c_sources} \ - ${i2c_implementation} ${common_sources} scd4x_i2c_example_usage.c - -clean: - $(RM) scd4x_i2c_example_usage diff --git a/README.md b/README.md index 6586695..8a94531 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,61 @@ -# Sensirion Embedded I2C SCD4x Driver +# Sensirion I2C SCD4X embedded Library -This is a generic embedded driver for the [Sensirion SCD4x Carbon Dioxide Sensor](https://www.sensirion.com/scd4x/). -It enables developers to communicate with the SCD4x sensor on different hardware platforms by only adapting the I2C communication related source files. +This document explains how to set up a sensor of the SCD4X family to run on an embedded device using the I²C interface. -[
](https://sensirion.com/my-scd-ek) + -# Getting started +Click [here](https://sensirion.com/products/catalog/SEK-SCD41) to learn more about the Sensirion SCD4X sensor family. -## Implement the I2C Interface -So we need to adjust two files according to your platform. +Not all sensors of this driver family support all measurements. +In case a measurement is not supported by all sensors, the products that +support it are listed in the API description. + + + +## Supported sensor types + +| Sensor name | I²C Addresses | +| ------------- | -------------- | +|[SCD40](https://sensirion.com/products/catalog/SCD40)| **0x62**| +|[SCD41](https://sensirion.com/products/catalog/SCD41)| **0x62**| + +The following instructions and examples use a *SCD41*. + + + +## Setup Guide + +### Connect the Sensor + +Your sensor has 4 pins that need to be connected to your board: SCL, GND, VDD, SDA. +Use the following description to connect your SCD4X: + + + +| *Pin* | *Cable Color* | *Name* | *Description* | *Comments* | +|-------|---------------|:------:|----------------|------------| +| 1 | yellow | SCL | I2C: Serial clock input | +| 2 | black | GND | Ground | +| 3 | red | VDD | Supply Voltage | 2.4V to 5.5V +| 4 | green | SDA | I2C: Serial data input / output | + + + +The recommended voltage is 3.3V. + +### Configure the code + +In order to use the provided code we need to adjust two files according to your platform. ### Edit `sensirion_i2c_hal.c` -This file contains the implementation of the sensor communication -(how to send requests to the sensor). Therefore, how this is done depends on your -hardware platform. Therefore we can only provide function stubs in which you -can implement the logic yourself. -There are sample implementations available for some platforms: [`sample-implementations`](sample-implementations). -If you are using a Linux based platform like Raspberry Pi -you can just replace the unimplemented HAL template with the provided +This file contains the implementation of the sensor communication, which +depends on your hardware platform. We provide function stubs for your +hardware's own implementation. +Sample implementations are available for some platforms: +[`sample-implementations`](sample-implementations). For Linux based platforms +like Raspberry Pi you can just replace the unimplemented HAL template with the implementation in `sample-implementations/linux_user_space/`: ``` @@ -28,17 +64,16 @@ cp sample-implementations/linux_user_space/sensirion_i2c_hal.c ./ ### Edit `sensirion_config.h` -If you are on a Linux based platform you can skip this part since -everything is already correctly setup for you. +Skip this part for Linux based platforms since everything is already setup for +this case. -Otherwise you need to check if the libraries `` and -`` are provided by your toolchain, compiler or system. -If you have no idea on how to do that you can skip this -step for now and come back when you get errors related to these names when -compiling the driver. -The features we use out of those libraries are standard integer sizes -from `` and `NULL` from ``. If they are not available -you need to specify the following integer types yourself: +Otherwise you need to check if the libraries `` and `` are +provided by your toolchain, compiler or system. If you have no idea on how to +do that you can skip this step for now and come back when you get errors +related to these names when compiling the driver. +The features we use from those libraries are type definitions for integer sizes +from `` and `NULL` from ``. If they are not available you +need to specify the following integer types yourself: * `int64_t` = signed 64bit integer * `uint64_t` = unsigned 64bit integer @@ -49,26 +84,47 @@ you need to specify the following integer types yourself: * `int8_t` = signed 8bit integer * `uint8_t` = unsigned 8bit integer -In addition to that you will need to specify `NULL`. -For both we have a detailed template where you just need to fill in -your system specific values. +In addition to that you will need to specify `NULL`. For both we have a +detailed template where you just need to fill in your system specific values. + +## Choose the i2c address to use with your product + +The provided example is working with a SCD41, I²C address 0x62. +In order to use the code with another product or I²C address you need to change it in the call scd4x_init(ADDRESS) in +`scd4x_i2c_example_usage.c`. The list of supported I²C-addresses is found in the header +`scd4x_i2c.h`. + Now we are ready to compile and run the example usage for your sensor. ## Compile and Run -Take the `.c` and `.h` files directly in this folder pass them to your -favorite C compiler and run the resulting binary. -This step may vary, depending on your platform. Here we demonstrate the -procedure for Linux based platforms: +Pass the source `.c` and header `.h` files in this folder into your C compiler +and run the resulting binary. This step may vary, depending on your platform. +Here we demonstrate the procedure for Linux based platforms: 1. Open up a terminal. 2. Navigate to the directory where this README is located. -3. Run `make` (this compiles all the code here to one binary). -4. Run `./scd4x_i2c_example_usage` (This will run your newly compiled binary). -5. Now you should see the first measurement values appear in your terminal. - As a next step you can adjust the example usage file or write your own - main function to use the sensor. +3. Navigate to the subdirectory example-usage. +4. Run `make` (this compiles the example code into one executable binary). +5. Run the compiled executable with `./scd4x_i2c_example_usage` +6. Now you should see the first measurement values appear in your terminal. As + a next step you can adjust the example usage file or write your own main + function to use the sensor. + +## Compile and Run Tests + +The testframekwork used is CppUTest. Pass the source `.cpp`, `.c` and header `.h` +files from the tests and top level folder into your CPP compiler and run the +resulting binary. This step may vary, depending on your platform. +Here we demonstrate the procedure for Linux based platforms: + +1. Open up a terminal. +2. Install CppUTest framework `apt install cpputest`. +3. Navigate to the directory `tests`. +4. Run `make` (this compiles the test code into one executable binary). +5. Run the compiled executable with `./scd4x_test_hw_i2c`. +6. Now you should see the test output on your console. # Background @@ -76,29 +132,53 @@ procedure for Linux based platforms: ### sensirion\_i2c.[ch] -In these files you can find the implementation of the I2C protocol used by Sensirion -sensors. The functions in these files are used by the embedded driver to build the -correct frame out of data to be sent to the sensor or receive a frame of data from -the sensor and convert it back to data readable by your machine. The functions in -here calculate and check CRCs, reorder bytes for different byte orders and build the -correct formatted frame for your sensor. +In these files you can find the implementation of the I2C protocol used by +Sensirion sensors. The functions in these files are used by the embedded driver +to build the correct frame out of data to be sent to the sensor or receive a +frame of data from the sensor and convert it back to data readable by your +machine. The functions in here calculate and check CRCs, reorder bytes for +different byte orders and build the correct formatted frame for your sensor. ### sensirion\_i2c\_hal.[ch] -In these files you can find the implementation of the hardware abstraction layer used -by Sensirion's I2C embedded drivers. This part of the code is specific to the underlying -hardware platform. This is an unimplemented template for the user to implement. -In the `sample-implementations/` folder we provide implementations for the most common -platforms. +These files contain the implementation of the hardware abstraction layer used +by Sensirion's I2C embedded drivers. This part of the code is specific to the +underlying hardware platform. This is an unimplemented template for the user to +implement. In the `sample-implementations/` folder we provide implementations +for the most common platforms. ### sensirion\_config.h -In this file we keep all the included libraries for our drivers and global defines. -Next to `sensirion_i2c_hal.c` *it's the only file you should need to edit to get your -driver working.* +In this file we keep all the included libraries for our drivers and global +defines. Next to `sensirion_i2c_hal.c` *it's the only file you should need to +edit to get your driver working.* ### sensirion\_common.[ch] -In these files you can find some helper functions used by Sensirion's embedded drivers. -It mostly contains byte order conversions for different variable types. These functions -are also used by the UART embedded drivers therefore they are kept in their own file. +In these files you can find some helper functions used by Sensirion's embedded +drivers. It mostly contains byte order conversions for different variable +types. These functions are also used by the UART embedded drivers therefore +they are kept in their own file. + +## Contributing + +**Contributions are welcome!** + +This Sensirion library uses +[`clang-format`](https://releases.llvm.org/download.html) to standardize the +formatting of all our `.c` and `.h` files. Make sure your contributions are +formatted accordingly: + +The `-i` flag will apply the format changes to the files listed. + +```bash +clang-format -i *.c *.h +``` + +Note that differences from this formatting will result in a failed build until +they are fixed. + + +# License + +See [LICENSE](LICENSE). \ No newline at end of file diff --git a/example-usage/Makefile b/example-usage/Makefile new file mode 100644 index 0000000..37ae2c2 --- /dev/null +++ b/example-usage/Makefile @@ -0,0 +1,23 @@ +src_dir = .. +common_sources = ${src_dir}/sensirion_config.h ${src_dir}/sensirion_common.h ${src_dir}/sensirion_common.c +i2c_sources = ${src_dir}/sensirion_i2c_hal.h ${src_dir}/sensirion_i2c.h ${src_dir}/sensirion_i2c.c +driver_sources = ${src_dir}/scd4x_i2c.h ${src_dir}/scd4x_i2c.c + +i2c_implementation ?= ${src_dir}/sensirion_i2c_hal.c + +CFLAGS = -Os -Wall -fstrict-aliasing -Wstrict-aliasing=1 -Wsign-conversion -fPIC -I${src_dir} -I. + +ifdef CI + CFLAGS += -Werror +endif + +.PHONY: all clean + +all: scd4x_i2c_example_usage + +scd4x_i2c_example_usage: clean + $(CC) $(CFLAGS) -o $@ ${driver_sources} ${i2c_sources} \ + ${i2c_implementation} ${common_sources} scd4x_i2c_example_usage.c + +clean: + $(RM) scd4x_i2c_example_usage \ No newline at end of file diff --git a/example-usage/scd4x_i2c_example_usage.c b/example-usage/scd4x_i2c_example_usage.c new file mode 100644 index 0000000..27f60f8 --- /dev/null +++ b/example-usage/scd4x_i2c_example_usage.c @@ -0,0 +1,141 @@ +/* + * THIS FILE IS AUTOMATICALLY GENERATED + * + * Generator: sensirion-driver-generator 1.1.2 + * Product: scd4x + * Model-Version: 2.0 + */ +/* + * Copyright (c) 2025, Sensirion AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Sensirion AG nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include "scd4x_i2c.h" +#include "sensirion_common.h" +#include "sensirion_i2c_hal.h" +#include // PRIx64 +#include // printf + +#define sensirion_hal_sleep_us sensirion_i2c_hal_sleep_usec + +void convert_and_print_serial(uint16_t* serial_raw) { + uint64_t serial_as_int = 0; + sensirion_common_to_integer((uint8_t*)serial_raw, (uint8_t*)&serial_as_int, + LONG_INTEGER, 6); + printf("0x%" PRIx64, serial_as_int); +} + +int main(void) { + int16_t error = NO_ERROR; + sensirion_i2c_hal_init(); + scd4x_init(SCD41_I2C_ADDR_62); + + uint16_t serial_number[3] = {0}; + sensirion_hal_sleep_us(30000); + // Ensure sensor is in clean state + error = scd4x_wake_up(); + if (error != NO_ERROR) { + printf("error executing wake_up(): %i\n", error); + } + + error = scd4x_stop_periodic_measurement(); + if (error != NO_ERROR) { + printf("error executing stop_periodic_measurement(): %i\n", error); + } + + error = scd4x_reinit(); + if (error != NO_ERROR) { + printf("error executing reinit(): %i\n", error); + } + + // Read out information about the sensor + error = scd4x_get_serial_number(serial_number, 3); + if (error != NO_ERROR) { + printf("error executing get_serial_number(): %i\n", error); + return error; + } + printf("serial number: "); + convert_and_print_serial(serial_number); + printf("\n"); + // + // If temperature offset and/or sensor altitude compensation + // is required, you should call the respective functions here. + // Check out the header file for the function definitions. + // Start periodic measurements (5sec interval) + error = scd4x_start_periodic_measurement(); + if (error != NO_ERROR) { + printf("error executing start_periodic_measurement(): %i\n", error); + return error; + } + // + // If low-power mode is required, switch to the low power + // measurement function instead of the standard measurement + // function above. Check out the header file for the definition. + // + bool data_ready = false; + uint16_t co2_concentration = 0; + uint16_t temperature = 0; + uint16_t relative_humidity = 0; + uint16_t repetition = 0; + for (repetition = 0; repetition < 50; repetition++) { + // + // Slow down the sampling to 0.2Hz. + // + sensirion_hal_sleep_us(5000000); + // + // If ambient pressure compensation during measurement + // is required, you should call the respective functions here. + // Check out the header file for the function definition. + error = scd4x_get_data_ready_status(&data_ready); + if (error != NO_ERROR) { + printf("error executing get_data_ready_status(): %i\n", error); + continue; + } + while (!data_ready) { + sensirion_hal_sleep_us(100000); + error = scd4x_get_data_ready_status(&data_ready); + if (error != NO_ERROR) { + printf("error executing get_data_ready_status(): %i\n", error); + continue; + } + } + error = scd4x_read_measurement_raw(&co2_concentration, &temperature, + &relative_humidity); + if (error != NO_ERROR) { + printf("error executing read_measurement_raw(): %i\n", error); + continue; + } + // + // Print results in physical units. + printf("CO2 concentration [ppm]: %u\n", co2_concentration); + printf("Temperature ticks: %u\n", temperature); + printf("Humidity ticks: %u\n", relative_humidity); + } + + return NO_ERROR; +} diff --git a/images/SCD41.png b/images/SCD41.png new file mode 100644 index 0000000..d6f6c9c Binary files /dev/null and b/images/SCD41.png differ diff --git a/images/SCD41_pinout.png b/images/SCD41_pinout.png new file mode 100644 index 0000000..bc33679 Binary files /dev/null and b/images/SCD41_pinout.png differ diff --git a/images/SCD4x.png b/images/SCD4x.png deleted file mode 100644 index b82a898..0000000 Binary files a/images/SCD4x.png and /dev/null differ diff --git a/metadata.yml b/metadata.yml new file mode 100644 index 0000000..b53c9f0 --- /dev/null +++ b/metadata.yml @@ -0,0 +1,7 @@ +# driver generation metadata +generator_version: 1.1.2 +model_version: '2.0' +dg_status: released +is_manually_modified: false +first_generated: '2021-01-15 10:54' +last_generated: '2025-01-29 12:04' diff --git a/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c b/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c index 18e8863..30ea666 100644 --- a/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c +++ b/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c @@ -57,7 +57,7 @@ void sensirion_i2c_hal_init(void) { void sensirion_i2c_hal_free(void) { } -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { struct i2c_master_packet packet = { .address = address, .data_length = count, @@ -69,7 +69,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { } int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { struct i2c_master_packet packet = { .address = address, .data_length = count, diff --git a/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c b/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c index a946e17..3d2f6d0 100644 --- a/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c +++ b/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c @@ -88,7 +88,7 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { int8_t ret; uint8_t send_ack; uint16_t i; @@ -123,7 +123,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { int8_t ret; uint16_t i; diff --git a/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c b/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c index c251f0f..e26ba9c 100644 --- a/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c +++ b/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c @@ -92,8 +92,8 @@ void sensirion_i2c_hal_free(void) { * error codes: 3 -> error detected by hardware (internal error) * 17 -> driver not ready for new transfer (busy) */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { - int8_t err = nrf_drv_twi_rx(&i2c_instance, address, data, (uint8_t)count); +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { + int8_t err = nrf_drv_twi_rx(&i2c_instance, address, data, count); return err; } @@ -112,9 +112,8 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * 17 -> driver not ready for new transfer (busy) */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { - int8_t err = - nrf_drv_twi_tx(&i2c_instance, address, data, (uint8_t)count, false); + uint8_t count) { + int8_t err = nrf_drv_twi_tx(&i2c_instance, address, data, count, false); return err; } diff --git a/sample-implementations/STM32F1_series/sensirion_i2c_hal.c b/sample-implementations/STM32F1_series/sensirion_i2c_hal.c index 424b2e1..83db176 100644 --- a/sample-implementations/STM32F1_series/sensirion_i2c_hal.c +++ b/sample-implementations/STM32F1_series/sensirion_i2c_hal.c @@ -79,7 +79,7 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { return (int8_t)HAL_I2C_Master_Receive(&hi2c1, (uint16_t)(address << 1), data, count, 100); } @@ -96,7 +96,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { return (int8_t)HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)(address << 1), (uint8_t*)data, count, 100); } diff --git a/sample-implementations/esp32/sensirion_i2c_esp32_config.h b/sample-implementations/esp32/sensirion_i2c_esp32_config.h new file mode 100644 index 0000000..a7e4d15 --- /dev/null +++ b/sample-implementations/esp32/sensirion_i2c_esp32_config.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, Sensirion AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Sensirion AG nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SENSIRION_I2C_ESP32_CONFIG_H +#define SENSIRION_I2C_ESP32_CONFIG_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int esp_err_t; + +struct esp32_i2c_config { + uint32_t freq; + uint8_t addr; + i2c_port_t port; + gpio_num_t sda; + gpio_num_t scl; + bool sda_pullup; + bool scl_pullup; +}; + +extern esp_err_t sensirion_i2c_config_esp32(struct esp32_i2c_config* cfg); +extern esp_err_t sensirion_i2c_esp32_ok(void); + +#ifdef __cplusplus +} +#endif + +#endif /* SENSIRION_I2C_ESP32_CONFIG_H */ diff --git a/sample-implementations/esp32/sensirion_i2c_hal.c b/sample-implementations/esp32/sensirion_i2c_hal.c new file mode 100644 index 0000000..b6c89dc --- /dev/null +++ b/sample-implementations/esp32/sensirion_i2c_hal.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2023, Sensirion AG + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of Sensirion AG nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "sensirion_i2c_hal.h" +#include "sensirion_common.h" +#include "sensirion_config.h" + +#include +#include +#include +#include + +#include "sensirion_i2c_esp32_config.h" + +static const char* TAG = "sensirion_i2c_hal"; + +#define SLEEP_MS(x) \ + vTaskDelay(((x) + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS) +#define CHECK(x) \ + do { \ + esp_err_t __; \ + if ((__ = x) != ESP_OK) \ + return __; \ + } while (0) +#define CHECK_ARG(VAL) \ + do { \ + if (!(VAL)) \ + return ESP_ERR_INVALID_ARG; \ + } while (0) +#define UNUSED_PARAM(x) (void)x + +static i2c_dev_t dev = {0}; +static struct esp32_i2c_config i2c_cfg = {0}; +static esp_err_t i2c_ok = ESP_OK; + +/* + * INSTRUCTIONS + * ============ + * + * Implement all functions where they are marked as IMPLEMENT. + * Follow the function specification in the comments. + */ + +/** + * Select the current i2c bus by index. + * All following i2c operations will be directed at that bus. + * + * THE IMPLEMENTATION IS OPTIONAL ON SINGLE-BUS SETUPS (all sensors on the same + * bus) + * + * @param bus_idx Bus index to select + * @returns 0 on success, an error code otherwise + */ +int16_t sensirion_i2c_hal_select_bus(uint8_t bus_idx) { + /* TODO:IMPLEMENT or leave empty if all sensors are located on one single + * bus + */ + return NOT_IMPLEMENTED_ERROR; +} + +esp_err_t sensirion_i2c_config_esp32(struct esp32_i2c_config* cfg) { + if (cfg != NULL) { + memcpy(&i2c_cfg, cfg, sizeof(*cfg)); + return ESP_OK; + } else { + return ESP_FAIL; + } +} + +esp_err_t sensirion_i2c_esp32_ok(void) { + return i2c_ok; +} + +/** + * Initialize all hard- and software components that are needed for the I2C + * communication. + */ +void sensirion_i2c_hal_init(void) { + memset(&dev, 0, sizeof(i2c_dev_t)); + // dev.addr = addr; + dev.port = i2c_cfg.port; + dev.cfg.mode = I2C_MODE_MASTER; + dev.cfg.sda_io_num = i2c_cfg.sda; + dev.cfg.scl_io_num = i2c_cfg.scl; + dev.cfg.sda_pullup_en = i2c_cfg.sda_pullup; + dev.cfg.scl_pullup_en = i2c_cfg.scl_pullup; +#if HELPER_TARGET_IS_ESP32 + dev.cfg.master.clk_speed = i2c_cfg.freq; +#endif + + esp_err_t err = i2c_dev_create_mutex(&dev); + if (err == ESP_OK) { + ESP_LOGI( + TAG, + "Sensirion I2C initialized. Address: 0x%x Port: %d SDA: %d SCL: %d", + i2c_cfg.addr, i2c_cfg.port, i2c_cfg.sda, i2c_cfg.scl); + } else { + ESP_LOGE(TAG, + "Error initializing Sensirion I2C! Address: 0x%x Port: %d " + "SDA: %d SCL: %d", + i2c_cfg.addr, i2c_cfg.port, i2c_cfg.sda, i2c_cfg.scl); + } + + i2c_ok = err; +} + +/** + * Release all resources initialized by sensirion_i2c_hal_init(). + */ +void sensirion_i2c_hal_free(void) { +} + +/** + * Execute one read transaction on the I2C bus, reading a given number of bytes. + * If the device does not acknowledge the read command, an error shall be + * returned. + * + * @param address 7-bit I2C address to read from + * @param data pointer to the buffer where the data is to be stored + * @param count number of bytes to read from I2C and store in the buffer + * @returns 0 on success, error code otherwise + */ +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { + ESP_LOGI(TAG, "sensirion_i2c_hal_read: len: %d", count); + dev.addr = address; + I2C_DEV_TAKE_MUTEX(&dev); + I2C_DEV_CHECK(&dev, i2c_dev_read(&dev, NULL, 0, data, count)); + I2C_DEV_GIVE_MUTEX(&dev); + ESP_LOGI(TAG, "READ OK"); + return (int8_t)ESP_OK; +} + +/** + * Execute one write transaction on the I2C bus, sending a given number of + * bytes. The bytes in the supplied buffer must be sent to the given address. If + * the slave device does not acknowledge any of the bytes, an error shall be + * returned. + * + * @param address 7-bit I2C address to write to + * @param data pointer to the buffer containing the data to write + * @param count number of bytes to read from the buffer and send over I2C + * @returns 0 on success, error code otherwise + */ +int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, + uint16_t count) { + ESP_LOGI(TAG, "sensirion_i2c_hal_write: len: %d", count); + dev.addr = address; + I2C_DEV_TAKE_MUTEX(&dev); + I2C_DEV_CHECK(&dev, i2c_dev_write(&dev, NULL, 0, data, count)); + I2C_DEV_GIVE_MUTEX(&dev); + ESP_LOGI(TAG, "WRITE OK"); + return (int8_t)ESP_OK; +} + +/** + * Sleep for a given number of microseconds. The function should delay the + * execution for at least the given time, but may also sleep longer. + * + * Despite the unit, a <10 millisecond precision is sufficient. + * + * @param useconds the sleep time in microseconds + */ +void sensirion_i2c_hal_sleep_usec(uint32_t useconds) { + ESP_LOGI(TAG, "sensirion_i2c_hal_sleep: %d usec", useconds); + SLEEP_MS(useconds / 1000); +} diff --git a/sample-implementations/linux_user_space/sensirion_i2c_hal.c b/sample-implementations/linux_user_space/sensirion_i2c_hal.c index d55ecc8..74a7f57 100644 --- a/sample-implementations/linux_user_space/sensirion_i2c_hal.c +++ b/sample-implementations/linux_user_space/sensirion_i2c_hal.c @@ -89,7 +89,7 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { if (i2c_address != address) { ioctl(i2c_device, I2C_SLAVE, address); i2c_address = address; @@ -113,7 +113,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { if (i2c_address != address) { ioctl(i2c_device, I2C_SLAVE, address); i2c_address = address; diff --git a/sample-implementations/mbed/sensirion_i2c_hal.cpp b/sample-implementations/mbed/sensirion_i2c_hal.cpp index 9b3b841..ce846a1 100644 --- a/sample-implementations/mbed/sensirion_i2c_hal.cpp +++ b/sample-implementations/mbed/sensirion_i2c_hal.cpp @@ -63,7 +63,7 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { if (i2c_connection.read(address << 1, (char*)data, count) != 0) return E_MBED_I2C_READ_FAILED; return 0; @@ -81,7 +81,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { if (i2c_connection.write(address << 1, (char*)data, count) != 0) return E_MBED_I2C_WRITE_FAILED; return 0; diff --git a/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c b/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c index fc96443..a0dd0d4 100644 --- a/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c +++ b/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c @@ -91,7 +91,7 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { return i2c_read(i2c_dev, data, count, address); } @@ -107,7 +107,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { return i2c_write(i2c_dev, data, count, address); } diff --git a/scd4x_i2c.c b/scd4x_i2c.c index 47b54de..7712af7 100644 --- a/scd4x_i2c.c +++ b/scd4x_i2c.c @@ -1,12 +1,12 @@ /* - * THIS FILE IS AUTOMATICALLY GENERATED AND MUST NOT BE EDITED MANUALLY! + * THIS FILE IS AUTOMATICALLY GENERATED * - * I2C-Generator: 0.2.0 - * Yaml Version: 0.1.0 - * Template Version: 0.2.1 + * Generator: sensirion-driver-generator 1.1.2 + * Product: scd4x + * Model-Version: 2.0 */ /* - * Copyright (c) 2021, Sensirion AG + * Copyright (c) 2025, Sensirion AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,414 +41,618 @@ #include "sensirion_i2c.h" #include "sensirion_i2c_hal.h" -#define SCD4X_I2C_ADDRESS 98 +#define sensirion_hal_sleep_us sensirion_i2c_hal_sleep_usec -int16_t scd4x_start_periodic_measurement() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21B1); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; - } - sensirion_i2c_hal_sleep_usec(1000); - return NO_ERROR; -} - -int16_t scd4x_read_measurement_ticks(uint16_t* co2, uint16_t* temperature, - uint16_t* humidity) { - int16_t error; - uint8_t buffer[9]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xEC05); +#define ROUND(x) ((int32_t)((x) + 0.5)) - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; - } +static uint8_t communication_buffer[9] = {0}; - sensirion_i2c_hal_sleep_usec(1000); +static uint8_t _i2c_address; - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 6); - if (error) { - return error; - } - *co2 = sensirion_common_bytes_to_uint16_t(&buffer[0]); - *temperature = sensirion_common_bytes_to_uint16_t(&buffer[2]); - *humidity = sensirion_common_bytes_to_uint16_t(&buffer[4]); - return NO_ERROR; +void scd4x_init(uint8_t i2c_address) { + _i2c_address = i2c_address; } -int16_t scd4x_read_measurement(uint16_t* co2, int32_t* temperature_m_deg_c, - int32_t* humidity_m_percent_rh) { - int16_t error; - uint16_t temperature; - uint16_t humidity; - - error = scd4x_read_measurement_ticks(co2, &temperature, &humidity); - if (error) { - return error; - } - *temperature_m_deg_c = ((21875 * (int32_t)temperature) >> 13) - 45000; - *humidity_m_percent_rh = ((12500 * (int32_t)humidity) >> 13); - return NO_ERROR; +uint16_t scd4x_signal_co2_concentration(uint16_t raw_co2_concentration) { + uint16_t co2_concentration = 0; + co2_concentration = raw_co2_concentration; + return co2_concentration; } -int16_t scd4x_stop_periodic_measurement() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3F86); +uint32_t scd4x_signal_ambient_pressure(uint16_t raw_ambient_pressure) { + uint32_t ambient_pressure = 0; + ambient_pressure = (uint32_t)raw_ambient_pressure * 100; + return ambient_pressure; +} - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_set_ambient_pressure(uint32_t ambient_pressure) { + int16_t local_error = 0; + uint16_t raw_ambient_pressure = (uint16_t)ROUND(ambient_pressure / 100.0); + local_error = scd4x_set_ambient_pressure_raw(raw_ambient_pressure); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(500000); - return NO_ERROR; + return local_error; } -int16_t scd4x_get_temperature_offset_ticks(uint16_t* t_offset) { - int16_t error; - uint8_t buffer[3]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2318); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_get_ambient_pressure(uint32_t* a_ambient_pressure) { + uint16_t raw_ambient_pressure = 0; + int16_t local_error = 0; + local_error = scd4x_get_ambient_pressure_raw(&raw_ambient_pressure); + if (local_error != NO_ERROR) { + return local_error; } + *a_ambient_pressure = scd4x_signal_ambient_pressure(raw_ambient_pressure); - sensirion_i2c_hal_sleep_usec(1000); + return local_error; +} - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2); - if (error) { - return error; +int16_t scd4x_get_data_ready_status(bool* arg_0) { + uint16_t data_ready_status = 0; + int16_t local_error = 0; + local_error = scd4x_get_data_ready_status_raw(&data_ready_status); + if (local_error != NO_ERROR) { + return local_error; } - *t_offset = sensirion_common_bytes_to_uint16_t(&buffer[0]); - return NO_ERROR; + *arg_0 = (data_ready_status & 2047) != 0; + ; + return local_error; } -int16_t scd4x_get_temperature_offset(int32_t* t_offset_m_deg_c) { - int16_t error; - uint16_t t_offset; - - error = scd4x_get_temperature_offset_ticks(&t_offset); - if (error) { - return error; +int16_t scd4x_get_sensor_variant(scd4x_sensor_variant* a_sensor_variant) { + uint16_t raw_sensor_variant = 0; + int16_t local_error = 0; + local_error = scd4x_get_sensor_variant_raw(&raw_sensor_variant); + if (local_error != NO_ERROR) { + return local_error; } - *t_offset_m_deg_c = ((21875 * (int32_t)t_offset) >> 13); - return NO_ERROR; + uint16_t variant = (uint16_t)(raw_sensor_variant & 4); + if (variant == 0) { + *a_sensor_variant = SCD4X_SENSOR_VARIANT_SCD40; + ; + return local_error; + } else if (variant == 1) { + *a_sensor_variant = SCD4X_SENSOR_VARIANT_SCD41; + ; + return local_error; + } + *a_sensor_variant = SCD4X_SENSOR_VARIANT_UNKNOWN; + ; + return local_error; } -int16_t scd4x_set_temperature_offset_ticks(uint16_t t_offset) { - int16_t error; - uint8_t buffer[5]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x241D); - - offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, t_offset); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_start_periodic_measurement() { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x21b1); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(1000); - return NO_ERROR; + return local_error; } -int16_t scd4x_set_temperature_offset(int32_t t_offset_m_deg_c) { - uint16_t t_offset = (uint16_t)((t_offset_m_deg_c * 12271) >> 15); - return scd4x_set_temperature_offset_ticks(t_offset); +int16_t scd4x_read_measurement_raw(uint16_t* co2_concentration, + uint16_t* temperature, + uint16_t* relative_humidity) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0xec05); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 6); + if (local_error != NO_ERROR) { + return local_error; + } + *co2_concentration = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + *temperature = sensirion_common_bytes_to_uint16_t(&buffer_ptr[2]); + *relative_humidity = sensirion_common_bytes_to_uint16_t(&buffer_ptr[4]); + return local_error; } -int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude) { - int16_t error; - uint8_t buffer[3]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2322); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_stop_periodic_measurement() { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x3f86); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } + sensirion_i2c_hal_sleep_usec(500 * 1000); + return local_error; +} - sensirion_i2c_hal_sleep_usec(1000); +int16_t scd4x_set_temperature_offset_raw(uint16_t offset_temperature) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x241d); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, offset_temperature); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; +} - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2); - if (error) { - return error; +int16_t scd4x_get_temperature_offset_raw(uint16_t* offset_temperature) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2318); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - *sensor_altitude = sensirion_common_bytes_to_uint16_t(&buffer[0]); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; + } + *offset_temperature = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } int16_t scd4x_set_sensor_altitude(uint16_t sensor_altitude) { - int16_t error; - uint8_t buffer[5]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2427); - - offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, - sensor_altitude); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2427); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, sensor_altitude); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(1000); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; } -int16_t scd4x_set_ambient_pressure(uint16_t ambient_pressure) { - int16_t error; - uint8_t buffer[5]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xE000); +int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2322); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; + } + *sensor_altitude = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; +} - offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, - ambient_pressure); +int16_t scd4x_set_ambient_pressure_raw(uint16_t ambient_pressure) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0xe000); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, ambient_pressure); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; +} - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_get_ambient_pressure_raw(uint16_t* ambient_pressure) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0xe000); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(1000); - return NO_ERROR; + *ambient_pressure = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } int16_t scd4x_perform_forced_recalibration(uint16_t target_co2_concentration, uint16_t* frc_correction) { - int16_t error; - uint8_t buffer[5]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x362F); - - offset = sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, - target_co2_concentration); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x362f); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, target_co2_concentration); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - - sensirion_i2c_hal_sleep_usec(400000); - - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2); - if (error) { - return error; + sensirion_i2c_hal_sleep_usec(400 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - *frc_correction = sensirion_common_bytes_to_uint16_t(&buffer[0]); - return NO_ERROR; + *frc_correction = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } -int16_t scd4x_get_automatic_self_calibration(uint16_t* asc_enabled) { - int16_t error; - uint8_t buffer[3]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2313); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_set_automatic_self_calibration_enabled(uint16_t asc_enabled) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2416); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, asc_enabled); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; +} - sensirion_i2c_hal_sleep_usec(1000); - - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2); - if (error) { - return error; +int16_t scd4x_get_automatic_self_calibration_enabled(uint16_t* asc_enabled) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2313); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - *asc_enabled = sensirion_common_bytes_to_uint16_t(&buffer[0]); - return NO_ERROR; + *asc_enabled = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } -int16_t scd4x_set_automatic_self_calibration(uint16_t asc_enabled) { - int16_t error; - uint8_t buffer[5]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2416); - - offset = - sensirion_i2c_add_uint16_t_to_buffer(&buffer[0], offset, asc_enabled); +int16_t scd4x_set_automatic_self_calibration_target(uint16_t asc_target) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x243a); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, asc_target); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; +} - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_get_automatic_self_calibration_target(uint16_t* asc_target) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x233f); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(1000); - return NO_ERROR; + *asc_target = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } int16_t scd4x_start_low_power_periodic_measurement() { - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x21AC); - - return sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x21ac); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + return local_error; } -int16_t scd4x_get_data_ready_flag(bool* data_ready_flag) { - int16_t error; - uint8_t buffer[3]; - uint16_t offset = 0; - uint16_t local_data_ready = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0xE4B8); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_get_data_ready_status_raw(uint16_t* data_ready_status) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0xe4b8); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - - sensirion_i2c_hal_sleep_usec(1000); - - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2); - if (error) { - return error; + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - local_data_ready = sensirion_common_bytes_to_uint16_t(&buffer[0]); - *data_ready_flag = (local_data_ready & 0x07FF) != 0; - return NO_ERROR; + *data_ready_status = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } int16_t scd4x_persist_settings() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3615); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x3615); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(800000); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(800 * 1000); + return local_error; } -int16_t scd4x_get_serial_number(uint16_t* serial_0, uint16_t* serial_1, - uint16_t* serial_2) { - int16_t error; - uint8_t buffer[9]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3682); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_get_serial_number(uint16_t* serial_number, + uint16_t serial_number_size) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x3682); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - - sensirion_i2c_hal_sleep_usec(1000); - - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 6); - if (error) { - return error; + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 6); + if (local_error != NO_ERROR) { + return local_error; } - *serial_0 = sensirion_common_bytes_to_uint16_t(&buffer[0]); - *serial_1 = sensirion_common_bytes_to_uint16_t(&buffer[2]); - *serial_2 = sensirion_common_bytes_to_uint16_t(&buffer[4]); - return NO_ERROR; + sensirion_common_copy_bytes(&buffer_ptr[0], (uint8_t*)serial_number, + (serial_number_size * 2)); + return local_error; } int16_t scd4x_perform_self_test(uint16_t* sensor_status) { - int16_t error; - uint8_t buffer[3]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3639); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x3639); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - - sensirion_i2c_hal_sleep_usec(10000000); - - error = sensirion_i2c_read_data_inplace(SCD4X_I2C_ADDRESS, &buffer[0], 2); - if (error) { - return error; + sensirion_i2c_hal_sleep_usec(10000 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - *sensor_status = sensirion_common_bytes_to_uint16_t(&buffer[0]); - return NO_ERROR; + *sensor_status = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } int16_t scd4x_perform_factory_reset() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3632); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x3632); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(800000); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(1200 * 1000); + return local_error; } int16_t scd4x_reinit() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x3646); + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x3646); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(30 * 1000); + return local_error; +} - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; +int16_t scd4x_get_sensor_variant_raw(uint16_t* sensor_variant) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x202f); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(20000); - return NO_ERROR; + *sensor_variant = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } int16_t scd4x_measure_single_shot() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x219D); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x219d); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(5000000); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(5000 * 1000); + return local_error; } int16_t scd4x_measure_single_shot_rht_only() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x2196); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2196); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(50000); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(50 * 1000); + return local_error; } int16_t scd4x_power_down() { - int16_t error; - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x36E0); - - error = sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - if (error) { - return error; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x36e0); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; } - sensirion_i2c_hal_sleep_usec(1000); - return NO_ERROR; + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; } int16_t scd4x_wake_up() { - uint8_t buffer[2]; - uint16_t offset = 0; - offset = sensirion_i2c_add_command_to_buffer(&buffer[0], offset, 0x36F6); - - // Sensor does not acknowledge the wake-up call, error is ignored - (void)sensirion_i2c_write_data(SCD4X_I2C_ADDRESS, &buffer[0], offset); - sensirion_i2c_hal_sleep_usec(20000); - return NO_ERROR; + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x36f6); + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + sensirion_i2c_hal_sleep_usec(30 * 1000); + return local_error; +} + +int16_t scd4x_set_automatic_self_calibration_initial_period( + uint16_t asc_initial_period) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2445); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, asc_initial_period); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; +} + +int16_t scd4x_get_automatic_self_calibration_initial_period( + uint16_t* asc_initial_period) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x2340); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; + } + *asc_initial_period = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; +} + +int16_t scd4x_set_automatic_self_calibration_standard_period( + uint16_t asc_standard_period) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x244e); + local_offset = sensirion_i2c_add_uint16_t_to_buffer( + buffer_ptr, local_offset, asc_standard_period); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + return local_error; +} + +int16_t scd4x_get_automatic_self_calibration_standard_period( + uint16_t* asc_standard_period) { + int16_t local_error = NO_ERROR; + uint8_t* buffer_ptr = communication_buffer; + uint16_t local_offset = 0; + local_offset = + sensirion_i2c_add_command16_to_buffer(buffer_ptr, local_offset, 0x234b); + local_error = + sensirion_i2c_write_data(_i2c_address, buffer_ptr, local_offset); + if (local_error != NO_ERROR) { + return local_error; + } + sensirion_i2c_hal_sleep_usec(1 * 1000); + local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2); + if (local_error != NO_ERROR) { + return local_error; + } + *asc_standard_period = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]); + return local_error; } diff --git a/scd4x_i2c.h b/scd4x_i2c.h index b5ee44d..9375b14 100644 --- a/scd4x_i2c.h +++ b/scd4x_i2c.h @@ -1,12 +1,12 @@ /* - * THIS FILE IS AUTOMATICALLY GENERATED AND MUST NOT BE EDITED MANUALLY! + * THIS FILE IS AUTOMATICALLY GENERATED * - * I2C-Generator: 0.2.0 - * Yaml Version: 0.1.0 - * Template Version: 0.2.1 + * Generator: sensirion-driver-generator 1.1.2 + * Product: scd4x + * Model-Version: 2.0 */ /* - * Copyright (c) 2021, Sensirion AG + * Copyright (c) 2025, Sensirion AG * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,335 +44,714 @@ extern "C" { #endif #include "sensirion_config.h" +#define SCD40_I2C_ADDR_62 0x62 +#define SCD41_I2C_ADDR_62 0x62 + +typedef enum { + SCD4X_START_PERIODIC_MEASUREMENT_CMD_ID = 0x21b1, + SCD4X_READ_MEASUREMENT_RAW_CMD_ID = 0xec05, + SCD4X_STOP_PERIODIC_MEASUREMENT_CMD_ID = 0x3f86, + SCD4X_SET_TEMPERATURE_OFFSET_RAW_CMD_ID = 0x241d, + SCD4X_GET_TEMPERATURE_OFFSET_RAW_CMD_ID = 0x2318, + SCD4X_SET_SENSOR_ALTITUDE_CMD_ID = 0x2427, + SCD4X_GET_SENSOR_ALTITUDE_CMD_ID = 0x2322, + SCD4X_SET_AMBIENT_PRESSURE_RAW_CMD_ID = 0xe000, + SCD4X_GET_AMBIENT_PRESSURE_RAW_CMD_ID = 0xe000, + SCD4X_PERFORM_FORCED_RECALIBRATION_CMD_ID = 0x362f, + SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED_CMD_ID = 0x2416, + SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED_CMD_ID = 0x2313, + SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_TARGET_CMD_ID = 0x243a, + SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_TARGET_CMD_ID = 0x233f, + SCD4X_START_LOW_POWER_PERIODIC_MEASUREMENT_CMD_ID = 0x21ac, + SCD4X_GET_DATA_READY_STATUS_RAW_CMD_ID = 0xe4b8, + SCD4X_PERSIST_SETTINGS_CMD_ID = 0x3615, + SCD4X_GET_SERIAL_NUMBER_CMD_ID = 0x3682, + SCD4X_PERFORM_SELF_TEST_CMD_ID = 0x3639, + SCD4X_PERFORM_FACTORY_RESET_CMD_ID = 0x3632, + SCD4X_REINIT_CMD_ID = 0x3646, + SCD4X_GET_SENSOR_VARIANT_RAW_CMD_ID = 0x202f, + SCD4X_MEASURE_SINGLE_SHOT_CMD_ID = 0x219d, + SCD4X_MEASURE_SINGLE_SHOT_RHT_ONLY_CMD_ID = 0x2196, + SCD4X_POWER_DOWN_CMD_ID = 0x36e0, + SCD4X_WAKE_UP_CMD_ID = 0x36f6, + SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_INITIAL_PERIOD_CMD_ID = 0x2445, + SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_INITIAL_PERIOD_CMD_ID = 0x2340, + SCD4X_SET_AUTOMATIC_SELF_CALIBRATION_STANDARD_PERIOD_CMD_ID = 0x244e, + SCD4X_GET_AUTOMATIC_SELF_CALIBRATION_STANDARD_PERIOD_CMD_ID = 0x234b, +} SCD4X_CMD_ID; + +typedef enum { + SCD4X_SENSOR_VARIANT_UNKNOWN = 0, + SCD4X_SENSOR_VARIANT_SCD40 = 1, + SCD4X_SENSOR_VARIANT_SCD41 = 2, +} scd4x_sensor_variant; /** - * scd4x_start_periodic_measurement() - start periodic measurement, signal - * update interval is 5 seconds. + * @brief Initialize i2c address of driver * - * @note This command is only available in idle mode. + * @param[in] i2c_address Used i2c address * - * @return 0 on success, an error code otherwise */ -int16_t scd4x_start_periodic_measurement(void); +void scd4x_init(uint8_t i2c_address); /** - * scd4x_read_measurement_ticks() - read sensor output. The measurement data can - * only be read out once per signal update interval as the buffer is emptied - * upon read-out. If no data is available in the buffer, the sensor returns a - * NACK. To avoid a NACK response the get_data_ready_flag can be issued to - * check data status. The I2C master can abort the read transfer with a NACK - * followed by a STOP condition after any data byte if the user is not - * interested in subsequent data. + * @brief scd4x_signal_co2_concentration * - * @note This command is only available in measurement mode. The firmware - * updates the measurement values depending on the measurement mode. + * @param[in] raw_co2_concentration * - * @param co2 CO₂ concentration in ppm - * - * @param temperature Convert value to °C by: -45 °C + 175 °C * value/2^16 + * @return CO₂ concentration in ppm + */ +uint16_t scd4x_signal_co2_concentration(uint16_t raw_co2_concentration); + +/** + * @brief scd4x_signal_ambient_pressure * - * @param humidity Convert value to %RH by: 100%RH * value/2^16 + * @param[in] raw_ambient_pressure * - * @return 0 on success, an error code otherwise + * @return Pressure in Pa */ -int16_t scd4x_read_measurement_ticks(uint16_t* co2, uint16_t* temperature, - uint16_t* humidity); +uint32_t scd4x_signal_ambient_pressure(uint16_t raw_ambient_pressure); /** - * scd4x_read_measurement() - read sensor output and convert. - * See @ref scd4x_read_measurement_ticks() for more details. + * @brief Set the ambient pressure around the sensor. * - * @note This command is only available in measurement mode. The firmware - * updates the measurement values depending on the measurement mode. + * The set_ambient_pressure command can be sent during periodic measurements to + * enable continuous pressure compensation. Note that setting an ambient + * pressure overrides any pressure compensation based on a previously set sensor + * altitude. Use of this command is highly recommended for applications + * experiencing significant ambient pressure changes to ensure sensor accuracy. + * Valid input values are between 70000 - 120000 Pa. The default value is 101300 + * Pa. * - * @param co2 CO₂ concentration in ppm + * @param[in] ambient_pressure Ambient pressure around the sensor in Pa * - * @param temperature_m_deg_c Temperature in milli degrees celsius (°C * 1000) + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_set_ambient_pressure(uint32_t ambient_pressure); + +/** + * @brief Get the ambient pressure around the sensor. * - * @param humidity_m_percent_rh Relative humidity in milli percent RH - * (%RH * 1000) + * @param[out] a_ambient_pressure Pressure in Pa * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_read_measurement(uint16_t* co2, int32_t* temperature_m_deg_c, - int32_t* humidity_m_percent_rh); +int16_t scd4x_get_ambient_pressure(uint32_t* a_ambient_pressure); /** - * scd4x_stop_periodic_measurement() - Stop periodic measurement and return to - * idle mode for sensor configuration or to safe energy. + * @brief Read if data is ready. + * + * Polls the sensor for whether data from a periodic or single shot measurement + * is ready to be read out. * - * @note This command is only available in measurement mode. + * @param[out] arg_0 * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_stop_periodic_measurement(void); +int16_t scd4x_get_data_ready_status(bool* arg_0); /** - * scd4x_get_temperature_offset_ticks() - The temperature offset represents the - * difference between the measured temperature by the SCD4x and the actual - * ambient temperature. Per default, the temperature offset is set to 4°C. + * @brief Reads out the SCD4x sensor variant. * - * @note Only available in idle mode. + * @param[out] a_sensor_variant * - * @param t_offset Temperature offset. Convert value to °C by: 175 * value / - * 2^16 + * @note This command is only available in idle mode. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_get_temperature_offset_ticks(uint16_t* t_offset); +int16_t scd4x_get_sensor_variant(scd4x_sensor_variant* a_sensor_variant); /** - * scd4x_get_temperature_offset() - The temperature offset represents the - * difference between the measured temperature by the SCD4x and the actual - * ambient temperature. Per default, the temperature offset is set to 4°C. + * @brief Start periodic measurement mode. * - * @note Only available in idle mode. + * Starts the periodic measurement mode. The signal update interval is 5 + * seconds. * - * @param t_offset_m_deg_c Temperature offset in milli degrees Celsius. + * @note This command is only available in idle mode. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_get_temperature_offset(int32_t* t_offset_m_deg_c); +int16_t scd4x_start_periodic_measurement(); /** - * scd4x_set_temperature_offset_ticks() - Setting the temperature offset of the - * SCD4x inside the customer device correctly allows the user to leverage the RH - * and T output signal. Note that the temperature offset can depend on various - * factors such as the SCD4x measurement mode, self-heating of close components, - * the ambient temperature and air flow. Thus, the SCD4x temperature offset - * should be determined inside the customer device under its typical operation - * and in thermal equilibrium. - * - * @note Only available in idle mode. + * @brief Read CO₂, temperature, and humidity measurements raw values. + * + * Reads the sensor output. The measurement data can only be read out once per + * signal update interval as the buffer is emptied upon read-out. If no data is + * available in the buffer, the sensor returns a NACK. To avoid a NACK response, + * the get_data_ready_status can be issued to check data status. The I2C master + * can abort the read transfer with a NACK followed by a STOP condition after + * any data byte if the user is not interested in subsequent data. + * + * @param[out] co2_concentration CO₂ concentration in ppm + * @param[out] temperature Convert to degrees celsius by (175 * value / 65535) - + * 45 + * @param[out] relative_humidity Convert to relative humidity in % by (100 * + * value / 65535) + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_read_measurement_raw(uint16_t* co2_concentration, + uint16_t* temperature, + uint16_t* relative_humidity); + +/** + * @brief Stop periodic measurement to change the sensor configuration or to + * save power. * - * @param t_offset Temperature offset. Convert °C to value by: T * 2^16 / 175. + * Command returns a sensor running in periodic measurement mode or low power + * periodic measurement mode back to the idle state, e.g. to then allow changing + * the sensor configuration or to save power. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_set_temperature_offset_ticks(uint16_t t_offset); +int16_t scd4x_stop_periodic_measurement(); /** - * scd4x_set_temperature_offset() - Setting the temperature offset of the SCD4x - * inside the customer device correctly allows the user to leverage the RH and T - * output signal. Note that the temperature offset can depend on various factors - * such as the SCD4x measurement mode, self-heating of close components, the - * ambient temperature and air flow. Thus, the SCD4x temperature offset should - * be determined inside the customer device under its typical operation and in - * thermal equilibrium. + * @brief Set the temperature compensation offset (raw value). + * + * Setting the temperature offset of the SCD4x inside the customer device allows + * the user to optimize the RH and T output signal. The temperature offset can + * depend on several factors such as the SCD4x measurement mode, self-heating of + * close components, the ambient temperature and air flow. Thus, the SCD4x + * temperature offset should be determined after integration into the final + * device and under its typical operating conditions (including the operation + * mode to be used in the application) in thermal equilibrium. By default, the + * temperature offset is set to 4 °C. To save the setting to the EEPROM, the + * persist_settings command may be issued. Equation (1) details how the + * characteristic temperature offset can be calculated using the current + * temperature output of the sensor (TSCD4x), a reference temperature value + * (TReference), and the previous temperature offset (Toffset_pervious) obtained + * using the get_temperature_offset_raw command: + * + * Toffset_actual = TSCD4x - TReference + Toffset_pervious. + * + * Recommended temperature offset values are between 0 °C and 20 °C. The + * temperature offset does not impact the accuracy of the CO2 output. + * + * @param[in] offset_temperature Temperature offset. Convert Toffset in °C to + * value by: (Toffset * 65535 / 175) * - * @note Only available in idle mode. + * @note This command is only available in idle mode. * - * @param t_offset_m_deg_c Temperature offset in milli degrees Celsius. + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- + * + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_temperature_offset_raw(1498); + * if (local_error != NO_ERROR) { + * return local_error; + * } + * + * @endcode * - * @return 0 on success, an error code otherwise */ -int16_t scd4x_set_temperature_offset(int32_t t_offset_m_deg_c); +int16_t scd4x_set_temperature_offset_raw(uint16_t offset_temperature); /** - * scd4x_get_sensor_altitude() - Get configured sensor altitude in meters above - * sea level. Per default, the sensor altitude is set to 0 meter above - * sea-level. + * @brief Get the raw temperature compensation offset used by the sensor. * - * @note Only available in idle mode. + * @param[out] offset_temperature Convert to °C by (175 * value / 65535) * - * @param sensor_altitude Sensor altitude in meters. + * @note This command is only available in idle mode. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude); +int16_t scd4x_get_temperature_offset_raw(uint16_t* offset_temperature); /** - * scd4x_set_sensor_altitude() - Set sensor altitude in meters above sea level. - * Note that setting a sensor altitude to the sensor overrides any pressure - * compensation based on a previously set ambient pressure. + * @brief Set the altitude of the sensor (in meters above sea level). + * + * Typically, the sensor altitude is set once after device installation. To save + * the setting to the EEPROM, the persist_settings command must be issued. The + * default sensor altitude value is set to 0 meters above sea level. Note that + * setting a sensor altitude to the sensor overrides any pressure compensation + * based on a previously set ambient pressure. * - * @note Only available in idle mode. + * @param[in] sensor_altitude Sensor altitude in meters above sea level. Valid + * input values are between 0 - 3000 m. + * + * @note This command is only available in idle mode. * - * @param sensor_altitude Sensor altitude in meters. + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- + * + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_sensor_altitude(0); + * if (local_error != NO_ERROR) { + * return local_error; + * } + * + * @endcode * - * @return 0 on success, an error code otherwise */ int16_t scd4x_set_sensor_altitude(uint16_t sensor_altitude); /** - * scd4x_set_ambient_pressure() - The set_ambient_pressure command can be sent - * during periodic measurements to enable continuous pressure compensation. Note - * that setting an ambient pressure to the sensor overrides any pressure - * compensation based on a previously set sensor altitude. + * @brief Get the sensor altitude used by the sensor. + * + * @param[out] sensor_altitude Sensor altitude used by the sensor in meters + * above sea level. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_get_sensor_altitude(uint16_t* sensor_altitude); + +/** + * @brief Set the raw ambient pressure value. + * + * The set_ambient_pressure command can be sent during periodic measurements to + * enable continuous pressure compensation. Note that setting an ambient + * pressure overrides any pressure compensation based on a previously set sensor + * altitude. Use of this command is highly recommended for applications + * experiencing significant ambient pressure changes to ensure sensor accuracy. + * Valid input values are between 70000 - 120000 Pa. The default value is 101300 + * Pa. + * + * @param[in] ambient_pressure Convert ambient_pressure in hPa to Pa by + * ambient_pressure / 100. * * @note Available during measurements. * - * @param ambient_pressure Ambient pressure in hPa. Convert value to Pa by: - * value * 100. + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- + * + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_ambient_pressure_raw(1013); + * if (local_error != NO_ERROR) { + * return local_error; + * } + * + * @endcode * - * @return 0 on success, an error code otherwise */ -int16_t scd4x_set_ambient_pressure(uint16_t ambient_pressure); +int16_t scd4x_set_ambient_pressure_raw(uint16_t ambient_pressure); /** - * scd4x_perform_forced_recalibration() - To successfully conduct an accurate -forced recalibration, the following steps need to be carried out: -1. Operate the SCD4x in a periodic measurement mode for > 3 minutes in an -environment with homogenous and constant CO₂ concentration. -2. Stop periodic measurement. Wait 500 ms. -3. Subsequently issue the perform_forced_recalibration command and optionally -read out the baseline correction. A return value of 0xffff indicates that the -forced recalibration failed. + * @brief Get the ambient pressure around the sensor. + * + * @param[out] ambient_pressure Convert to Pa by value = ambient_pressure * 100. * - * @param target_co2_concentration Target CO₂ concentration in ppm. + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_get_ambient_pressure_raw(uint16_t* ambient_pressure); + +/** + * @brief Perform a forced recalibration (FRC) of the CO₂ concentration. * - * @param frc_correction FRC correction value in CO₂ ppm or 0xFFFF if the -command failed. Convert value to CO₂ ppm with: value - 0x8000 + * To successfully conduct an accurate FRC, the following steps need to be + * carried out: * - * @return 0 on success, an error code otherwise + * 1. Operate the SCD4x in the operation mode later used for normal sensor + * operation (e.g. periodic measurement) for at least 3 minutes in an + * environment with a homogenous and constant CO2 concentration. The sensor must + * be operated at the voltage desired for the application when performing the + * FRC sequence. 2. Issue the stop_periodic_measurement command. 3. Issue the + * perform_forced_recalibration command. + * + * A return value of 0xffff indicates that the FRC has failed because the sensor + * was not operated before sending the command. + * + * @param[in] target_co2_concentration Target CO₂ concentration in ppm CO₂. + * @param[out] frc_correction Convert to FRC correction in ppm CO₂ by + * frc_correction - 0x8000. A return value of 0xFFFF indicates that the FRC has + * failed because the sensor was not operated before sending the command. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. */ int16_t scd4x_perform_forced_recalibration(uint16_t target_co2_concentration, uint16_t* frc_correction); /** - * scd4x_get_automatic_self_calibration() - By default, the ASC is enabled. + * @brief Enable or disable automatic self calibration (ASC). + * + * Sets the current state (enabled / disabled) of the ASC. By default, ASC is + * enabled. To save the setting to the EEPROM, the persist_settings command must + * be issued. The ASC enables excellent long-term stability of SCD4x without the + * need for regular user intervention. The algorithm leverages the sensor's + * measurement history and the assumption of exposure of the sensor to a known + * minimum background CO₂ concentration at least once over a period of + * cumulative operation. By default, the ASC algorithm assumes that the sensor + * is exposed to outdoor fresh air at 400 ppm CO₂ concentration at least once + * per week of accumulated operation using one of the following measurement + * modes for at least 4 hours without interruption at a time: periodic + * measurement mode, low power periodic measurement mode or single shot mode + * with a measurement interval of 5 minutes (SCD41 only). + * + * @param[in] asc_enabled 1 enables ASC, 0 disables ASC. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- * - * @param asc_enabled 1 if ASC is enabled, 0 if ASC is disabled + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_automatic_self_calibration_enabled(1); + * if (local_error != NO_ERROR) { + * return local_error; + * } + * + * @endcode * - * @return 0 on success, an error code otherwise */ -int16_t scd4x_get_automatic_self_calibration(uint16_t* asc_enabled); +int16_t scd4x_set_automatic_self_calibration_enabled(uint16_t asc_enabled); /** - * scd4x_set_automatic_self_calibration() - By default, the ASC is enabled. + * @brief Check if automatic self calibration (ASC) is enabled. + * + * @param[out] asc_enabled 1 if ASC is enabled, 0 if ASC is disabled. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_get_automatic_self_calibration_enabled(uint16_t* asc_enabled); + +/** + * @brief Set the value of ASC baseline target in ppm. + * + * Sets the value of the ASC baseline target, i.e. the CO₂ concentration in ppm + * which the ASC algorithm will assume as lower-bound background to which the + * SCD4x is exposed to regularly within one ASC period of operation. To save the + * setting to the EEPROM, the persist_settings command must be issued + * subsequently. The factory default value is 400 ppm. + * + * @param[in] asc_target ASC baseline value in ppm CO₂ + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- + * + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_automatic_self_calibration_target(400); + * if (local_error != NO_ERROR) { + * return local_error; + * } * - * @param asc_enabled 1 to enable ASC, 0 to disable ASC + * @endcode * - * @return 0 on success, an error code otherwise */ -int16_t scd4x_set_automatic_self_calibration(uint16_t asc_enabled); +int16_t scd4x_set_automatic_self_calibration_target(uint16_t asc_target); /** - * scd4x_start_low_power_periodic_measurement() - Start low power periodic - * measurement, signal update interval is 30 seconds. + * @brief Reads out the ASC baseline target concentration parameter. + * + * @param[out] asc_target ASC baseline target concentration parameter in ppm + * CO₂. * * @note This command is only available in idle mode. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_get_automatic_self_calibration_target(uint16_t* asc_target); + +/** + * @brief Start a low-power periodic measurement (interval 30 s). + * + * To enable use-cases with a constrained power budget, the SCD4x features a low + * power periodic measurement mode with a signal update interval of + * approximately 30 seconds. The low power periodic measurement mode is + * initiated using the start_low_power_periodic_measurement command and read-out + * in a similar manner as the periodic measurement mode using the + * read_measurement command. To periodically check whether a new measurement + * result is available for read out, the get_data_ready_status command can be + * used to synchronize to the sensor's internal measurement interval as an + * alternative to relying on the ACK/NACK status of the + * read_measurement_command. + * + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_start_low_power_periodic_measurement(void); +int16_t scd4x_start_low_power_periodic_measurement(); /** - * scd4x_get_data_ready_flag() - Check whether new measurement data is - * available for read-out. + * @brief Read if data is ready. * - * @param data_ready_flag True if data available, otherwise false. + * Polls the sensor for whether data from a periodic or single shot measurement + * is ready to be read out. * - * @return 0 on success, an error code otherwise + * @param[out] data_ready_status If one or more of the 11 least significant bits + * are 1, then the data is ready. + * + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_get_data_ready_flag(bool* data_ready_flag); +int16_t scd4x_get_data_ready_status_raw(uint16_t* data_ready_status); /** - * scd4x_persist_settings() - Configuration settings such as the temperature - * offset, sensor altitude and the ASC enabled/disabled parameter are by default - * stored in the volatile memory (RAM) only and will be lost after a - * power-cycle. The persist_settings command stores the current configuration in - * the EEPROM of the SCD4x, making them resistant to power-cycling. To avoid - * unnecessary wear of the EEPROM, the persist_settings command should only be - * sent when persistence is required and if actual changes to the configuration - * have been made. Note that field calibration history (i.e. FRC and ASC) is - * stored in the EEPROM automatically. + * @brief Store volatile sensor settings in the EEPROM. + * + * Configuration settings such as the temperature offset, sensor altitude and + * the ASC enabled/disabled parameters are by default stored in the volatile + * memory (RAM) only. The persist_settings command stores the current + * configuration in the EEPROM of the SCD4x, ensuring the current settings + * persist after power-cycling. To avoid unnecessary wear of the EEPROM, the + * persist_settings command should only be sent following configuration changes + * whose persistence is required. The EEPROM is guaranteed to withstand at least + * 2000 write cycles. Note that field calibration history (i.e. FRC and ASC) is + * automatically stored in a separate EEPROM dimensioned for the specified + * sensor lifetime when operated continuously in either periodic measurement + * mode, low power periodic measurement mode or single shot mode with 5 minute + * measurement interval (SCD41 only). * - * @note + * @note This command is only available in idle mode. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_persist_settings(void); +int16_t scd4x_persist_settings(); /** - * scd4x_get_serial_number() - Reading out the serial number can be used to - * identify the chip and to verify the presence of the sensor. The get serial - * number command returns 3 words. Together, the 3 words constitute a unique - * serial number with a length of 48 bits (big endian format). + * @brief Read the sensor's unique serial number. * - * @param serial_0 First word of the 48 bit serial number + * Reading out the serial number can be used to identify the chip and to verify + * the presence of the sensor. The get_serial_number command returns 3 words, + * and every word is followed by an 8-bit CRC checksum. Together, the 3 words + * constitute a unique serial number with a length of 48 bits (in big endian + * format). * - * @param serial_1 Second word of the 48 bit serial number + * @param[out] serial_number 48-bit unique serial number of the sensor. * - * @param serial_2 Third word of the 48 bit serial number + * @note This command is only available in idle mode. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_get_serial_number(uint16_t* serial_0, uint16_t* serial_1, - uint16_t* serial_2); +int16_t scd4x_get_serial_number(uint16_t* serial_number, + uint16_t serial_number_size); /** - * scd4x_perform_self_test() - The perform_self_test feature can be used as an - * end-of-line test to confirm sensor functionality. + * @brief Perform self test to assess sensor functionality and power supply. * - * @param sensor_status 0 means no malfunction detected + * Can be used as an end-of-line test to check the sensor functionality. * - * @return 0 on success, an error code otherwise + * @param[out] sensor_status If sensor status is equal to 0, no malfunction has + * been detected. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. */ int16_t scd4x_perform_self_test(uint16_t* sensor_status); /** - * scd4x_perform_factory_reset() - Initiates the reset of all configurations - * stored in the EEPROM and erases the FRC and ASC algorithm history. + * @brief Perform factory reset to erase the settings stored in the EEPROM. + * + * The perform_factory_reset command resets all configuration settings stored in + * the EEPROM and erases the FRC and ASC algorithm history. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_perform_factory_reset(); + +/** + * @brief Reinitialize the sensor by reloading the settings from the EEPROM. + * + * The reinit command reinitialize the sensor by reloading user settings from + * EEPROM. The sensor must be in the idle state before sending the reinit + * command. If the reinit command does not trigger the desired + * re-initialization, a power-cycle should be applied to the SCD4x. + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_reinit(); + +/** + * @brief Reads out the SCD4x sensor variant. + * + * @param[out] sensor_variant Bits[15…12] = 0000 → SCD40 Bits[15…12] = 0001 → + * SCD41 + * + * @note This command is only available in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_get_sensor_variant_raw(uint16_t* sensor_variant); + +/** + * @brief On-demand measurement of the CO₂ concentration, temperature, and + * humidity. + * + * The sensor output is read out by using the read_measurement command. The + * fastest possible sampling interval for single shot measurements is 5 seconds. + * The ASC is enabled by default in single shot operation and optimized for + * single shot measurements performed every 5 minutes. For more details about + * single shot measurements and optimization of power consumption please refer + * to the datasheet. + * + * @note This command is only available for SCD41. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_perform_factory_reset(void); +int16_t scd4x_measure_single_shot(); /** - * scd4x_reinit() - The reinit command reinitializes the sensor by reloading - * user settings from EEPROM. Before sending the reinit command, the stop - * measurement command must be issued. If reinit command does not trigger the - * desired re-initialization, a power-cycle should be applied to the SCD4x. + * @brief On-demand measurement of the temperature and humidity only. * - * @note Only available in idle mode. + * For more details about single shot measurements and optimization of power + * consumption please refer to the datasheet. * - * @return 0 on success, an error code otherwise + * @note This command is only available for SCD41. + * + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_reinit(void); +int16_t scd4x_measure_single_shot_rht_only(); /** - * scd4x_measure_single_shot() - On-demand measurement of CO₂ concentration, - * relative humidity and temperature. The sensor output is read with the - * read_measurement command. + * @brief Put the sensor into sleep mode from idle mode. + * + * Put the sensor from idle to sleep to reduce power consumption. Can be used to + * power down when operating the sensor in power-cycled single shot mode. * - * @note Only available in idle mode. + * @note This command is only available in idle mode. Only for SCD41. * - * @return 0 on success, an error code otherwise + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_measure_single_shot(void); +int16_t scd4x_power_down(); /** - * scd4x_measure_single_shot_rht_only() - On-demand measurement of relative - * humidity and temperature only. + * @brief Wake up sensor from sleep mode to idle mode. * - * @note Only available in idle mode. + * Wake up the sensor from sleep mode into idle mode. Note that the SCD4x does + * not acknowledge the wake_up command. The sensor's idle state after wake up + * can be verified by reading out the serial number. * - * @return 0 on success, an error code otherwise + * @note This command is only available for SCD41. + * + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_measure_single_shot_rht_only(void); +int16_t scd4x_wake_up(); /** - * scd4x_power_down() - Put the sensor from idle to sleep mode to reduce current - * consumption. + * @brief Sets the initial period for ASC correction + * + * Sets the duration of the initial period for ASC correction (in hours). By + * default, the initial period for ASC correction is 44 hours. Allowed values + * are integer multiples of 4 hours. A value of 0 results in an immediate + * correction. To save the setting to the EEPROM, the persist_settings command + * must be issued. + * + * For single shot operation, this parameter always assumes a measurement + * interval of 5 minutes, counting the number of single shots to calculate + * elapsed time. If single shot measurements are taken more / less frequently + * than once every 5 minutes, this parameter must be scaled accordingly to + * achieve the intended period in hours (e.g. for a 10-minute measurement + * interval, the scaled parameter value is obtained by multiplying the intended + * period in hours by 0.5). + * + * @param[in] asc_initial_period ASC initial period in hours * - * @note Only available in idle mode. + * @note This command is available for SCD41 and only in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- + * + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_automatic_self_calibration_initial_period(44); + * if (local_error != NO_ERROR) { + * return local_error; + * } + * + * @endcode * - * @return 0 on success, an error code otherwise */ -int16_t scd4x_power_down(void); +int16_t scd4x_set_automatic_self_calibration_initial_period( + uint16_t asc_initial_period); /** - * scd4x_wake_up() - Wake up sensor from sleep mode to idle mode. + * @brief Read out the initial period for ASC correction * - * @note Only available in sleep mode. + * @param[out] asc_initial_period ASC initial period in hours * - * @return 0 on success, an error code otherwise + * @note This command is only available for SCD41 and only in idle mode. + * + * @return error_code 0 on success, an error code otherwise. */ -int16_t scd4x_wake_up(void); +int16_t scd4x_get_automatic_self_calibration_initial_period( + uint16_t* asc_initial_period); + +/** + * @brief Sets the standard period for ASC correction. + * + * Sets the standard period for ASC correction (in hours). By default, the + * standard period for ASC correction is 156 hours. Allowed values are integer + * multiples of 4 hours. Note: a value of 0 results in an immediate correction. + * To save the setting to the EEPROM, the persist_settings (see Section 3.10.1) + * command must be issued. + * + * For single shot operation, this parameter always assumes a measurement + * interval of 5 minutes, counting the number of single shots to calculate + * elapsed time. If single shot measurements are taken more / less frequently + * than once every 5 minutes, this parameter must be scaled accordingly to + * achieve the intended period in hours (e.g. for a 10-minute measurement + * interval, the scaled parameter value is obtained by multiplying the intended + * period in hours by 0.5). + * + * @param[in] asc_standard_period ASC standard period in hours + * + * @note This command is only available for SCD41 and only in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + * + * Example: + * -------- + * + * @code{.c} + * + * int16_t local_error = 0; + * local_error = scd4x_set_automatic_self_calibration_standard_period(156); + * if (local_error != NO_ERROR) { + * return local_error; + * } + * + * @endcode + * + */ +int16_t scd4x_set_automatic_self_calibration_standard_period( + uint16_t asc_standard_period); + +/** + * @brief Get the standard period for ASC correction. + * + * @param[out] asc_standard_period ASC standard period in hours + * + * @note This command is only available for SCD41 and only in idle mode. + * + * @return error_code 0 on success, an error code otherwise. + */ +int16_t scd4x_get_automatic_self_calibration_standard_period( + uint16_t* asc_standard_period); #ifdef __cplusplus } #endif - -#endif /* SCD4X_I2C_H */ +#endif // SCD4X_I2C_H diff --git a/scd4x_i2c_example_usage.c b/scd4x_i2c_example_usage.c deleted file mode 100644 index 89f6bb5..0000000 --- a/scd4x_i2c_example_usage.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2021, Sensirion AG - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Sensirion AG nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include // printf - -#include "scd4x_i2c.h" -#include "sensirion_common.h" -#include "sensirion_i2c_hal.h" - -/** - * TO USE CONSOLE OUTPUT (PRINTF) IF NOT PRESENT ON YOUR PLATFORM - */ -//#define printf(...) - -int main(void) { - int16_t error = 0; - - sensirion_i2c_hal_init(); - - // Clean up potential SCD40 states - scd4x_wake_up(); - scd4x_stop_periodic_measurement(); - scd4x_reinit(); - - uint16_t serial_0; - uint16_t serial_1; - uint16_t serial_2; - error = scd4x_get_serial_number(&serial_0, &serial_1, &serial_2); - if (error) { - printf("Error executing scd4x_get_serial_number(): %i\n", error); - } else { - printf("serial: 0x%04x%04x%04x\n", serial_0, serial_1, serial_2); - } - - // Start Measurement - - error = scd4x_start_periodic_measurement(); - if (error) { - printf("Error executing scd4x_start_periodic_measurement(): %i\n", - error); - } - - printf("Waiting for first measurement... (5 sec)\n"); - - for (;;) { - // Read Measurement - sensirion_i2c_hal_sleep_usec(100000); - bool data_ready_flag = false; - error = scd4x_get_data_ready_flag(&data_ready_flag); - if (error) { - printf("Error executing scd4x_get_data_ready_flag(): %i\n", error); - continue; - } - if (!data_ready_flag) { - continue; - } - - uint16_t co2; - int32_t temperature; - int32_t humidity; - error = scd4x_read_measurement(&co2, &temperature, &humidity); - if (error) { - printf("Error executing scd4x_read_measurement(): %i\n", error); - } else if (co2 == 0) { - printf("Invalid sample detected, skipping.\n"); - } else { - printf("CO2: %u\n", co2); - printf("Temperature: %d m°C\n", temperature); - printf("Humidity: %d mRH\n", humidity); - } - } - - return 0; -} diff --git a/sensirion_common.c b/sensirion_common.c index 4ee7a96..3cab3c1 100644 --- a/sensirion_common.c +++ b/sensirion_common.c @@ -60,27 +60,27 @@ float sensirion_common_bytes_to_float(const uint8_t* bytes) { } void sensirion_common_uint32_t_to_bytes(const uint32_t value, uint8_t* bytes) { - bytes[0] = value >> 24; - bytes[1] = value >> 16; - bytes[2] = value >> 8; - bytes[3] = value; + bytes[0] = (uint8_t)(value >> 24); + bytes[1] = (uint8_t)(value >> 16); + bytes[2] = (uint8_t)(value >> 8); + bytes[3] = (uint8_t)(value); } void sensirion_common_uint16_t_to_bytes(const uint16_t value, uint8_t* bytes) { - bytes[0] = value >> 8; - bytes[1] = value; + bytes[0] = (uint8_t)(value >> 8); + bytes[1] = (uint8_t)value; } void sensirion_common_int32_t_to_bytes(const int32_t value, uint8_t* bytes) { - bytes[0] = value >> 24; - bytes[1] = value >> 16; - bytes[2] = value >> 8; - bytes[3] = value; + bytes[0] = (uint8_t)(value >> 24); + bytes[1] = (uint8_t)(value >> 16); + bytes[2] = (uint8_t)(value >> 8); + bytes[3] = (uint8_t)value; } void sensirion_common_int16_t_to_bytes(const int16_t value, uint8_t* bytes) { - bytes[0] = value >> 8; - bytes[1] = value; + bytes[0] = (uint8_t)(value >> 8); + bytes[1] = (uint8_t)value; } void sensirion_common_float_to_bytes(const float value, uint8_t* bytes) { @@ -99,3 +99,22 @@ void sensirion_common_copy_bytes(const uint8_t* source, uint8_t* destination, destination[i] = source[i]; } } + +void sensirion_common_to_integer(const uint8_t* source, uint8_t* destination, + INT_TYPE int_type, uint8_t data_length) { + + if (data_length > int_type) { + data_length = 0; // we do not read at all if data_length is bigger than + // the provided integer! + } + + // pad missing bytes + uint8_t offset = int_type - data_length; + for (uint8_t i = 0; i < offset; i++) { + destination[int_type - i - 1] = 0; + } + + for (uint8_t i = 1; i <= data_length; i++) { + destination[int_type - offset - i] = source[i - 1]; + } +} diff --git a/sensirion_common.h b/sensirion_common.h index cfcd19f..e290933 100644 --- a/sensirion_common.h +++ b/sensirion_common.h @@ -50,6 +50,11 @@ extern "C" { #define SENSIRION_NUM_WORDS(x) (sizeof(x) / SENSIRION_WORD_SIZE) #define SENSIRION_MAX_BUFFER_WORDS 32 +/** + * Enum to describe the type of an integer + */ +typedef enum { BYTE = 1, SHORT = 2, INTEGER = 4, LONG_INTEGER = 8 } INT_TYPE; + /** * sensirion_common_bytes_to_int16_t() - Convert an array of bytes to an int16_t * @@ -174,6 +179,17 @@ void sensirion_common_float_to_bytes(const float value, uint8_t* bytes); void sensirion_common_copy_bytes(const uint8_t* source, uint8_t* destination, uint16_t data_length); +/** + * sensirion_common_to_integer() - Copy bytes from byte array to integer. + * + * @param source Array of bytes to be copied. + * @param int_value Pointer to integer of bytes to be copied to. + * @param int_type Type (size) of the integer to be copied. + * @param data_length Number of bytes to copy. + */ +void sensirion_common_to_integer(const uint8_t* source, uint8_t* destination, + INT_TYPE int_type, uint8_t data_length); + #ifdef __cplusplus } #endif diff --git a/sensirion_i2c.c b/sensirion_i2c.c index 784c402..5f709fc 100644 --- a/sensirion_i2c.c +++ b/sensirion_i2c.c @@ -177,6 +177,19 @@ uint16_t sensirion_i2c_add_command_to_buffer(uint8_t* buffer, uint16_t offset, return offset; } +uint16_t sensirion_i2c_add_command16_to_buffer(uint8_t* buffer, uint16_t offset, + uint16_t command) { + buffer[offset++] = (uint8_t)((command & 0xFF00) >> 8); + buffer[offset++] = (uint8_t)((command & 0x00FF) >> 0); + return offset; +} + +uint16_t sensirion_i2c_add_command8_to_buffer(uint8_t* buffer, uint16_t offset, + uint8_t command) { + buffer[offset++] = command; + return offset; +} + uint16_t sensirion_i2c_add_uint32_t_to_buffer(uint8_t* buffer, uint16_t offset, uint32_t data) { buffer[offset++] = (uint8_t)((data & 0xFF000000) >> 24); @@ -238,7 +251,7 @@ uint16_t sensirion_i2c_add_float_to_buffer(uint8_t* buffer, uint16_t offset, } uint16_t sensirion_i2c_add_bytes_to_buffer(uint8_t* buffer, uint16_t offset, - uint8_t* data, + const uint8_t* data, uint16_t data_length) { uint16_t i; diff --git a/sensirion_i2c.h b/sensirion_i2c.h index db99c20..45ff5c3 100644 --- a/sensirion_i2c.h +++ b/sensirion_i2c.h @@ -181,6 +181,39 @@ int16_t sensirion_i2c_read_cmd(uint8_t address, uint16_t cmd, uint16_t sensirion_i2c_add_command_to_buffer(uint8_t* buffer, uint16_t offset, uint16_t command); +/** + * sensirion_i2c_add_command16_to_buffer() - Add a command to the buffer at + * the specified offset. This function is equivalent to the + * function sensirion_i2c_add_command_to_buffer(). + * + * @param buffer Pointer to buffer in which the write frame will be prepared. + * Caller needs to make sure that there is enough space after + * offset left to write the data into the buffer. + * @param offset Offset of the next free byte in the buffer. + * @param command Command to be written into the buffer. + * + * @return Offset of next free byte in the buffer after writing the data. + */ +uint16_t sensirion_i2c_add_command16_to_buffer(uint8_t* buffer, uint16_t offset, + uint16_t command); + +/** + * sensirion_i2c_add_command8_to_buffer() - Add a command to the buffer at + * offset. Adds one bytes command to the buffer. + * This is used for sensor that only take one command byte such as + * SHT. + * + * @param buffer Pointer to buffer in which the write frame will be prepared. + * Caller needs to make sure that there is enough space after + * offset left to write the data into the buffer. + * @param offset Offset of the next free byte in the buffer. + * @param command Command to be written into the buffer. + * + * @return Offset of next free byte in the buffer after writing the data. + */ +uint16_t sensirion_i2c_add_command8_to_buffer(uint8_t* buffer, uint16_t offset, + uint8_t command); + /** * sensirion_i2c_add_uint32_t_to_buffer() - Add a uint32_t to the buffer at * offset. Adds 6 bytes to the buffer. @@ -274,7 +307,8 @@ uint16_t sensirion_i2c_add_float_to_buffer(uint8_t* buffer, uint16_t offset, * data. */ uint16_t sensirion_i2c_add_bytes_to_buffer(uint8_t* buffer, uint16_t offset, - uint8_t* data, uint16_t data_length); + const uint8_t* data, + uint16_t data_length); /** * sensirion_i2c_write_data() - Writes data to the Sensor. diff --git a/sensirion_i2c_hal.c b/sensirion_i2c_hal.c index d63f815..d368cf2 100644 --- a/sensirion_i2c_hal.c +++ b/sensirion_i2c_hal.c @@ -83,7 +83,7 @@ void sensirion_i2c_hal_free(void) { * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count) { /* TODO:IMPLEMENT */ return NOT_IMPLEMENTED_ERROR; } @@ -100,7 +100,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count) { * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count) { + uint8_t count) { /* TODO:IMPLEMENT */ return NOT_IMPLEMENTED_ERROR; } diff --git a/sensirion_i2c_hal.h b/sensirion_i2c_hal.h index f97444a..d6267b8 100644 --- a/sensirion_i2c_hal.h +++ b/sensirion_i2c_hal.h @@ -71,7 +71,7 @@ void sensirion_i2c_hal_free(void); * @param count number of bytes to read from I2C and store in the buffer * @returns 0 on success, error code otherwise */ -int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count); +int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint8_t count); /** * Execute one write transaction on the I2C bus, sending a given number of @@ -85,7 +85,7 @@ int8_t sensirion_i2c_hal_read(uint8_t address, uint8_t* data, uint16_t count); * @returns 0 on success, error code otherwise */ int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data, - uint16_t count); + uint8_t count); /** * Sleep for a given number of microseconds. The function should delay the diff --git a/tests/Makefile b/tests/Makefile index a0b32ba..3da3839 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,19 +1,33 @@ -driver_dir := .. -mux_dir := ../i2c-mux-testbed -i2c_mux_sources = ${mux_dir}/i2c_mux.h ${mux_dir}/i2c_mux.c -common_sources = ${driver_dir}/sensirion_config.h ${driver_dir}/sensirion_common.h ${driver_dir}/sensirion_common.c -i2c_sources = ${driver_dir}/sensirion_i2c_hal.h ${driver_dir}/sensirion_i2c.h ${driver_dir}/sensirion_i2c.c -sensirion_test_sources = sensirion_test_setup.cpp ${i2c_mux_sources} +# use the second argument of make as argument to control test bed initialization +TEST_ARG := $(wordlist 2, 2, $(MAKECMDGOALS)) +# ...turn it into do-nothing target +$(eval $(TEST_ARG):;@:) + + ifeq ($(TEST_ARG), mux) + driver_dir := .. + mux_dir := ../i2c-mux-testbed + i2c_mux_sources = ${mux_dir}/i2c_mux.h ${mux_dir}/i2c_mux.c + macros := "-D USE_MUX" + else + mux_dir := . + i2c_mux_sources = + macros := + endif + +driver_dir := .. +common_sources = $(driver_dir)/sensirion_config.h $(driver_dir)/sensirion_common.h $(driver_dir)/sensirion_common.c +i2c_sources = $(driver_dir)/sensirion_i2c_hal.h ${driver_dir}/sensirion_i2c.h $(driver_dir)/sensirion_i2c.c +sensirion_test_sources = sensirion_test_setup.cpp $(i2c_mux_sources) -sw_i2c_dir := ${driver_dir}/sample-implementations/GPIO_bit_banging +sw_i2c_dir := $(driver_dir)/sample-implementations/GPIO_bit_banging -hw_i2c_impl_src = ${driver_dir}/sample-implementations/linux_user_space/sensirion_i2c_hal.c -sw_i2c_impl_src = ${sw_i2c_dir}/sample-implementations/linux_user_space/sensirion_i2c_gpio.c ${sw_i2c_dir}/sensirion_i2c_hal.c +hw_i2c_impl_src = $(driver_dir)/sample-implementations/linux_user_space/sensirion_i2c_hal.c +sw_i2c_impl_src = $(sw_i2c_dir)/sample-implementations/linux_user_space/sensirion_i2c_gpio.c $(sw_i2c_dir)/sensirion_i2c_hal.c -scd4x_sources = ${driver_dir}/scd4x_i2c.h ${driver_dir}/scd4x_i2c.c +scd4x_sources = $(driver_dir)/scd4x_i2c.h $(driver_dir)/scd4x_i2c.c -CXXFLAGS ?= $(CFLAGS) -fsanitize=address -I${mux_dir} -I${driver_dir} -I${sw_i2c_dir} +CXXFLAGS ?= $(CFLAGS) -fsanitize=address -I$(mux_dir) -I$(driver_dir) -I$(sw_i2c_dir) ${macros} ifdef CI CXXFLAGS += -Werror endif @@ -22,18 +36,18 @@ LDFLAGS ?= -lasan -lstdc++ -lCppUTest -lCppUTestExt .PHONY: clean test -scd4x_test_binaries := scd4x-test-hw_i2c scd4x-test-sw_i2c +scd4x_test_binaries := scd4x_test_hw_i2c scd4x_test_sw_i2c -all: ${scd4x_test_binaries} +all: $(scd4x_test_binaries) -scd4x-test-hw_i2c: scd4x_i2c_test.cpp ${scd4x_sources} ${sensirion_test_sources} ${i2c_sources} ${hw_i2c_impl_src} ${common_sources} +scd4x_test_hw_i2c: scd4x_i2c_test.cpp $(scd4x_sources) $(sensirion_test_sources) $(i2c_sources) $(hw_i2c_impl_src) $(common_sources) $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -scd4x-test-sw_i2c: scd4x_i2c_test.cpp ${scd4x_sources} ${sensirion_test_sources} ${i2c_sources} ${sw_i2c_impl_src} ${common_sources} +scd4x_test_sw_i2c: scd4x_i2c_test.cpp $(scd4x_sources) $(sensirion_test_sources) $(i2c_sources) $(sw_i2c_impl_src) $(common_sources) $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -test: ${scd4x_test_binaries} - set -ex; for test in ${scd4x_test_binaries}; do echo $${test}; ./$${test}; echo; done; +test: $(scd4x_test_binaries) + set -ex; for test in $(scd4x_test_binaries); do echo $${test}; ./$${test}; echo; done; clean: - $(RM) ${scd4x_test_binaries} + $(RM) $(scd4x_test_binaries) diff --git a/tests/scd4x_i2c_test.cpp b/tests/scd4x_i2c_test.cpp index 0bce7cb..0dff761 100644 --- a/tests/scd4x_i2c_test.cpp +++ b/tests/scd4x_i2c_test.cpp @@ -1,35 +1,11 @@ /* - * Copyright (c) 2021, Sensirion AG - * All rights reserved. + * THIS FILE IS AUTOMATICALLY GENERATED * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of Sensirion AG nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * Generator: sensirion-driver-generator 1.1.2 + * Product: scd4x + * Model-Version: 2.0 */ -#include "i2c_mux.h" #include "scd4x_i2c.h" #include "sensirion_common.h" #include "sensirion_i2c.h" @@ -38,268 +14,268 @@ #include #include -TEST_GROUP (SCD4X_Tests) { - void setup() { - sensirion_i2c_hal_init(); - - // Select MUX 2 channel 0 - int16_t error = sensirion_i2c_mux_set_single_channel(0x72, 0); - CHECK_EQUAL_ZERO_TEXT(error, "sensirion_i2c_mux_set_single_channel"); +#define sensirion_hal_sleep_us sensirion_i2c_hal_sleep_usec - // try to reset sensor - (void)scd4x_wake_up(); - (void)scd4x_stop_periodic_measurement(); - error = scd4x_reinit(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_reinit"); +void print_ushort_array(uint16_t* array, uint16_t len) { + uint16_t i = 0; + printf("0x"); + for (; i < len; i++) { + printf("%04x", array[i]); } +} - void teardown() { - sensirion_i2c_hal_free(); +TEST_GROUP (SCD4X_Tests) { + void setup() { + scd4x_init(0x62); } }; -TEST (SCD4X_Tests, SCD4X_Test_start_periodic_measurement) { - int16_t error; - error = scd4x_start_periodic_measurement(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_start_periodic_measurement"); - - sensirion_i2c_hal_sleep_usec(5000000); - uint16_t co2; - uint16_t temperature; - uint16_t humidity; - error = scd4x_read_measurement_ticks(&co2, &temperature, &humidity); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_read_measurement_ticks"); - - printf("co2: %i\n", co2); - printf("temperature: %i\n", temperature); - printf("humidity: %i\n", humidity); - - error = scd4x_stop_periodic_measurement(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_stop_periodic_measurement"); +TEST (SCD4X_Tests, test_set_temperature_offset_raw1) { + int16_t local_error = 0; + local_error = scd4x_set_temperature_offset_raw(1498); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_temperature_offset_raw"); } -TEST (SCD4X_Tests, SCD4X_Test_read_measurement_ticks_fails_in_idle) { - int16_t error; - uint16_t co2; - uint16_t temperature; - uint16_t humidity; - error = scd4x_read_measurement_ticks(&co2, &temperature, &humidity); - CHECK_TEXT(error != 0, "scd4x_read_measurement_ticks should fail in idle"); +TEST (SCD4X_Tests, test_get_temperature_offset_raw1) { + int16_t local_error = 0; + uint16_t offset_temperature = 0; + local_error = scd4x_get_temperature_offset_raw(&offset_temperature); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_temperature_offset_raw"); + printf("offset_temperature: %u\n", offset_temperature); } -TEST (SCD4X_Tests, SCD4X_Test_stop_periodic_measurement_in_idle) { - int16_t error; - error = scd4x_stop_periodic_measurement(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_stop_periodic_measurement"); +TEST (SCD4X_Tests, test_set_sensor_altitude1) { + int16_t local_error = 0; + local_error = scd4x_set_sensor_altitude(0); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_sensor_altitude"); } -TEST (SCD4X_Tests, SCD4X_Test_get_temperature_offset_ticks_after_reinit) { - int16_t error; - uint16_t t_offset; - error = scd4x_get_temperature_offset_ticks(&t_offset); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_temperature_offset"); - printf("t_offset: %i\n", t_offset); - - CHECK_EQUAL_TEXT(t_offset, 1498, "T offset wrong after reinit") +TEST (SCD4X_Tests, test_get_sensor_altitude1) { + int16_t local_error = 0; + uint16_t sensor_altitude = 0; + local_error = scd4x_get_sensor_altitude(&sensor_altitude); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_sensor_altitude"); + printf("sensor_altitude: %u\n", sensor_altitude); } -TEST (SCD4X_Tests, SCD4X_Test_set_temperature_offset_ticks) { - int16_t error; - error = scd4x_set_temperature_offset_ticks(0); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_set_temperature_offset"); - - uint16_t t_offset; - error = scd4x_get_temperature_offset_ticks(&t_offset); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_temperature_offset"); - printf("t_offset: %i\n", t_offset); - - CHECK_EQUAL_TEXT(t_offset, 0, "T offset should be 0") +TEST (SCD4X_Tests, test_get_serial_number1) { + int16_t local_error = 0; + uint16_t serial_number[3] = {0}; + local_error = scd4x_get_serial_number(serial_number, 3); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_serial_number"); + printf("serial_number: "); + print_ushort_array(serial_number, 3); + printf("\n"); } -TEST (SCD4X_Tests, SCD4X_Test_get_sensor_altitude_default) { - int16_t error; - uint16_t sensor_altitude; - error = scd4x_get_sensor_altitude(&sensor_altitude); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_sensor_altitude"); - printf("sensor_altitude: %i\n", sensor_altitude); - - CHECK_EQUAL_TEXT(sensor_altitude, 0, - "sensor_altitude should be 0 by default"); +TEST (SCD4X_Tests, test_perform_forced_recalibration1) { + int16_t local_error = 0; + uint16_t frc_correction = 0; + local_error = scd4x_perform_forced_recalibration(400, &frc_correction); + CHECK_EQUAL_ZERO_TEXT(local_error, "perform_forced_recalibration"); + printf("frc_correction: %u\n", frc_correction); } -TEST (SCD4X_Tests, SCD4X_Test_set_sensor_altitude) { - int16_t error; - error = scd4x_set_sensor_altitude(42); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_set_sensor_altitude"); - - uint16_t sensor_altitude; - error = scd4x_get_sensor_altitude(&sensor_altitude); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_sensor_altitude"); - printf("sensor_altitude: %i\n", sensor_altitude); - - CHECK_EQUAL_TEXT(sensor_altitude, 42, - "sensor_altitude should be the set value"); +TEST (SCD4X_Tests, test_set_automatic_self_calibration_enabled1) { + int16_t local_error = 0; + local_error = scd4x_set_automatic_self_calibration_enabled(1); + CHECK_EQUAL_ZERO_TEXT(local_error, + "set_automatic_self_calibration_enabled"); } -TEST (SCD4X_Tests, SCD4X_Test_set_ambient_pressure) { - int16_t error; - uint16_t ambient_pressure = 0; - error = scd4x_set_ambient_pressure(ambient_pressure); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_set_ambient_pressure"); +TEST (SCD4X_Tests, test_get_automatic_self_calibration_enabled1) { + int16_t local_error = 0; + uint16_t asc_enabled = 0; + local_error = scd4x_get_automatic_self_calibration_enabled(&asc_enabled); + CHECK_EQUAL_ZERO_TEXT(local_error, + "get_automatic_self_calibration_enabled"); + printf("asc_enabled: %u\n", asc_enabled); } -TEST (SCD4X_Tests, SCD4X_Test_perform_forced_recalibration) { - int16_t error; - uint16_t target_co2_concentration = 400; - uint16_t frc_correction; - error = scd4x_perform_forced_recalibration(target_co2_concentration, - &frc_correction); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_perform_forced_recalibration"); - printf("frc_correction: %i\n", frc_correction); +TEST (SCD4X_Tests, test_set_automatic_self_calibration_target1) { + int16_t local_error = 0; + local_error = scd4x_set_automatic_self_calibration_target(400); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_automatic_self_calibration_target"); } -TEST (SCD4X_Tests, SCD4X_Test_get_automatic_self_calibration_default) { - int16_t error; - uint16_t asc_enabled; - error = scd4x_get_automatic_self_calibration(&asc_enabled); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_automatic_self_calibration"); - printf("asc_enabled: %i\n", asc_enabled); - CHECK_EQUAL_TEXT(asc_enabled, 1, "ASC should be enabled by default"); +TEST (SCD4X_Tests, test_get_automatic_self_calibration_target1) { + int16_t local_error = 0; + uint16_t asc_target = 0; + local_error = scd4x_get_automatic_self_calibration_target(&asc_target); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_automatic_self_calibration_target"); + printf("asc_target: %u\n", asc_target); } -TEST (SCD4X_Tests, SCD4X_Test_set_automatic_self_calibration) { - int16_t error; - uint16_t asc_enabled = 0; - error = scd4x_set_automatic_self_calibration(asc_enabled); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_set_automatic_self_calibration"); - - error = scd4x_get_automatic_self_calibration(&asc_enabled); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_automatic_self_calibration"); - printf("asc_enabled: %i\n", asc_enabled); - CHECK_EQUAL_TEXT(asc_enabled, 0, "ASC should be set correctly"); +TEST (SCD4X_Tests, test_persist_settings1) { + int16_t local_error = 0; + local_error = scd4x_persist_settings(); + CHECK_EQUAL_ZERO_TEXT(local_error, "persist_settings"); } -TEST (SCD4X_Tests, SCD4X_Test_start_low_power_periodic_measurement) { - int16_t error; - error = scd4x_start_low_power_periodic_measurement(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_start_low_power_periodic_measurement"); - - sensirion_i2c_hal_sleep_usec(3000000); - - uint16_t co2; - uint16_t temperature; - uint16_t humidity; - error = scd4x_read_measurement_ticks(&co2, &temperature, &humidity); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_read_measurement_ticks"); - - printf("co2: %i\n", co2); - printf("temperature: %i\n", temperature); - printf("humidity: %i\n", humidity); - - error = scd4x_stop_periodic_measurement(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_stop_periodic_measurement"); +TEST (SCD4X_Tests, test_perform_self_test1) { + int16_t local_error = 0; + uint16_t sensor_status = 0; + local_error = scd4x_perform_self_test(&sensor_status); + CHECK_EQUAL_ZERO_TEXT(local_error, "perform_self_test"); + printf("sensor_status: %u\n", sensor_status); } -TEST (SCD4X_Tests, SCD4X_Test_get_data_ready_flag) { - int16_t error; - bool data_ready; - error = scd4x_get_data_ready_flag(&data_ready); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_data_ready_flag"); - printf("data_ready: %i\n", data_ready); +TEST (SCD4X_Tests, test_perform_factory_reset1) { + int16_t local_error = 0; + local_error = scd4x_perform_factory_reset(); + CHECK_EQUAL_ZERO_TEXT(local_error, "perform_factory_reset"); } -/* - * Persist settings may break the test -TEST (SCD4X_Tests, SCD4X_Test_persist_settings) { - int16_t error; - error = scd4x_persist_settings(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_persist_settings"); +TEST (SCD4X_Tests, test_reinit1) { + int16_t local_error = 0; + local_error = scd4x_reinit(); + CHECK_EQUAL_ZERO_TEXT(local_error, "reinit"); } -*/ -TEST (SCD4X_Tests, SCD4X_Test_get_serial_number) { - int16_t error; - uint16_t serial_0; - uint16_t serial_1; - uint16_t serial_2; - error = scd4x_get_serial_number(&serial_0, &serial_1, &serial_2); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_get_serial_number"); - printf("serial_0: %i\n", serial_0); - printf("serial_1: %i\n", serial_1); - printf("serial_2: %i\n", serial_2); +TEST (SCD4X_Tests, test_get_sensor_variant_raw1) { + int16_t local_error = 0; + uint16_t sensor_variant = 0; + local_error = scd4x_get_sensor_variant_raw(&sensor_variant); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_sensor_variant_raw"); + printf("sensor_variant: %u\n", sensor_variant); } -TEST (SCD4X_Tests, SCD4X_Test_perform_self_test) { - int16_t error; - uint16_t sensor_status; - error = scd4x_perform_self_test(&sensor_status); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_perform_self_test"); - printf("sensor_status: %i\n", sensor_status); +TEST (SCD4X_Tests, test_get_sensor_variant1) { + int16_t local_error = 0; + scd4x_sensor_variant a_sensor_variant = SCD4X_SENSOR_VARIANT_UNKNOWN; + local_error = scd4x_get_sensor_variant(&a_sensor_variant); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_sensor_variant"); + printf("a_sensor_variant: %i\n", a_sensor_variant); } -/* - * Continous factory reset may break the device -TEST (SCD4X_Tests, SCD4X_Test_perform_factory_reset) { - int16_t error; - error = scd4x_perform_factory_reset(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_perform_factory_reset"); +TEST (SCD4X_Tests, test_set_automatic_self_calibration_initial_period1) { + int16_t local_error = 0; + local_error = scd4x_set_automatic_self_calibration_initial_period(44); + CHECK_EQUAL_ZERO_TEXT(local_error, + "set_automatic_self_calibration_initial_period"); } -*/ -TEST (SCD4X_Tests, SCD4X_Test_reinit) { - int16_t error; - error = scd4x_reinit(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_reinit"); +TEST (SCD4X_Tests, test_get_automatic_self_calibration_initial_period1) { + int16_t local_error = 0; + uint16_t asc_initial_period = 0; + local_error = scd4x_get_automatic_self_calibration_initial_period( + &asc_initial_period); + CHECK_EQUAL_ZERO_TEXT(local_error, + "get_automatic_self_calibration_initial_period"); + printf("asc_initial_period: %u\n", asc_initial_period); } -TEST (SCD4X_Tests, SCD4X_Test_measure_single_shot) { - int16_t error; - error = scd4x_measure_single_shot(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_measure_single_shot"); - - uint16_t co2; - uint16_t temperature; - uint16_t humidity; - error = scd4x_read_measurement_ticks(&co2, &temperature, &humidity); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_read_measurement_ticks"); - - printf("co2: %i\n", co2); - printf("temperature: %i\n", temperature); - printf("humidity: %i\n", humidity); +TEST (SCD4X_Tests, test_set_automatic_self_calibration_standard_period1) { + int16_t local_error = 0; + local_error = scd4x_set_automatic_self_calibration_standard_period(156); + CHECK_EQUAL_ZERO_TEXT(local_error, + "set_automatic_self_calibration_standard_period"); } -TEST (SCD4X_Tests, SCD4X_Test_measure_single_shot_rht_only) { - int16_t error; - error = scd4x_measure_single_shot_rht_only(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_measure_single_shot_rht_only"); - - uint16_t co2; - uint16_t temperature; - uint16_t humidity; - error = scd4x_read_measurement_ticks(&co2, &temperature, &humidity); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_read_measurement_ticks"); - - printf("co2: %i\n", co2); - printf("temperature: %i\n", temperature); - printf("humidity: %i\n", humidity); - - CHECK_EQUAL_TEXT(co2, 0, "CO2 should be zero in rht only measurement"); +TEST (SCD4X_Tests, test_get_automatic_self_calibration_standard_period1) { + int16_t local_error = 0; + uint16_t asc_standard_period = 0; + local_error = scd4x_get_automatic_self_calibration_standard_period( + &asc_standard_period); + CHECK_EQUAL_ZERO_TEXT(local_error, + "get_automatic_self_calibration_standard_period"); + printf("asc_standard_period: %u\n", asc_standard_period); } -TEST (SCD4X_Tests, SCD4X_Test_power_down) { - int16_t error; - error = scd4x_power_down(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_power_down"); +TEST (SCD4X_Tests, test_measure_single_shot_rht_only1) { + int16_t local_error = 0; + local_error = scd4x_measure_single_shot_rht_only(); + CHECK_EQUAL_ZERO_TEXT(local_error, "measure_single_shot_rht_only"); + local_error = scd4x_read_measurement_raw(&co2_concentration, &temperature, + &relative_humidity); + CHECK_EQUAL_ZERO_TEXT(local_error, "read_measurement_raw"); + printf("co2_concentration: %u ", co2_concentration); + printf("temperature: %u ", temperature); + printf("relative_humidity: %u\n", relative_humidity); +} - sensirion_i2c_hal_sleep_usec(20000); +TEST (SCD4X_Tests, test_measure_single_shot1) { + int16_t local_error = 0; + local_error = scd4x_measure_single_shot(); + CHECK_EQUAL_ZERO_TEXT(local_error, "measure_single_shot"); + local_error = scd4x_read_measurement_raw(&co2_concentration, &temperature, + &relative_humidity); + CHECK_EQUAL_ZERO_TEXT(local_error, "read_measurement_raw"); + printf("co2_concentration: %u ", co2_concentration); + printf("temperature: %u ", temperature); + printf("relative_humidity: %u\n", relative_humidity); +} - error = scd4x_wake_up(); - printf("error: %i\n", error); - CHECK_EQUAL_ZERO_TEXT(error, "wake-up should not forward error"); +TEST (SCD4X_Tests, test_start_periodic_measurement1) { + int16_t local_error = 0; + uint16_t co2_concentration = 0; + uint16_t temperature = 0; + uint16_t relative_humidity = 0; + uint32_t a_ambient_pressure = 0; + uint16_t ambient_pressure = 0; + bool arg_0 = false; + uint16_t data_ready_status = 0; + local_error = scd4x_start_periodic_measurement(); + CHECK_EQUAL_ZERO_TEXT(local_error, "start_periodic_measurement"); + // wait for measurement + sensirion_hal_sleep_us(5000000); + local_error = scd4x_get_data_ready_status(&arg_0); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_data_ready_status"); + printf("arg_0: %d\n", arg_0); + local_error = scd4x_get_data_ready_status_raw(&data_ready_status); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_data_ready_status_raw"); + printf("data_ready_status: %u\n", data_ready_status); + local_error = scd4x_read_measurement_raw(&co2_concentration, &temperature, + &relative_humidity); + CHECK_EQUAL_ZERO_TEXT(local_error, "read_measurement_raw"); + printf("co2_concentration: %u ", co2_concentration); + printf("temperature: %u ", temperature); + printf("relative_humidity: %u\n", relative_humidity); + local_error = scd4x_set_ambient_pressure(101300); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_ambient_pressure"); + local_error = scd4x_get_ambient_pressure(&a_ambient_pressure); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_ambient_pressure"); + printf("a_ambient_pressure: %u\n", a_ambient_pressure); + local_error = scd4x_set_ambient_pressure_raw(1013); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_ambient_pressure_raw"); + local_error = scd4x_get_ambient_pressure_raw(&ambient_pressure); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_ambient_pressure_raw"); + printf("ambient_pressure: %u\n", ambient_pressure); + local_error = scd4x_stop_periodic_measurement(); + CHECK_EQUAL_ZERO_TEXT(local_error, "stop_periodic_measurement"); } -TEST (SCD4X_Tests, SCD4X_Test_wake_up) { - int16_t error; - error = scd4x_wake_up(); - CHECK_EQUAL_ZERO_TEXT(error, "scd4x_wake_up"); +TEST (SCD4X_Tests, test_start_low_power_periodic_measurement1) { + int16_t local_error = 0; + uint16_t co2_concentration = 0; + uint16_t temperature = 0; + uint16_t relative_humidity = 0; + uint32_t a_ambient_pressure = 0; + uint16_t ambient_pressure = 0; + bool arg_0 = false; + uint16_t data_ready_status = 0; + local_error = scd4x_start_low_power_periodic_measurement(); + CHECK_EQUAL_ZERO_TEXT(local_error, "start_low_power_periodic_measurement"); + local_error = scd4x_read_measurement_raw(&co2_concentration, &temperature, + &relative_humidity); + // as we do not wait the read measurement command returns with nack/error + CHECK_EQUAL_TEXT(local_error, 1, "read_measurement_raw"); + local_error = scd4x_set_ambient_pressure(101300); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_ambient_pressure"); + local_error = scd4x_get_ambient_pressure(&a_ambient_pressure); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_ambient_pressure"); + printf("a_ambient_pressure: %u\n", a_ambient_pressure); + local_error = scd4x_set_ambient_pressure_raw(1013); + CHECK_EQUAL_ZERO_TEXT(local_error, "set_ambient_pressure_raw"); + local_error = scd4x_get_ambient_pressure_raw(&ambient_pressure); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_ambient_pressure_raw"); + printf("ambient_pressure: %u\n", ambient_pressure); + local_error = scd4x_get_data_ready_status(&arg_0); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_data_ready_status"); + printf("arg_0: %d\n", arg_0); + local_error = scd4x_get_data_ready_status_raw(&data_ready_status); + CHECK_EQUAL_ZERO_TEXT(local_error, "get_data_ready_status_raw"); + printf("data_ready_status: %u\n", data_ready_status); + local_error = scd4x_stop_periodic_measurement(); + CHECK_EQUAL_ZERO_TEXT(local_error, "stop_periodic_measurement"); } diff --git a/tests/sensirion_test_setup.cpp b/tests/sensirion_test_setup.cpp index 22dbb6e..e47f022 100644 --- a/tests/sensirion_test_setup.cpp +++ b/tests/sensirion_test_setup.cpp @@ -1,5 +1,21 @@ +#include "sensirion_test_setup.h" #include "CppUTest/CommandLineTestRunner.h" +#include "sensirion_i2c_hal.h" + +#ifdef USE_MUX +#include "i2c_mux.h" +#define INIT_TESTBED(x, y) sensirion_i2c_mux_set_single_channel((x), (y)) +#else +#define INIT_TESTBED(x, y) 0 +#endif + +#define MUX_CHANNEL 0x71 int main(int argc, char** argv) { - return CommandLineTestRunner::RunAllTests(argc, argv); + sensirion_i2c_hal_init(); + int16_t error = INIT_TESTBED(MUX_CHANNEL, 1); + CHECK_EQUAL_ZERO_TEXT(error, "test-bed initialization failed"); + int result = CommandLineTestRunner::RunAllTests(argc, argv); + sensirion_i2c_hal_free(); + return result; }