diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..047f2ad
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,14 @@
+---
+Language: Cpp
+BasedOnStyle: LLVM
+IndentWidth: 4
+AlignAfterOpenBracket: Align
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+IndentCaseLabels: true
+SpacesBeforeTrailingComments: 2
+PointerAlignment: Left
+AlignEscapedNewlines: Left
+ForEachMacros: ['TEST_GROUP', 'TEST']
+...
diff --git a/.github/workflows/embedded_quality_check.yml b/.github/workflows/embedded_quality_check.yml
new file mode 100644
index 0000000..95e97ee
--- /dev/null
+++ b/.github/workflows/embedded_quality_check.yml
@@ -0,0 +1,21 @@
+name: Quality check
+
+on:
+ pull_request:
+ branches:
+ - main
+ push:
+ branches:
+ - main
+
+jobs:
+ driver-quality:
+ uses: sensirion/.github/.github/workflows/driver.c.check.yml@main
+
+ todo-check:
+ if: github.event_name == 'push' && github.ref != 'refs/head/main'
+ uses: sensirion/.github/.github/workflows/driver.common.todo_check.yml@main
+
+ code-generation-check:
+ if: github.event_name == 'push' && github.ref != 'refs/head/main'
+ uses: sensirion/.github/.github/workflows/driver.generated.metadata_check.yml@main
diff --git a/.github/workflows/github_release.yml b/.github/workflows/github_release.yml
new file mode 100644
index 0000000..d98ee0e
--- /dev/null
+++ b/.github/workflows/github_release.yml
@@ -0,0 +1,10 @@
+name: Github Release
+
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ github-release:
+ uses: sensirion/.github/.github/workflows/driver.common.github_release.yml@main
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b333179
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/example-usage/sen66_i2c_example_usage
+/tests/sen66_test_hw_i2c
+/tests/sen66_test_sw_i2c
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..d1e7060
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,17 @@
+# 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.1.0] - 2024-10-31
+
+### Added
+
+- Add product picture
+- Add interfaces to start, stop and read measurements.
+- Add interfaces to read product name, serial number and version
+
+[Unreleased]: https://github.com/Sensirion/embedded-i2c-sen66/compare/0.1.0...HEAD
+[0.1.0]: https://github.com/Sensirion/embedded-i2c-sen66/releases/tag/0.1.0
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1daaf92
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2024, 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:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holder 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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..65d8f71
--- /dev/null
+++ b/README.md
@@ -0,0 +1,170 @@
+# Sensirion I2C SEN66 embedded Library
+
+This document explains how to set up a SEN66 sensor to run on an embedded device using the I²C interface.
+
+
+
+Click [here](https://sensirion.com/sen6x-air-quality-sensor-platform) to learn more about the Sensirion SEN66 sensor.
+
+
+unknown
+
+
+The default I²C address of [SEN66](https://www.sensirion.com/products/catalog/SEN6x) is **0x6B**.
+
+
+
+## Setup Guide
+
+### Connecting the Sensor
+
+Your sensor has 4 different signals that need to be connected to your board: VDD, GND, SDA, SCL.
+Use the following pins to connect your SEN66:
+
+
+
+| *Pin* | *Cable Color* | *Name* | *Description* | *Comments* |
+|-------|---------------|:------:|----------------|------------|
+| 1 | red | VDD | Supply Voltage | 3.3V ±10%
+| 2 | black | GND | Ground |
+| 3 | green | SDA | I2C: Serial data input / output | TTL 5V compatible
+| 4 | yellow | SCL | I2C: Serial clock input | TTL 5V compatible
+| 5 | | NC | Do not connect |
+| 6 | | NC | Do not connect |
+
+
+
+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, 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/`:
+
+```
+cp sample-implementations/linux_user_space/sensirion_i2c_hal.c ./
+```
+
+### Edit `sensirion_config.h`
+
+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 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
+* `int32_t` = signed 32bit integer
+* `uint32_t` = unsigned 32bit integer
+* `int16_t` = signed 16bit integer
+* `uint16_t` = unsigned 16bit integer
+* `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.
+
+
+
+Now we are ready to compile and run the example usage for your sensor.
+
+## Compile and Run
+
+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. Navigate to the subdirectory example-usage.
+4. Run `make` (this compiles the example code into one executable binary).
+5. Run the compiled executable with `./sen66_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 `./sen66_test_hw_i2c`.
+6. Now you should see the test output on your console.
+
+# Background
+
+## Files
+
+### 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.
+
+### sensirion\_i2c\_hal.[ch]
+
+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.*
+
+### 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.
+
+## 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..7ce1b66
--- /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}/sen66_i2c.h ${src_dir}/sen66_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: sen66_i2c_example_usage
+
+sen66_i2c_example_usage: clean
+ $(CC) $(CFLAGS) -o $@ ${driver_sources} ${i2c_sources} \
+ ${i2c_implementation} ${common_sources} sen66_i2c_example_usage.c
+
+clean:
+ $(RM) sen66_i2c_example_usage
\ No newline at end of file
diff --git a/example-usage/sen66_i2c_example_usage.c b/example-usage/sen66_i2c_example_usage.c
new file mode 100644
index 0000000..d7edc75
--- /dev/null
+++ b/example-usage/sen66_i2c_example_usage.c
@@ -0,0 +1,117 @@
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED
+ *
+ * Generator: sensirion-driver-generator 1.0.1
+ * Product: sen66
+ * Model-Version: 1.2.0
+ */
+/*
+ * Copyright (c) 2024, 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 "sen66_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 print_byte_array(uint8_t* array, uint16_t len) {
+ uint16_t i = 0;
+ printf("0x");
+ for (; i < len; i++) {
+ printf("%02x", array[i]);
+ }
+}
+
+int main(void) {
+ int16_t error = NO_ERROR;
+ sensirion_i2c_hal_init();
+ sen66_init(SEN66_I2C_ADDR_6B);
+
+ error = sen66_device_reset();
+ if (error != NO_ERROR) {
+ printf("error executing device_reset(): %i\n", error);
+ return error;
+ }
+ sensirion_hal_sleep_us(1200000);
+ uint8_t serial_number[32] = {0};
+ error = sen66_get_serial_number(serial_number, 32);
+ if (error != NO_ERROR) {
+ printf("error executing get_serial_number(): %i\n", error);
+ return error;
+ }
+ printf("serial_number: ");
+ print_byte_array(serial_number, 32);
+ printf("\n");
+ error = sen66_start_continuous_measurement();
+ if (error != NO_ERROR) {
+ printf("error executing start_continuous_measurement(): %i\n", error);
+ return error;
+ }
+ uint16_t mass_concentration_pm1p0 = 0;
+ uint16_t mass_concentration_pm2p5 = 0;
+ uint16_t mass_concentration_pm4p0 = 0;
+ uint16_t mass_concentration_pm10p0 = 0;
+ int16_t ambient_humidity = 0;
+ int16_t ambient_temperature = 0;
+ int16_t voc_index = 0;
+ int16_t nox_index = 0;
+ uint16_t co2 = 0;
+ uint16_t repetition = 0;
+ for (repetition = 0; repetition < 50; repetition++) {
+ sensirion_hal_sleep_us(1000000);
+ error = sen66_read_measured_values_as_integers(
+ &mass_concentration_pm1p0, &mass_concentration_pm2p5,
+ &mass_concentration_pm4p0, &mass_concentration_pm10p0,
+ &ambient_humidity, &ambient_temperature, &voc_index, &nox_index,
+ &co2);
+ if (error != NO_ERROR) {
+ printf("error executing read_measured_values_as_integers(): %i\n",
+ error);
+ continue;
+ }
+ printf("mass_concentration_pm1p0: %u ", mass_concentration_pm1p0);
+ printf("mass_concentration_pm2p5: %u ", mass_concentration_pm2p5);
+ printf("mass_concentration_pm4p0: %u ", mass_concentration_pm4p0);
+ printf("mass_concentration_pm10p0: %u ", mass_concentration_pm10p0);
+ printf("ambient_humidity: %i ", ambient_humidity);
+ printf("ambient_temperature: %i ", ambient_temperature);
+ printf("voc_index: %i ", voc_index);
+ printf("nox_index: %i ", nox_index);
+ printf("co2: %u\n", co2);
+ }
+
+ error = sen66_stop_measurement();
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return NO_ERROR;
+}
diff --git a/images/sen6x-pinout.png b/images/sen6x-pinout.png
new file mode 100644
index 0000000..42b5f0a
Binary files /dev/null and b/images/sen6x-pinout.png differ
diff --git a/images/sen6x.png b/images/sen6x.png
new file mode 100644
index 0000000..b97bde3
Binary files /dev/null and b/images/sen6x.png differ
diff --git a/metadata.yml b/metadata.yml
new file mode 100644
index 0000000..5a15951
--- /dev/null
+++ b/metadata.yml
@@ -0,0 +1,7 @@
+# driver generation metadata
+generator_version: 1.0.1
+model_version: 1.2.0
+dg_status: released
+is_manually_modified: false
+first_generated: '2024-10-30 08:14'
+last_generated: '2024-10-30 08:14'
diff --git a/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c b/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c
new file mode 100644
index 0000000..30ea666
--- /dev/null
+++ b/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_hal.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018, 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
+#include
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_hal.h"
+
+struct i2c_master_module i2c_master_instance;
+
+void sensirion_i2c_hal_init(void) {
+ /* initialize config structure and software module */
+ struct i2c_master_config config_i2c_master;
+ i2c_master_get_config_defaults(&config_i2c_master);
+
+ /* change buffer timeout to something longer */
+ config_i2c_master.buffer_timeout = 10000;
+
+ /* initialize and enable device with config. */
+ i2c_master_init(&i2c_master_instance, SERCOM2, &config_i2c_master);
+ i2c_master_enable(&i2c_master_instance);
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_hal_init().
+ */
+void sensirion_i2c_hal_free(void) {
+}
+
+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,
+ .data = (uint8_t*)data,
+ .ten_bit_address = false,
+ .high_speed = false,
+ };
+ return i2c_master_read_packet_wait(&i2c_master_instance, &packet);
+}
+
+int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data,
+ uint8_t count) {
+ struct i2c_master_packet packet = {
+ .address = address,
+ .data_length = count,
+ .data = (uint8_t*)data,
+ .ten_bit_address = false,
+ .high_speed = false,
+ };
+ return i2c_master_write_packet_wait_no_stop(&i2c_master_instance, &packet);
+}
+
+void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
+ delay_us(useconds);
+}
diff --git a/sample-implementations/GPIO_bit_banging/README.md b/sample-implementations/GPIO_bit_banging/README.md
new file mode 100644
index 0000000..1fd1d5d
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/README.md
@@ -0,0 +1,19 @@
+# GPIO based Software I2C
+
+This implementation of the I2C HAL uses bit banging on GPIOs to run
+the I2C communication. It is different from other sample-implementations.
+It uses an additional pair of `.c` and `.h` file to make the implementation
+generic, in a way that GPIO bitbanging can be performed on any system providing
+access to GPIOs. Note that in this case the pull-up resistors in the electric
+circuit are still needed, or need to be enabled manually if your microprocessor
+provides this option. These files are called `sensirion_i2c_gpio.[ch]`.
+Similar to the I2C HAL, we provide an unimplemented template in this in this
+folder to implement yourself for your platform and sample implementations in the
+so called folder.
+
+## Getting started
+
+To use this I2C HAL copy `sensirion_i2c_hal.c` and `sensirion_i2c_gpio.h`
+from this folder to the main folder of your driver. Then either choose a
+sample implementation or implement `sensirion_i2c_gpio.c` and copy
+it to the main driver folder as well.
diff --git a/sample-implementations/GPIO_bit_banging/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_gpio.c b/sample-implementations/GPIO_bit_banging/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_gpio.c
new file mode 100644
index 0000000..958111a
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sample-implementations/Atmel_SAMD2_series/sensirion_i2c_gpio.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2018, 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_gpio.h"
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+
+#include
+
+/*
+ * We use the following names for the two I2C signal lines:
+ * SCL for the clock line
+ * SDA for the data line
+ *
+ * Both lines must be equipped with pull-up resistors appropriate to the bus
+ * frequency.
+ */
+
+ioport_pin_t scl_port = IOPORT_CREATE_PIN(IOPORT_PORTA, 9);
+ioport_pin_t sda_port = IOPORT_CREATE_PIN(IOPORT_PORTA, 8);
+
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void) {
+ ioport_enable_pin(scl_port);
+ ioport_enable_pin(sda_port);
+ sensirion_i2c_gpio_SDA_in();
+ sensirion_i2c_gpio_SCL_in();
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void) {
+}
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void) {
+ ioport_set_pin_dir(sda_port, IOPORT_DIR_INPUT);
+ ioport_set_pin_mode(sda_port, IOPORT_MODE_OPEN_DRAIN);
+}
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void) {
+ ioport_set_pin_dir(sda_port, IOPORT_DIR_OUTPUT);
+ ioport_set_pin_level(sda_port, false);
+}
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void) {
+ return ioport_get_pin_level(sda_port);
+}
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void) {
+ ioport_set_pin_dir(scl_port, IOPORT_DIR_INPUT);
+ ioport_set_pin_mode(scl_port, IOPORT_MODE_OPEN_DRAIN);
+}
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void) {
+ ioport_set_pin_dir(scl_port, IOPORT_DIR_OUTPUT);
+ ioport_set_pin_level(scl_port, false);
+}
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void) {
+ return ioport_get_pin_level(scl_port);
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds) {
+ /* TODO: IMPLEMENT */
+}
diff --git a/sample-implementations/GPIO_bit_banging/sample-implementations/Nordic_nRF5_series/sensirion_i2c_gpio.c b/sample-implementations/GPIO_bit_banging/sample-implementations/Nordic_nRF5_series/sensirion_i2c_gpio.c
new file mode 100644
index 0000000..8009cd0
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sample-implementations/Nordic_nRF5_series/sensirion_i2c_gpio.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2018, 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_gpio.h"
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+
+#include
+
+/*
+ * We use the following names for the two I2C signal lines:
+ * SCL for the clock line
+ * SDA for the data line
+ *
+ * Both lines must be equipped with pull-up resistors appropriate to the bus
+ * frequency.
+ */
+
+/* Nordic specific include for Nordic specific configuration below */
+#include
+
+/**
+ * Nordic specific configuration. Change the pin numbers if you use other pins
+ * than defined below.
+ */
+#define SENSIRION_SDA_PIN 0
+#define SENSIRION_SCL_PIN 2
+
+uint32_t sda;
+uint32_t scl;
+volatile uint32_t* sclPinRegAddr;
+volatile uint32_t* sdaPinRegAddr;
+
+/* configuration register value for I2C output pin */
+static const uint32_t REGVALOUT =
+ (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
+ (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
+ (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
+ (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos) |
+ (GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
+/* configuration register value for I2C input pin */
+static const uint32_t REGVALIN =
+ (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos) |
+ (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos) |
+ (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos) |
+ (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) |
+ (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos);
+
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void) {
+ scl = SENSIRION_SCL_PIN;
+ sda = SENSIRION_SDA_PIN;
+ sclPinRegAddr = &NRF_GPIO->PIN_CNF[(uint32_t)scl];
+ sdaPinRegAddr = &NRF_GPIO->PIN_CNF[(uint32_t)sda];
+ *sclPinRegAddr = REGVALIN;
+ *sdaPinRegAddr = REGVALIN;
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void) {
+}
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void) {
+ *sdaPinRegAddr = REGVALIN;
+}
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void) {
+ *sdaPinRegAddr = REGVALOUT;
+}
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void) {
+ return (uint8_t)nrf_gpio_pin_read(sda);
+}
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void) {
+ *sclPinRegAddr = REGVALIN;
+}
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void) {
+ *sclPinRegAddr = REGVALOUT;
+}
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void) {
+ return (uint8_t)nrf_gpio_pin_read(scl);
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds) {
+ nrf_delay_us(useconds);
+}
diff --git a/sample-implementations/GPIO_bit_banging/sample-implementations/STM32F1_series/sensirion_i2c_gpio.c b/sample-implementations/GPIO_bit_banging/sample-implementations/STM32F1_series/sensirion_i2c_gpio.c
new file mode 100644
index 0000000..ead2498
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sample-implementations/STM32F1_series/sensirion_i2c_gpio.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2018, 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
+#include
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_gpio.h"
+
+/*
+ * We use the following names for the two I2C signal lines:
+ * SCL for the clock line
+ * SDA for the data line
+ *
+ * Both lines must be equipped with pull-up resistors appropriate to the bus
+ * frequency.
+ */
+
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void) {
+ __GPIOB_CLK_ENABLE();
+ sensirion_i2c_gpio_SDA_in();
+ sensirion_i2c_gpio_SCL_in();
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void) {
+}
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void) {
+ GPIO_InitTypeDef GPIO_InitStruct = {
+ .Pin = GPIO_PIN_9,
+ .Mode = GPIO_MODE_INPUT,
+ .Pull = GPIO_NOPULL,
+ .Speed = GPIO_SPEED_HIGH,
+ };
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+}
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void) {
+ GPIO_InitTypeDef GPIO_InitStruct = {
+ .Pin = GPIO_PIN_9,
+ .Mode = GPIO_MODE_OUTPUT_PP,
+ .Pull = GPIO_NOPULL,
+ .Speed = GPIO_SPEED_HIGH,
+ };
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
+}
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void) {
+ return (uint8_t)HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == GPIO_PIN_SET;
+}
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void) {
+ GPIO_InitTypeDef GPIO_InitStruct = {
+ .Pin = GPIO_PIN_8,
+ .Mode = GPIO_MODE_INPUT,
+ .Pull = GPIO_NOPULL,
+ .Speed = GPIO_SPEED_FREQ_HIGH,
+ };
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+}
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void) {
+ GPIO_InitTypeDef GPIO_InitStruct = {
+ .Pin = GPIO_PIN_8,
+ .Mode = GPIO_MODE_OUTPUT_PP,
+ .Pull = GPIO_NOPULL,
+ .Speed = GPIO_SPEED_FREQ_HIGH,
+ };
+ HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
+ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
+}
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void) {
+ return (uint8_t)HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET;
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds) {
+ HAL_Delay(useconds / 1000 + 1);
+}
diff --git a/sample-implementations/GPIO_bit_banging/sample-implementations/linux_user_space/sensirion_i2c_gpio.c b/sample-implementations/GPIO_bit_banging/sample-implementations/linux_user_space/sensirion_i2c_gpio.c
new file mode 100644
index 0000000..c7ce9f2
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sample-implementations/linux_user_space/sensirion_i2c_gpio.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/* Enable usleep function */
+#define _DEFAULT_SOURCE
+
+#include /* close, open */
+#include /* fprintf, perror, stderr */
+#include /* exit */
+#include /* strlen */
+#include /* mode_t */
+#include /* access, lseek, read, usleep */
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_gpio.h"
+
+/*
+ * We use the following names for the two I2C signal lines:
+ * SCL for the clock line
+ * SDA for the data line
+ *
+ * Both lines must be equipped with pull-up resistors appropriate to the bus
+ * frequency.
+ */
+#define GPIO_PIN_SCL 12
+#define GPIO_PIN_SDA 13
+#define GPIO_DIR "/sys/class/gpio/"
+
+#define __str(x) #x
+#define GPIO_PIN_STR(p) __str(p)
+#define GPIO_PIN_SCL_STR GPIO_PIN_STR(GPIO_PIN_SCL)
+#define GPIO_PIN_SDA_STR GPIO_PIN_STR(GPIO_PIN_SDA)
+#define GPIO_ID "gpio"
+#define GPIO(p) GPIO_ID __str(p)
+#define GPIO_PATH(p) GPIO_DIR p
+#define GPIO_EXPORT_PATH GPIO_PATH("export")
+#define GPIO_SCL_DIR GPIO_PATH(GPIO(GPIO_PIN_SCL))
+#define GPIO_SDA_DIR GPIO_PATH(GPIO(GPIO_PIN_SDA))
+#define GPIO_SCL_PATH(d) GPIO_SCL_DIR d
+#define GPIO_SDA_PATH(d) GPIO_SDA_DIR d
+#define GPIO_SCL_DIRECTION GPIO_SCL_PATH("/direction")
+#define GPIO_SDA_DIRECTION GPIO_SDA_PATH("/direction")
+#define GPIO_SCL_VALUE GPIO_SCL_PATH("/value")
+#define GPIO_SDA_VALUE GPIO_SDA_PATH("/value")
+
+#define GPIO_DIRECTION_IN "in"
+#define GPIO_DIRECTION_OUT "out"
+#define GPIO_LOW 0
+
+static int scl_dir_fd;
+static int scl_val_fd;
+static int sda_dir_fd;
+static int sda_val_fd;
+
+static int open_or_exit(const char* path, int flags) {
+ int fd = open(path, flags);
+ if (fd < 0) {
+ perror(NULL);
+ fprintf(stderr, "Error opening %s (mode %d)\n", path, flags);
+ exit(-1);
+ }
+ return fd;
+}
+
+static void rev_or_exit(int fd) {
+ if (lseek(fd, 0, SEEK_SET) < 0) {
+ perror("Error seeking gpio");
+ exit(-1);
+ }
+}
+
+static void write_or_exit(int fd, const char* buf) {
+ size_t len = strlen(buf);
+
+ ssize_t w = write(fd, buf, len);
+
+ /* Adapted from stackoverflow answer by Stephen Canon
+ See: https://www.stackoverflow.com/a/16086724 */
+ if (w < 0 || (size_t)w != len) {
+ perror("Error writing");
+ exit(-1);
+ }
+}
+
+static void gpio_export(const char* path, const char* export_pin) {
+ int fd;
+
+ if (access(path, F_OK) == -1) {
+ fd = open_or_exit(GPIO_EXPORT_PATH, O_WRONLY);
+ write_or_exit(fd, export_pin);
+ close(fd);
+ }
+}
+
+static void gpio_set_value(int fd, int value) {
+ char buf[] = {'0', '\0'};
+
+ buf[0] += value;
+ rev_or_exit(fd);
+ write_or_exit(fd, buf);
+}
+
+static void gpio_set_direction(int fd, const char* dir) {
+ rev_or_exit(fd);
+ write_or_exit(fd, dir);
+}
+
+static uint8_t gpio_get_value(int fd) {
+ char c;
+
+ rev_or_exit(fd);
+ if (read(fd, &c, 1) != 1) {
+ perror("Error reading GPIO value");
+ exit(-1);
+ }
+ return c == '1';
+}
+
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void) {
+ gpio_export(GPIO_SCL_DIR, GPIO_PIN_SCL_STR);
+ gpio_export(GPIO_SDA_DIR, GPIO_PIN_SDA_STR);
+
+ scl_dir_fd = open_or_exit(GPIO_SCL_DIRECTION, O_WRONLY);
+ scl_val_fd = open_or_exit(GPIO_SCL_VALUE, O_RDWR);
+ sda_dir_fd = open_or_exit(GPIO_SDA_DIRECTION, O_WRONLY);
+ sda_val_fd = open_or_exit(GPIO_SDA_VALUE, O_RDWR);
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void) {
+}
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void) {
+ gpio_set_direction(sda_dir_fd, GPIO_DIRECTION_IN);
+}
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void) {
+ gpio_set_direction(sda_dir_fd, GPIO_DIRECTION_OUT);
+ gpio_set_value(sda_val_fd, GPIO_LOW);
+}
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void) {
+ return gpio_get_value(sda_val_fd);
+}
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void) {
+ gpio_set_direction(scl_dir_fd, GPIO_DIRECTION_IN);
+}
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void) {
+ gpio_set_direction(scl_dir_fd, GPIO_DIRECTION_OUT);
+ gpio_set_value(scl_val_fd, GPIO_LOW);
+}
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void) {
+ return gpio_get_value(scl_val_fd);
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds) {
+ usleep(useconds);
+}
diff --git a/sample-implementations/GPIO_bit_banging/sample-implementations/mbed/sensirion_i2c_gpio.cpp b/sample-implementations/GPIO_bit_banging/sample-implementations/mbed/sensirion_i2c_gpio.cpp
new file mode 100644
index 0000000..ee0d8fa
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sample-implementations/mbed/sensirion_i2c_gpio.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2018, 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
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_gpio.h"
+
+/*
+ * We use the following names for the two I2C signal lines:
+ * SCL for the clock line
+ * SDA for the data line
+ *
+ * Both lines must be equipped with pull-up resistors appropriate to the bus
+ * frequency.
+ */
+
+/* Create SDA and SCL pin objects. Different pins can be used too. */
+DigitalInOut sda(p28);
+DigitalInOut scl(p27);
+
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void) {
+ /* Do nothing */
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void) {
+ /* Do nothing */
+}
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void) {
+ sda.input();
+ sda = 1;
+}
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void) {
+ sda.output();
+ sda = 0;
+}
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void) {
+ return sda.read();
+}
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void) {
+ scl.input();
+ scl = 1;
+}
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void) {
+ scl.output();
+ scl = 0;
+}
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void) {
+ return scl.read();
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds) {
+ wait_us(useconds);
+}
diff --git a/sample-implementations/GPIO_bit_banging/sensirion_i2c_gpio.c b/sample-implementations/GPIO_bit_banging/sensirion_i2c_gpio.c
new file mode 100644
index 0000000..d1d03bb
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sensirion_i2c_gpio.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2018, 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_gpio.h"
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+
+/*
+ * INSTRUCTIONS
+ * ============
+ *
+ * Implement all functions where they are marked as IMPLEMENT.
+ * Follow the function specification in the comments.
+ *
+ * We use the following names for the two I2C signal lines:
+ * SCL for the clock line
+ * SDA for the data line
+ *
+ * Both lines must be equipped with pull-up resistors appropriate to the bus
+ * frequency.
+ */
+
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void) {
+ /* TODO:IMPLEMENT or leave empty if no resources need to be freed */
+}
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void) {
+ /* TODO:IMPLEMENT */
+ return NOT_IMPLEMENTED_ERROR;
+}
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void) {
+ /* TODO:IMPLEMENT */
+ return NOT_IMPLEMENTED_ERROR;
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds) {
+ /* TODO:IMPLEMENT */
+}
diff --git a/sample-implementations/GPIO_bit_banging/sensirion_i2c_gpio.h b/sample-implementations/GPIO_bit_banging/sensirion_i2c_gpio.h
new file mode 100644
index 0000000..19dcc62
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sensirion_i2c_gpio.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018, 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_SW_I2C_H
+#define SENSIRION_SW_I2C_H
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+
+/**
+ * The clock period of the i2c bus in microseconds. Increase this, if your GPIO
+ * ports cannot support a 200 kHz output rate. (2 * 1 / 10usec == 200Khz)
+ *
+ * This is only relevant for the sw-i2c HAL (bit-banging on GPIO pins). The
+ * pulse length is half the clock period, the number should thus be even.
+ */
+#define SENSIRION_I2C_CLOCK_PERIOD_USEC 10
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/**
+ * Initialize all hard- and software components that are needed to set the
+ * SDA and SCL pins.
+ */
+void sensirion_i2c_gpio_init_pins(void);
+
+/**
+ * Release all resources initialized by sensirion_i2c_gpio_init_pins()
+ */
+void sensirion_i2c_gpio_release_pins(void);
+
+/**
+ * Configure the SDA pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SDA_in(void);
+
+/**
+ * Configure the SDA pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SDA_out(void);
+
+/**
+ * Read the value of the SDA pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SDA_read(void);
+
+/**
+ * Configure the SCL pin as an input. With an external pull-up resistor the line
+ * should be left floating, without external pull-up resistor, the input must be
+ * configured to use the internal pull-up resistor.
+ */
+void sensirion_i2c_gpio_SCL_in(void);
+
+/**
+ * Configure the SCL pin as an output and drive it low or set to logical false.
+ */
+void sensirion_i2c_gpio_SCL_out(void);
+
+/**
+ * Read the value of the SCL pin.
+ * @returns 0 if the pin is low and 1 otherwise.
+ */
+uint8_t sensirion_i2c_gpio_SCL_read(void);
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_gpio_sleep_usec(uint32_t useconds);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SENSIRION_SW_I2C_H */
diff --git a/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c b/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c
new file mode 100644
index 0000000..3d2f6d0
--- /dev/null
+++ b/sample-implementations/GPIO_bit_banging/sensirion_i2c_hal.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2018, 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 "sensirion_i2c.h"
+#include "sensirion_i2c_gpio.h"
+
+#define DELAY_USEC (SENSIRION_I2C_CLOCK_PERIOD_USEC / 2)
+
+/**
+ * Declaration of static helpers.
+ */
+static int8_t sensirion_i2c_gpio_write_byte(uint8_t data);
+static uint8_t sensirion_i2c_gpio_read_byte(uint8_t ack);
+static int8_t sensirion_i2c_gpio_start(void);
+static void sensirion_i2c_gpio_stop(void);
+
+/**
+ * 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) {
+ return NO_ERROR; /* not relevant for software I2C */
+}
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ sensirion_i2c_gpio_init_pins();
+ sensirion_i2c_gpio_SCL_in();
+ sensirion_i2c_gpio_SDA_in();
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_hal_init().
+ */
+void sensirion_i2c_hal_free(void) {
+ sensirion_i2c_gpio_SCL_in();
+ sensirion_i2c_gpio_SDA_in();
+ sensirion_i2c_gpio_release_pins();
+}
+
+/**
+ * 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, uint8_t count) {
+ int8_t ret;
+ uint8_t send_ack;
+ uint16_t i;
+
+ ret = sensirion_i2c_gpio_start();
+ if (ret != NO_ERROR)
+ return ret;
+
+ ret = sensirion_i2c_gpio_write_byte((address << 1) | 1);
+ if (ret != NO_ERROR) {
+ sensirion_i2c_gpio_stop();
+ return ret;
+ }
+ for (i = 0; i < count; i++) {
+ send_ack = i < (count - 1); /* last byte must be NACK'ed */
+ data[i] = sensirion_i2c_gpio_read_byte(send_ack);
+ }
+
+ sensirion_i2c_gpio_stop();
+ return NO_ERROR;
+}
+
+/**
+ * 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,
+ uint8_t count) {
+ int8_t ret;
+ uint16_t i;
+
+ ret = sensirion_i2c_gpio_start();
+ if (ret != NO_ERROR)
+ return ret;
+
+ ret = sensirion_i2c_gpio_write_byte(address << 1);
+ if (ret != NO_ERROR) {
+ sensirion_i2c_gpio_stop();
+ return ret;
+ }
+ for (i = 0; i < count; i++) {
+ ret = sensirion_i2c_gpio_write_byte(data[i]);
+ if (ret != NO_ERROR) {
+ sensirion_i2c_gpio_stop();
+ break;
+ }
+ }
+ sensirion_i2c_gpio_stop();
+ return ret;
+}
+
+/**
+ * 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) {
+ sensirion_i2c_gpio_sleep_usec(useconds);
+}
+
+/**
+ * The following functions are static helpers.
+ */
+
+static int8_t sensirion_wait_while_clock_stretching(void) {
+ /* Maximal timeout of 150ms (SCD30) in sleep polling cycles */
+ uint32_t timeout_cycles = 150000 / SENSIRION_I2C_CLOCK_PERIOD_USEC;
+
+ while (--timeout_cycles) {
+ if (sensirion_i2c_gpio_SCL_read())
+ return NO_ERROR;
+ sensirion_i2c_gpio_sleep_usec(SENSIRION_I2C_CLOCK_PERIOD_USEC);
+ }
+
+ return I2C_BUS_ERROR;
+}
+
+static int8_t sensirion_i2c_gpio_write_byte(uint8_t data) {
+ int8_t nack, i;
+ for (i = 7; i >= 0; i--) {
+ sensirion_i2c_gpio_SCL_out();
+ if ((data >> i) & 0x01)
+ sensirion_i2c_gpio_SDA_in();
+ else
+ sensirion_i2c_gpio_SDA_out();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SCL_in();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ if (sensirion_wait_while_clock_stretching())
+ return I2C_BUS_ERROR;
+ }
+ sensirion_i2c_gpio_SCL_out();
+ sensirion_i2c_gpio_SDA_in();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SCL_in();
+ if (sensirion_wait_while_clock_stretching())
+ return I2C_BUS_ERROR;
+ nack = (sensirion_i2c_gpio_SDA_read() != 0);
+ sensirion_i2c_gpio_SCL_out();
+
+ return nack;
+}
+
+static uint8_t sensirion_i2c_gpio_read_byte(uint8_t ack) {
+ int8_t i;
+ uint8_t data = 0;
+ sensirion_i2c_gpio_SDA_in();
+ for (i = 7; i >= 0; i--) {
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SCL_in();
+ if (sensirion_wait_while_clock_stretching())
+ return 0xFF; /* return 0xFF on error */
+ data |= (sensirion_i2c_gpio_SDA_read() != 0) << i;
+ sensirion_i2c_gpio_SCL_out();
+ }
+ if (ack)
+ sensirion_i2c_gpio_SDA_out();
+ else
+ sensirion_i2c_gpio_SDA_in();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SCL_in();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ if (sensirion_wait_while_clock_stretching())
+ return 0xFF; /* return 0xFF on error */
+ sensirion_i2c_gpio_SCL_out();
+ sensirion_i2c_gpio_SDA_in();
+
+ return data;
+}
+
+static int8_t sensirion_i2c_gpio_start(void) {
+ sensirion_i2c_gpio_SCL_in();
+ if (sensirion_wait_while_clock_stretching())
+ return I2C_BUS_ERROR;
+
+ sensirion_i2c_gpio_SDA_out();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SCL_out();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ return NO_ERROR;
+}
+
+static void sensirion_i2c_gpio_stop(void) {
+ sensirion_i2c_gpio_SDA_out();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SCL_in();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+ sensirion_i2c_gpio_SDA_in();
+ sensirion_i2c_gpio_sleep_usec(DELAY_USEC);
+}
diff --git a/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c b/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c
new file mode 100644
index 0000000..e26ba9c
--- /dev/null
+++ b/sample-implementations/Nordic_nRF5_series/sensirion_i2c_hal.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018, 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
+#include
+#include
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_hal.h"
+
+/**
+ * Nordic specific configuration. Change the pin numbers if you use other pins
+ * than defined below.
+ */
+#define SENSIRION_SDA_PIN 0
+#define SENSIRION_SCL_PIN 2
+
+/**
+ * Create new TWI instance. You may also use a different interface. In this
+ * case, please adapt the code below.
+ */
+static const nrf_drv_twi_t i2c_instance = NRF_DRV_TWI_INSTANCE(0);
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ int8_t err;
+ const nrf_drv_twi_config_t i2c_instance_config = {.scl = SENSIRION_SCL_PIN,
+ .sda = SENSIRION_SDA_PIN,
+ .frequency =
+ NRF_TWI_FREQ_100K,
+ .interrupt_priority = 0};
+ /* initiate TWI instance */
+ err = nrf_drv_twi_init(&i2c_instance, &i2c_instance_config, NULL, NULL);
+ if (err) {
+ /* Could be omitted if the prototyp is changed to non-void or an error
+ * flag is introduced */
+ printf("Error %d: Initialization of I2C connection failed!\n", err);
+ }
+ /* enable TWI instance */
+ nrf_drv_twi_enable(&i2c_instance);
+ return;
+}
+
+/**
+ * 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
+ *
+ * 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, uint8_t count) {
+ int8_t err = nrf_drv_twi_rx(&i2c_instance, address, data, count);
+ return err;
+}
+
+/**
+ * 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
+ *
+ * error codes: 3 -> error detected by hardware (internal error)
+ * 17 -> driver not ready for new transfer (busy)
+ */
+int8_t sensirion_i2c_hal_write(uint8_t address, const uint8_t* data,
+ uint8_t count) {
+ int8_t err = nrf_drv_twi_tx(&i2c_instance, address, data, count, false);
+ return err;
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution for at least the given time, but may also sleep longer.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
+ nrf_delay_us(useconds);
+}
diff --git a/sample-implementations/RaspberryPi_Pico/cMakeLists.txt b/sample-implementations/RaspberryPi_Pico/cMakeLists.txt
new file mode 100644
index 0000000..4a9c75a
--- /dev/null
+++ b/sample-implementations/RaspberryPi_Pico/cMakeLists.txt
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.12)
+include(${PICO_SDK_PATH}/pico_sdk_init.cmake)
+
+project(SCD4XSensor C CXX ASM)
+pico_sdk_init()
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
+add_executable(main
+ main.c
+ sensirion_i2c.c
+ sensirion_i2c.hal.c
+ scd4x_i2c.c
+ sensirion_common.c)
+
+# pull in common dependencies and additional i2c hardware support
+target_link_libraries(main pico_stdlib hardware_i2c)
+
+pico_enable_stdio_usb(main 1)
+
+pico_enable_stdio_uart(main 0)
+
+# create map/bin/hex file etc.
+pico_add_extra_outputs(main)
+
diff --git a/sample-implementations/RaspberryPi_Pico/main.c b/sample-implementations/RaspberryPi_Pico/main.c
new file mode 100644
index 0000000..195febe
--- /dev/null
+++ b/sample-implementations/RaspberryPi_Pico/main.c
@@ -0,0 +1,77 @@
+
+
+#include "hardware/i2c.h"
+#include "pico/binary_info.h"
+#include "pico/stdlib.h"
+#include "scd4x_i2c.h"
+#include
+
+/// I2C address
+static int addr = 0x62;
+
+// I2C Pins
+static uint sda_pin = 16;
+static uint scl_pin = 17;
+
+// This is the main entry for your c application. U
+// is
+int main() {
+
+ stdio_init_all();
+
+ // Setup I2c using pins 16 & 17
+ i2c_init(i2c_default, 400 * 1000);
+ gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
+ gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
+
+ // This variable will hold the return status of the function calls.
+ // You can separate each function call result into their own variable or re
+ // - use this.
+
+ int status = 0;
+
+ // Stop any readings if occuring
+ status = scd4x_stop_periodic_measurement();
+
+ // Perform self test
+ uint16_t* selfTest = 0;
+ scd4x_perform_self_test(selfTest);
+
+ // Get Serial number 3 parts
+ uint16_t one;
+ uint16_t two;
+ uint16_t three;
+
+ scd4x_get_serial_number(&one, &two, &three);
+
+ // Start the readings.
+ status1 = scd4x_start_periodic_measurement();
+
+ while (1) {
+
+ // Check if data is ready to read
+ bool dataReady;
+ while (dataReady == false) {
+
+ status1 = scd4x_get_data_ready_flag(&dataReady);
+ }
+
+ // Get the ticks. The scd4x_read_measurement function is giving
+ // incorrect data due to the arthimetic
+ uint16_t co2;
+ uint16_t temp;
+ uint16_t humidity;
+ status1 = scd4x_read_measurement_ticks(&co2, &temp, &humidity);
+
+ // Arithemtic to change raw data into information
+ int tempInCelsius = -45 + 175 * temp / 65536;
+ int tempInFarenheit = tempInCelsius * 1.8 + 32;
+ int humidityPercent = 100 * humidity / 65536;
+
+ // Print results to terminal (output)
+ printf("C:%d,T:%d,H:%d", co2, tempInFarenheit, humidityPercent);
+
+ // Sleep for 5 seconds.
+ sleep_ms(5000);
+ }
+}
diff --git a/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c b/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c
new file mode 100644
index 0000000..d7299ba
--- /dev/null
+++ b/sample-implementations/RaspberryPi_Pico/sensirion_i2c_hal.c
@@ -0,0 +1,127 @@
+#include
+/*
+ * Copyright (c) 2018, 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_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_hal.h"
+
+/*
+ * 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;
+}
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_hal_init().
+ */
+void sensirion_i2c_hal_free(void) {
+ /* TODO:IMPLEMENT or leave empty if no resources need to be freed */
+}
+
+/**
+ * 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) {
+ int status = i2c_read_blocking(i2c_default, address, data, count, false);
+ if (status == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * 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) {
+ // I2C Default is used (I2C0).
+ int status = i2c_write_blocking(i2c_default, address, data, count, true);
+
+ if (status == 0)
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * 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) {
+ sleep_ms(useconds / 1000);
+}
diff --git a/sample-implementations/STM32F1_series/sensirion_i2c_hal.c b/sample-implementations/STM32F1_series/sensirion_i2c_hal.c
new file mode 100644
index 0000000..83db176
--- /dev/null
+++ b/sample-implementations/STM32F1_series/sensirion_i2c_hal.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018, 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
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_hal.h"
+
+/**
+ * Create new I2C instance. You may also use a different interface, e.g. hi2c2,
+ * depending on your CubeMX configuration
+ */
+static I2C_HandleTypeDef hi2c1;
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ hi2c1.Instance = I2C1;
+ hi2c1.Init.ClockSpeed = 100000;
+ hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
+ hi2c1.Init.OwnAddress1 = 0;
+ hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
+ hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
+ hi2c1.Init.OwnAddress2 = 0;
+ hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
+ hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
+ /* Enable the remapping of Pins 6/7 to 8/9 and the I2C clock before the
+ * initialization of the GPIO Pins in HAL_I2C_Init(). This is a fix of the
+ * code generated by CubeMX v4.16.0 */
+ __HAL_AFIO_REMAP_I2C1_ENABLE();
+ __HAL_RCC_I2C1_CLK_ENABLE();
+ HAL_I2C_Init(&hi2c1);
+}
+
+/**
+ * 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, uint8_t count) {
+ return (int8_t)HAL_I2C_Master_Receive(&hi2c1, (uint16_t)(address << 1),
+ data, count, 100);
+}
+
+/**
+ * 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,
+ uint8_t count) {
+ return (int8_t)HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)(address << 1),
+ (uint8_t*)data, count, 100);
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution for at least the given time, but may also sleep longer.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
+ uint32_t msec = useconds / 1000;
+ if (useconds % 1000 > 0) {
+ msec++;
+ }
+
+ /*
+ * Increment by 1 if STM32F1 driver version less than 1.1.1
+ * Old firmwares of STM32F1 sleep 1ms shorter than specified in HAL_Delay.
+ * This was fixed with firmware 1.6 (driver version 1.1.1), so we have to
+ * fix it ourselves for older firmwares
+ */
+ if (HAL_GetHalVersion() < 0x01010100) {
+ msec++;
+ }
+
+ HAL_Delay(msec);
+}
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
new file mode 100644
index 0000000..74a7f57
--- /dev/null
+++ b/sample-implementations/linux_user_space/sensirion_i2c_hal.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2018, 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.
+ */
+
+/* Enable usleep function */
+#define _DEFAULT_SOURCE
+
+#include "sensirion_i2c_hal.h"
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+
+#include
+#include
+#include
+#include
+
+/**
+ * Linux specific configuration. Adjust the following define to the device path
+ * of your sensor.
+ */
+#define I2C_DEVICE_PATH "/dev/i2c-1"
+
+/**
+ * The following define was taken from i2c-dev.h. Alternatively the header file
+ * can be included. The define was added in Linux v3.10 and never changed since
+ * then.
+ */
+#define I2C_SLAVE 0x0703
+
+#define I2C_WRITE_FAILED -1
+#define I2C_READ_FAILED -1
+
+static int i2c_device = -1;
+static uint8_t i2c_address = 0;
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ /* open i2c adapter */
+ i2c_device = open(I2C_DEVICE_PATH, O_RDWR);
+ if (i2c_device == -1)
+ return; /* no error handling */
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_hal_init().
+ */
+void sensirion_i2c_hal_free(void) {
+ if (i2c_device >= 0)
+ close(i2c_device);
+}
+
+/**
+ * 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, uint8_t count) {
+ if (i2c_address != address) {
+ ioctl(i2c_device, I2C_SLAVE, address);
+ i2c_address = address;
+ }
+
+ if (read(i2c_device, data, count) != count) {
+ return I2C_READ_FAILED;
+ }
+ return 0;
+}
+
+/**
+ * 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,
+ uint8_t count) {
+ if (i2c_address != address) {
+ ioctl(i2c_device, I2C_SLAVE, address);
+ i2c_address = address;
+ }
+
+ if (write(i2c_device, data, count) != count) {
+ return I2C_WRITE_FAILED;
+ }
+ return 0;
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution for at least the given time, but may also sleep longer.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
+ usleep(useconds);
+}
diff --git a/sample-implementations/mbed/sensirion_i2c_hal.cpp b/sample-implementations/mbed/sensirion_i2c_hal.cpp
new file mode 100644
index 0000000..ce846a1
--- /dev/null
+++ b/sample-implementations/mbed/sensirion_i2c_hal.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018, 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
+
+#define E_MBED_I2C_READ_FAILED -1
+#define E_MBED_I2C_WRITE_FAILED -1
+
+/* open I2C connection */
+static I2C i2c_connection(I2C_SDA, I2C_SCL);
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+}
+
+/**
+ * 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, uint8_t count) {
+ if (i2c_connection.read(address << 1, (char*)data, count) != 0)
+ return E_MBED_I2C_READ_FAILED;
+ return 0;
+}
+
+/**
+ * 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,
+ uint8_t count) {
+ if (i2c_connection.write(address << 1, (char*)data, count) != 0)
+ return E_MBED_I2C_WRITE_FAILED;
+ return 0;
+}
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution for at least the given time, but may also sleep longer.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_hal_sleep_usec(uint32_t useconds) {
+ wait_us(useconds);
+}
diff --git a/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c b/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c
new file mode 100644
index 0000000..a0dd0d4
--- /dev/null
+++ b/sample-implementations/zephyr_user_space/sensirion_i2c_hal.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018, 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
+#include
+#include
+
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_hal.h"
+
+/* I2C device. */
+static struct device* i2c_dev;
+
+/**
+ * Select the current i2c bus by index.
+ * All following i2c operations will be directed at that 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) {
+ char bus_name[6] = "I2C_0";
+
+ if (bus_idx > 9) {
+ /* Invalid bus index */
+ return STATUS_FAIL;
+ }
+
+ bus_name[4] = bus_idx + '0';
+ i2c_dev = device_get_binding(bus_name);
+ if (i2c_dev == NULL) {
+ /* No valid device found */
+ return STATUS_FAIL;
+ }
+
+ return STATUS_OK;
+}
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ /* Device (specified by sps30_i2c_dev) is already initialized by the Zephyr
+ * boot-up process. Nothing to be done here. */
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_hal_init().
+ */
+void sensirion_i2c_hal_free(void) {
+ i2c_dev = NULL;
+}
+
+/**
+ * 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, uint8_t count) {
+ return i2c_read(i2c_dev, data, count, address);
+}
+
+/**
+ * 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,
+ uint8_t count) {
+ return i2c_write(i2c_dev, data, count, address);
+}
+
+/**
+ * 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) {
+ int32_t remaining = useconds;
+ while (remaining > 0) {
+ remaining = k_usleep(remaining);
+ }
+}
diff --git a/sen66_i2c.c b/sen66_i2c.c
new file mode 100644
index 0000000..97a9f4c
--- /dev/null
+++ b/sen66_i2c.c
@@ -0,0 +1,252 @@
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED
+ *
+ * Generator: sensirion-driver-generator 1.0.1
+ * Product: sen66
+ * Model-Version: 1.2.0
+ */
+/*
+ * Copyright (c) 2024, 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 "sen66_i2c.h"
+#include "sensirion_common.h"
+#include "sensirion_i2c.h"
+#include "sensirion_i2c_hal.h"
+
+#define sensirion_hal_sleep_us sensirion_i2c_hal_sleep_usec
+
+static uint8_t communication_buffer[48] = {0};
+
+static uint8_t _i2c_address;
+
+void sen66_init(uint8_t i2c_address) {
+ _i2c_address = i2c_address;
+}
+
+int16_t sen66_device_reset() {
+ 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, 0xd304);
+ 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(1200 * 1000);
+ return local_error;
+}
+
+int16_t sen66_start_continuous_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, 0x21);
+ 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(50 * 1000);
+ return local_error;
+}
+
+int16_t sen66_stop_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, 0x104);
+ 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(160 * 1000);
+ return local_error;
+}
+
+int16_t sen66_get_data_ready(uint8_t* padding, bool* data_ready) {
+ 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, 0x202);
+ 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(20 * 1000);
+ local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ *padding = (uint8_t)buffer_ptr[0];
+ *data_ready = (bool)buffer_ptr[1];
+ return local_error;
+}
+
+int16_t sen66_read_measured_values_as_integers(
+ uint16_t* mass_concentration_pm1p0, uint16_t* mass_concentration_pm2p5,
+ uint16_t* mass_concentration_pm4p0, uint16_t* mass_concentration_pm10p0,
+ int16_t* ambient_humidity, int16_t* ambient_temperature, int16_t* voc_index,
+ int16_t* nox_index, uint16_t* co2) {
+ 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, 0x300);
+ 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(20 * 1000);
+ local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 18);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ *mass_concentration_pm1p0 =
+ sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]);
+ *mass_concentration_pm2p5 =
+ sensirion_common_bytes_to_uint16_t(&buffer_ptr[2]);
+ *mass_concentration_pm4p0 =
+ sensirion_common_bytes_to_uint16_t(&buffer_ptr[4]);
+ *mass_concentration_pm10p0 =
+ sensirion_common_bytes_to_uint16_t(&buffer_ptr[6]);
+ *ambient_humidity = sensirion_common_bytes_to_int16_t(&buffer_ptr[8]);
+ *ambient_temperature = sensirion_common_bytes_to_int16_t(&buffer_ptr[10]);
+ *voc_index = sensirion_common_bytes_to_int16_t(&buffer_ptr[12]);
+ *nox_index = sensirion_common_bytes_to_int16_t(&buffer_ptr[14]);
+ *co2 = sensirion_common_bytes_to_uint16_t(&buffer_ptr[16]);
+ return local_error;
+}
+
+int16_t
+sen66_perform_forced_co2_recalibration(uint16_t target_co2_concentration,
+ uint16_t* correction) {
+ 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, 0x6707);
+ 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(500 * 1000);
+ local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 2);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ *correction = sensirion_common_bytes_to_uint16_t(&buffer_ptr[0]);
+ return local_error;
+}
+
+int16_t sen66_get_product_name(uint8_t* product_name,
+ uint16_t product_name_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, 0xd014);
+ 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(20 * 1000);
+ local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 32);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ sensirion_common_copy_bytes(&buffer_ptr[0], (uint8_t*)product_name,
+ product_name_size);
+ return local_error;
+}
+
+int16_t sen66_get_serial_number(uint8_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, 0xd033);
+ 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(20 * 1000);
+ local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 32);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ sensirion_common_copy_bytes(&buffer_ptr[0], (uint8_t*)serial_number,
+ serial_number_size);
+ return local_error;
+}
+
+int16_t sen66_get_version(uint8_t* firmware_major, uint8_t* firmware_minor,
+ bool* firmware_debug, uint8_t* hardware_major,
+ uint8_t* hardware_minor, uint8_t* protocol_major,
+ uint8_t* protocol_minor, uint8_t* padding) {
+ 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, 0xd000);
+ 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(20 * 1000);
+ local_error = sensirion_i2c_read_data_inplace(_i2c_address, buffer_ptr, 8);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ *firmware_major = (uint8_t)buffer_ptr[0];
+ *firmware_minor = (uint8_t)buffer_ptr[1];
+ *firmware_debug = (bool)buffer_ptr[2];
+ *hardware_major = (uint8_t)buffer_ptr[3];
+ *hardware_minor = (uint8_t)buffer_ptr[4];
+ *protocol_major = (uint8_t)buffer_ptr[5];
+ *protocol_minor = (uint8_t)buffer_ptr[6];
+ *padding = (uint8_t)buffer_ptr[7];
+ return local_error;
+}
diff --git a/sen66_i2c.h b/sen66_i2c.h
new file mode 100644
index 0000000..e88b470
Binary files /dev/null and b/sen66_i2c.h differ
diff --git a/sensirion_common.c b/sensirion_common.c
new file mode 100644
index 0000000..3cab3c1
--- /dev/null
+++ b/sensirion_common.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2018, 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_common.h"
+#include "sensirion_config.h"
+
+uint16_t sensirion_common_bytes_to_uint16_t(const uint8_t* bytes) {
+ return (uint16_t)bytes[0] << 8 | (uint16_t)bytes[1];
+}
+
+uint32_t sensirion_common_bytes_to_uint32_t(const uint8_t* bytes) {
+ return (uint32_t)bytes[0] << 24 | (uint32_t)bytes[1] << 16 |
+ (uint32_t)bytes[2] << 8 | (uint32_t)bytes[3];
+}
+
+int16_t sensirion_common_bytes_to_int16_t(const uint8_t* bytes) {
+ return (int16_t)sensirion_common_bytes_to_uint16_t(bytes);
+}
+
+int32_t sensirion_common_bytes_to_int32_t(const uint8_t* bytes) {
+ return (int32_t)sensirion_common_bytes_to_uint32_t(bytes);
+}
+
+float sensirion_common_bytes_to_float(const uint8_t* bytes) {
+ union {
+ uint32_t u32_value;
+ float float32;
+ } tmp;
+
+ tmp.u32_value = sensirion_common_bytes_to_uint32_t(bytes);
+ return tmp.float32;
+}
+
+void sensirion_common_uint32_t_to_bytes(const uint32_t value, uint8_t* bytes) {
+ 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] = (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] = (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] = (uint8_t)(value >> 8);
+ bytes[1] = (uint8_t)value;
+}
+
+void sensirion_common_float_to_bytes(const float value, uint8_t* bytes) {
+ union {
+ uint32_t u32_value;
+ float float32;
+ } tmp;
+ tmp.float32 = value;
+ sensirion_common_uint32_t_to_bytes(tmp.u32_value, bytes);
+}
+
+void sensirion_common_copy_bytes(const uint8_t* source, uint8_t* destination,
+ uint16_t data_length) {
+ uint16_t i;
+ for (i = 0; i < data_length; i++) {
+ 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
new file mode 100644
index 0000000..e290933
--- /dev/null
+++ b/sensirion_common.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2018, 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_COMMON_H
+#define SENSIRION_COMMON_H
+
+#include "sensirion_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NO_ERROR 0
+#define NOT_IMPLEMENTED_ERROR 31
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
+#endif
+
+#define SENSIRION_COMMAND_SIZE 2
+#define SENSIRION_WORD_SIZE 2
+#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
+ *
+ * Convert an array of bytes received from the sensor in big-endian/MSB-first
+ * format to an int16_t value in the correct system-endianness.
+ *
+ * @param bytes An array of at least two bytes (MSB first)
+ * @return The byte array represented as int16_t
+ */
+int16_t sensirion_common_bytes_to_int16_t(const uint8_t* bytes);
+
+/**
+ * sensirion_common_bytes_to_int32_t() - Convert an array of bytes to an int32_t
+ *
+ * Convert an array of bytes received from the sensor in big-endian/MSB-first
+ * format to an int32_t value in the correct system-endianness.
+ *
+ * @param bytes An array of at least four bytes (MSB first)
+ * @return The byte array represented as int32_t
+ */
+int32_t sensirion_common_bytes_to_int32_t(const uint8_t* bytes);
+
+/**
+ * sensirion_common_bytes_to_uint16_t() - Convert an array of bytes to an
+ * uint16_t
+ *
+ * Convert an array of bytes received from the sensor in big-endian/MSB-first
+ * format to an uint16_t value in the correct system-endianness.
+ *
+ * @param bytes An array of at least two bytes (MSB first)
+ * @return The byte array represented as uint16_t
+ */
+uint16_t sensirion_common_bytes_to_uint16_t(const uint8_t* bytes);
+
+/**
+ * sensirion_common_bytes_to_uint32_t() - Convert an array of bytes to an
+ * uint32_t
+ *
+ * Convert an array of bytes received from the sensor in big-endian/MSB-first
+ * format to an uint32_t value in the correct system-endianness.
+ *
+ * @param bytes An array of at least four bytes (MSB first)
+ * @return The byte array represented as uint32_t
+ */
+uint32_t sensirion_common_bytes_to_uint32_t(const uint8_t* bytes);
+
+/**
+ * sensirion_common_bytes_to_float() - Convert an array of bytes to a float
+ *
+ * Convert an array of bytes received from the sensor in big-endian/MSB-first
+ * format to an float value in the correct system-endianness.
+ *
+ * @param bytes An array of at least four bytes (MSB first)
+ * @return The byte array represented as float
+ */
+float sensirion_common_bytes_to_float(const uint8_t* bytes);
+
+/**
+ * sensirion_common_uint32_t_to_bytes() - Convert an uint32_t to an array of
+ * bytes
+ *
+ * Convert an uint32_t value in system-endianness to big-endian/MBS-first
+ * format to send to the sensor.
+ *
+ * @param value Value to convert
+ * @param bytes An array of at least four bytes
+ */
+void sensirion_common_uint32_t_to_bytes(const uint32_t value, uint8_t* bytes);
+
+/**
+ * sensirion_common_uint16_t_to_bytes() - Convert an uint16_t to an array of
+ * bytes
+ *
+ * Convert an uint16_t value in system-endianness to big-endian/MBS-first
+ * format to send to the sensor.
+ *
+ * @param value Value to convert
+ * @param bytes An array of at least two bytes
+ */
+void sensirion_common_uint16_t_to_bytes(const uint16_t value, uint8_t* bytes);
+
+/**
+ * sensirion_common_int32_t_to_bytes() - Convert an int32_t to an array of bytes
+ *
+ * Convert an int32_t value in system-endianness to big-endian/MBS-first
+ * format to send to the sensor.
+ *
+ * @param value Value to convert
+ * @param bytes An array of at least four bytes
+ */
+void sensirion_common_int32_t_to_bytes(const int32_t value, uint8_t* bytes);
+
+/**
+ * sensirion_common_int16_t_to_bytes() - Convert an int16_t to an array of bytes
+ *
+ * Convert an int16_t value in system-endianness to big-endian/MBS-first
+ * format to send to the sensor.
+ *
+ * @param value Value to convert
+ * @param bytes An array of at least two bytes
+ */
+void sensirion_common_int16_t_to_bytes(const int16_t value, uint8_t* bytes);
+
+/**
+ * sensirion_common_float_to_bytes() - Convert an float to an array of bytes
+ *
+ * Convert an float value in system-endianness to big-endian/MBS-first
+ * format to send to the sensor.
+ *
+ * @param value Value to convert
+ * @param bytes An array of at least four bytes
+ */
+void sensirion_common_float_to_bytes(const float value, uint8_t* bytes);
+
+/**
+ * sensirion_common_copy_bytes() - Copy bytes from one array to the other.
+ *
+ * @param source Array of bytes to be copied.
+ * @param destination Array of bytes to be copied to.
+ * @param data_length Number of bytes to copy.
+ */
+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
+
+#endif /* SENSIRION_COMMON_H */
diff --git a/sensirion_config.h b/sensirion_config.h
new file mode 100644
index 0000000..1e88ceb
--- /dev/null
+++ b/sensirion_config.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019, 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_CONFIG_H
+#define SENSIRION_CONFIG_H
+
+/**
+ * If your platform does not provide the library stdlib.h you have to remove the
+ * include and define NULL yourself (see below).
+ */
+#include
+
+/**
+ * #ifndef NULL
+ * #define NULL ((void *)0)
+ * #endif
+ */
+
+/**
+ * If your platform does not provide the library stdint.h you have to
+ * define the integral types yourself (see below).
+ */
+#include
+
+/**
+ * Typedef section for types commonly defined in
+ * If your system does not provide stdint headers, please define them
+ * accordingly. Please make sure to define int64_t and uint64_t.
+ */
+/* typedef unsigned long long int uint64_t;
+ * typedef long long int int64_t;
+ * typedef long int32_t;
+ * typedef unsigned long uint32_t;
+ * typedef short int16_t;
+ * typedef unsigned short uint16_t;
+ * typedef char int8_t;
+ * typedef unsigned char uint8_t;
+ */
+
+#ifndef __cplusplus
+
+/**
+ * If your platform doesn't define the bool type we define it as int. Depending
+ * on your system update the definition below.
+ */
+#if __STDC_VERSION__ >= 199901L
+#include
+#else
+
+#ifndef bool
+#define bool int
+#define true 1
+#define false 0
+#endif /* bool */
+
+#endif /* __STDC_VERSION__ */
+
+#endif /* __cplusplus */
+
+#endif /* SENSIRION_CONFIG_H */
diff --git a/sensirion_i2c.c b/sensirion_i2c.c
new file mode 100644
index 0000000..5f709fc
--- /dev/null
+++ b/sensirion_i2c.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2018, 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.h"
+#include "sensirion_common.h"
+#include "sensirion_config.h"
+#include "sensirion_i2c_hal.h"
+
+uint8_t sensirion_i2c_generate_crc(const uint8_t* data, uint16_t count) {
+ uint16_t current_byte;
+ uint8_t crc = CRC8_INIT;
+ uint8_t crc_bit;
+
+ /* calculates 8-Bit checksum with given polynomial */
+ for (current_byte = 0; current_byte < count; ++current_byte) {
+ crc ^= (data[current_byte]);
+ for (crc_bit = 8; crc_bit > 0; --crc_bit) {
+ if (crc & 0x80)
+ crc = (crc << 1) ^ CRC8_POLYNOMIAL;
+ else
+ crc = (crc << 1);
+ }
+ }
+ return crc;
+}
+
+int8_t sensirion_i2c_check_crc(const uint8_t* data, uint16_t count,
+ uint8_t checksum) {
+ if (sensirion_i2c_generate_crc(data, count) != checksum)
+ return CRC_ERROR;
+ return NO_ERROR;
+}
+
+int16_t sensirion_i2c_general_call_reset(void) {
+ const uint8_t data = 0x06;
+ return sensirion_i2c_hal_write(0, &data, (uint16_t)sizeof(data));
+}
+
+uint16_t sensirion_i2c_fill_cmd_send_buf(uint8_t* buf, uint16_t cmd,
+ const uint16_t* args,
+ uint8_t num_args) {
+ uint8_t i;
+ uint16_t idx = 0;
+
+ buf[idx++] = (uint8_t)((cmd & 0xFF00) >> 8);
+ buf[idx++] = (uint8_t)((cmd & 0x00FF) >> 0);
+
+ for (i = 0; i < num_args; ++i) {
+ buf[idx++] = (uint8_t)((args[i] & 0xFF00) >> 8);
+ buf[idx++] = (uint8_t)((args[i] & 0x00FF) >> 0);
+
+ uint8_t crc = sensirion_i2c_generate_crc((uint8_t*)&buf[idx - 2],
+ SENSIRION_WORD_SIZE);
+ buf[idx++] = crc;
+ }
+ return idx;
+}
+
+int16_t sensirion_i2c_read_words_as_bytes(uint8_t address, uint8_t* data,
+ uint16_t num_words) {
+ int16_t ret;
+ uint16_t i, j;
+ uint16_t size = num_words * (SENSIRION_WORD_SIZE + CRC8_LEN);
+ uint16_t word_buf[SENSIRION_MAX_BUFFER_WORDS];
+ uint8_t* const buf8 = (uint8_t*)word_buf;
+
+ ret = sensirion_i2c_hal_read(address, buf8, size);
+ if (ret != NO_ERROR)
+ return ret;
+
+ /* check the CRC for each word */
+ for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + CRC8_LEN) {
+
+ ret = sensirion_i2c_check_crc(&buf8[i], SENSIRION_WORD_SIZE,
+ buf8[i + SENSIRION_WORD_SIZE]);
+ if (ret != NO_ERROR)
+ return ret;
+
+ data[j++] = buf8[i];
+ data[j++] = buf8[i + 1];
+ }
+
+ return NO_ERROR;
+}
+
+int16_t sensirion_i2c_read_words(uint8_t address, uint16_t* data_words,
+ uint16_t num_words) {
+ int16_t ret;
+ uint8_t i;
+
+ ret = sensirion_i2c_read_words_as_bytes(address, (uint8_t*)data_words,
+ num_words);
+ if (ret != NO_ERROR)
+ return ret;
+
+ for (i = 0; i < num_words; ++i) {
+ const uint8_t* word_bytes = (uint8_t*)&data_words[i];
+ data_words[i] = ((uint16_t)word_bytes[0] << 8) | word_bytes[1];
+ }
+
+ return NO_ERROR;
+}
+
+int16_t sensirion_i2c_write_cmd(uint8_t address, uint16_t command) {
+ uint8_t buf[SENSIRION_COMMAND_SIZE];
+
+ sensirion_i2c_fill_cmd_send_buf(buf, command, NULL, 0);
+ return sensirion_i2c_hal_write(address, buf, SENSIRION_COMMAND_SIZE);
+}
+
+int16_t sensirion_i2c_write_cmd_with_args(uint8_t address, uint16_t command,
+ const uint16_t* data_words,
+ uint16_t num_words) {
+ uint8_t buf[SENSIRION_MAX_BUFFER_WORDS];
+ uint16_t buf_size;
+
+ buf_size =
+ sensirion_i2c_fill_cmd_send_buf(buf, command, data_words, num_words);
+ return sensirion_i2c_hal_write(address, buf, buf_size);
+}
+
+int16_t sensirion_i2c_delayed_read_cmd(uint8_t address, uint16_t cmd,
+ uint32_t delay_us, uint16_t* data_words,
+ uint16_t num_words) {
+ int16_t ret;
+ uint8_t buf[SENSIRION_COMMAND_SIZE];
+
+ sensirion_i2c_fill_cmd_send_buf(buf, cmd, NULL, 0);
+ ret = sensirion_i2c_hal_write(address, buf, SENSIRION_COMMAND_SIZE);
+ if (ret != NO_ERROR)
+ return ret;
+
+ if (delay_us)
+ sensirion_i2c_hal_sleep_usec(delay_us);
+
+ return sensirion_i2c_read_words(address, data_words, num_words);
+}
+
+int16_t sensirion_i2c_read_cmd(uint8_t address, uint16_t cmd,
+ uint16_t* data_words, uint16_t num_words) {
+ return sensirion_i2c_delayed_read_cmd(address, cmd, 0, data_words,
+ num_words);
+}
+
+uint16_t sensirion_i2c_add_command_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_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);
+ buffer[offset++] = (uint8_t)((data & 0x00FF0000) >> 16);
+ buffer[offset] = sensirion_i2c_generate_crc(
+ &buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
+ offset++;
+ buffer[offset++] = (uint8_t)((data & 0x0000FF00) >> 8);
+ buffer[offset++] = (uint8_t)((data & 0x000000FF) >> 0);
+ buffer[offset] = sensirion_i2c_generate_crc(
+ &buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
+ offset++;
+
+ return offset;
+}
+
+uint16_t sensirion_i2c_add_int32_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ int32_t data) {
+ return sensirion_i2c_add_uint32_t_to_buffer(buffer, offset, (uint32_t)data);
+}
+
+uint16_t sensirion_i2c_add_uint16_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ uint16_t data) {
+ buffer[offset++] = (uint8_t)((data & 0xFF00) >> 8);
+ buffer[offset++] = (uint8_t)((data & 0x00FF) >> 0);
+ buffer[offset] = sensirion_i2c_generate_crc(
+ &buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
+ offset++;
+
+ return offset;
+}
+
+uint16_t sensirion_i2c_add_int16_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ int16_t data) {
+ return sensirion_i2c_add_uint16_t_to_buffer(buffer, offset, (uint16_t)data);
+}
+
+uint16_t sensirion_i2c_add_float_to_buffer(uint8_t* buffer, uint16_t offset,
+ float data) {
+ union {
+ uint32_t uint32_data;
+ float float_data;
+ } convert;
+
+ convert.float_data = data;
+
+ buffer[offset++] = (uint8_t)((convert.uint32_data & 0xFF000000) >> 24);
+ buffer[offset++] = (uint8_t)((convert.uint32_data & 0x00FF0000) >> 16);
+ buffer[offset] = sensirion_i2c_generate_crc(
+ &buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
+ offset++;
+ buffer[offset++] = (uint8_t)((convert.uint32_data & 0x0000FF00) >> 8);
+ buffer[offset++] = (uint8_t)((convert.uint32_data & 0x000000FF) >> 0);
+ buffer[offset] = sensirion_i2c_generate_crc(
+ &buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
+ offset++;
+
+ return offset;
+}
+
+uint16_t sensirion_i2c_add_bytes_to_buffer(uint8_t* buffer, uint16_t offset,
+ const uint8_t* data,
+ uint16_t data_length) {
+ uint16_t i;
+
+ if (data_length % SENSIRION_WORD_SIZE != 0) {
+ return BYTE_NUM_ERROR;
+ }
+
+ for (i = 0; i < data_length; i += 2) {
+ buffer[offset++] = data[i];
+ buffer[offset++] = data[i + 1];
+
+ buffer[offset] = sensirion_i2c_generate_crc(
+ &buffer[offset - SENSIRION_WORD_SIZE], SENSIRION_WORD_SIZE);
+ offset++;
+ }
+
+ return offset;
+}
+
+int16_t sensirion_i2c_write_data(uint8_t address, const uint8_t* data,
+ uint16_t data_length) {
+ return sensirion_i2c_hal_write(address, data, data_length);
+}
+
+int16_t sensirion_i2c_read_data_inplace(uint8_t address, uint8_t* buffer,
+ uint16_t expected_data_length) {
+ int16_t error;
+ uint16_t i, j;
+ uint16_t size = (expected_data_length / SENSIRION_WORD_SIZE) *
+ (SENSIRION_WORD_SIZE + CRC8_LEN);
+
+ if (expected_data_length % SENSIRION_WORD_SIZE != 0) {
+ return BYTE_NUM_ERROR;
+ }
+
+ error = sensirion_i2c_hal_read(address, buffer, size);
+ if (error) {
+ return error;
+ }
+
+ for (i = 0, j = 0; i < size; i += SENSIRION_WORD_SIZE + CRC8_LEN) {
+
+ error = sensirion_i2c_check_crc(&buffer[i], SENSIRION_WORD_SIZE,
+ buffer[i + SENSIRION_WORD_SIZE]);
+ if (error) {
+ return error;
+ }
+ buffer[j++] = buffer[i];
+ buffer[j++] = buffer[i + 1];
+ }
+
+ return NO_ERROR;
+}
diff --git a/sensirion_i2c.h b/sensirion_i2c.h
new file mode 100644
index 0000000..45ff5c3
--- /dev/null
+++ b/sensirion_i2c.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2018, 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_H
+#define SENSIRION_I2C_H
+
+#include "sensirion_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CRC_ERROR 1
+#define I2C_BUS_ERROR 2
+#define I2C_NACK_ERROR 3
+#define BYTE_NUM_ERROR 4
+
+#define CRC8_POLYNOMIAL 0x31
+#define CRC8_INIT 0xFF
+#define CRC8_LEN 1
+
+#define SENSIRION_COMMAND_SIZE 2
+#define SENSIRION_WORD_SIZE 2
+#define SENSIRION_NUM_WORDS(x) (sizeof(x) / SENSIRION_WORD_SIZE)
+#define SENSIRION_MAX_BUFFER_WORDS 32
+
+uint8_t sensirion_i2c_generate_crc(const uint8_t* data, uint16_t count);
+
+int8_t sensirion_i2c_check_crc(const uint8_t* data, uint16_t count,
+ uint8_t checksum);
+
+/**
+ * sensirion_i2c_general_call_reset() - Send a general call reset.
+ *
+ * @warning This will reset all attached I2C devices on the bus which support
+ * general call reset.
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_general_call_reset(void);
+
+/**
+ * sensirion_i2c_fill_cmd_send_buf() - create the i2c send buffer for a command
+ * and a set of argument words. The output buffer interleaves argument words
+ * with their checksums.
+ * @buf: The generated buffer to send over i2c. Then buffer length must
+ * be at least SENSIRION_COMMAND_LEN + num_args *
+ * (SENSIRION_WORD_SIZE + CRC8_LEN).
+ * @cmd: The i2c command to send. It already includes a checksum.
+ * @args: The arguments to the command. Can be NULL if none.
+ * @num_args: The number of word arguments in args.
+ *
+ * @return The number of bytes written to buf
+ */
+uint16_t sensirion_i2c_fill_cmd_send_buf(uint8_t* buf, uint16_t cmd,
+ const uint16_t* args,
+ uint8_t num_args);
+
+/**
+ * sensirion_i2c_read_words() - read data words from sensor
+ *
+ * @address: Sensor i2c address
+ * @data_words: Allocated buffer to store the read words.
+ * The buffer may also have been modified in case of an error.
+ * @num_words: Number of data words to read (without CRC bytes)
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_read_words(uint8_t address, uint16_t* data_words,
+ uint16_t num_words);
+
+/**
+ * sensirion_i2c_read_words_as_bytes() - read data words as byte-stream from
+ * sensor
+ *
+ * Read bytes without adjusting values to the uP's word-order.
+ *
+ * @address: Sensor i2c address
+ * @data: Allocated buffer to store the read bytes.
+ * The buffer may also have been modified in case of an error.
+ * @num_words: Number of data words(!) to read (without CRC bytes)
+ * Since only word-chunks can be read from the sensor the size
+ * is still specified in sensor-words (num_words = num_bytes *
+ * SENSIRION_WORD_SIZE)
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_read_words_as_bytes(uint8_t address, uint8_t* data,
+ uint16_t num_words);
+
+/**
+ * sensirion_i2c_write_cmd() - writes a command to the sensor
+ * @address: Sensor i2c address
+ * @command: Sensor command
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_write_cmd(uint8_t address, uint16_t command);
+
+/**
+ * sensirion_i2c_write_cmd_with_args() - writes a command with arguments to the
+ * sensor
+ * @address: Sensor i2c address
+ * @command: Sensor command
+ * @data: Argument buffer with words to send
+ * @num_words: Number of data words to send (without CRC bytes)
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_write_cmd_with_args(uint8_t address, uint16_t command,
+ const uint16_t* data_words,
+ uint16_t num_words);
+
+/**
+ * sensirion_i2c_delayed_read_cmd() - send a command, wait for the sensor to
+ * process and read data back
+ * @address: Sensor i2c address
+ * @cmd: Command
+ * @delay: Time in microseconds to delay sending the read request
+ * @data_words: Allocated buffer to store the read data
+ * @num_words: Data words to read (without CRC bytes)
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_delayed_read_cmd(uint8_t address, uint16_t cmd,
+ uint32_t delay_us, uint16_t* data_words,
+ uint16_t num_words);
+/**
+ * sensirion_i2c_read_cmd() - reads data words from the sensor after a command
+ * is issued
+ * @address: Sensor i2c address
+ * @cmd: Command
+ * @data_words: Allocated buffer to store the read data
+ * @num_words: Data words to read (without CRC bytes)
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_read_cmd(uint8_t address, uint16_t cmd,
+ uint16_t* data_words, uint16_t num_words);
+
+/**
+ * sensirion_i2c_add_command_to_buffer() - Add a command to the buffer at
+ * offset. Adds 2 bytes to the 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_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.
+ *
+ * @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 data uint32_t to be written into the buffer.
+ *
+ * @return Offset of next free byte in the buffer after writing the data.
+ */
+uint16_t sensirion_i2c_add_uint32_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ uint32_t data);
+
+/**
+ * sensirion_i2c_add_int32_t_to_buffer() - Add a int32_t to the buffer at
+ * offset. Adds 6 bytes to the 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 data int32_t to be written into the buffer.
+ *
+ * @return Offset of next free byte in the buffer after writing the data.
+ */
+uint16_t sensirion_i2c_add_int32_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ int32_t data);
+
+/**
+ * sensirion_i2c_add_uint16_t_to_buffer() - Add a uint16_t to the buffer at
+ * offset. Adds 3 bytes to the 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 data uint16_t to be written into the buffer.
+ *
+ * @return Offset of next free byte in the buffer after writing the data.
+ */
+uint16_t sensirion_i2c_add_uint16_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ uint16_t data);
+
+/**
+ * sensirion_i2c_add_int16_t_to_buffer() - Add a int16_t to the buffer at
+ * offset. Adds 3 bytes to the 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 data int16_t to be written into the buffer.
+ *
+ * @return Offset of next free byte in the buffer after writing the data.
+ */
+uint16_t sensirion_i2c_add_int16_t_to_buffer(uint8_t* buffer, uint16_t offset,
+ int16_t data);
+
+/**
+ * sensirion_i2c_add_float_to_buffer() - Add a float to the buffer at offset.
+ * Adds 6 bytes to the 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 data float to be written into the buffer.
+ *
+ * @return Offset of next free byte in the buffer after writing the data.
+ */
+uint16_t sensirion_i2c_add_float_to_buffer(uint8_t* buffer, uint16_t offset,
+ float data);
+
+/**
+ * sensirion_i2c_add_bytes_to_buffer() - Add a byte array to the buffer at
+ * offset.
+ *
+ * @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 data Pointer to data to be written into the buffer.
+ * @param data_length Number of bytes to be written into the buffer. Needs to
+ * be a multiple of SENSIRION_WORD_SIZE otherwise the
+ * function returns BYTE_NUM_ERROR.
+ *
+ * @return Offset of next free byte in the buffer after writing the
+ * data.
+ */
+uint16_t sensirion_i2c_add_bytes_to_buffer(uint8_t* buffer, uint16_t offset,
+ const uint8_t* data,
+ uint16_t data_length);
+
+/**
+ * sensirion_i2c_write_data() - Writes data to the Sensor.
+ *
+ * @note This is just a wrapper for sensirion_i2c_hal_write() to
+ * not need to include the HAL in the drivers.
+ *
+ * @param address I2C address to write to.
+ * @param data Pointer to the buffer containing the data to write.
+ * @param data_length Number of bytes to send to the Sensor.
+ *
+ * @return NO_ERROR on success, error code otherwise
+ */
+int16_t sensirion_i2c_write_data(uint8_t address, const uint8_t* data,
+ uint16_t data_length);
+
+/**
+ * sensirion_i2c_read_data_inplace() - Reads data from the Sensor.
+ *
+ * @param address Sensor I2C address
+ * @param buffer Allocated buffer to store data as bytes. Needs
+ * to be big enough to store the data including
+ * CRC. Twice the size of data should always
+ * suffice.
+ * @param expected_data_length Number of bytes to read (without CRC). Needs
+ * to be a multiple of SENSIRION_WORD_SIZE,
+ * otherwise the function returns BYTE_NUM_ERROR.
+ *
+ * @return NO_ERROR on success, an error code otherwise
+ */
+int16_t sensirion_i2c_read_data_inplace(uint8_t address, uint8_t* buffer,
+ uint16_t expected_data_length);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SENSIRION_I2C_H */
diff --git a/sensirion_i2c_hal.c b/sensirion_i2c_hal.c
new file mode 100644
index 0000000..d368cf2
--- /dev/null
+++ b/sensirion_i2c_hal.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2018, 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"
+
+/*
+ * 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;
+}
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void) {
+ /* TODO:IMPLEMENT */
+}
+
+/**
+ * Release all resources initialized by sensirion_i2c_hal_init().
+ */
+void sensirion_i2c_hal_free(void) {
+ /* TODO:IMPLEMENT or leave empty if no resources need to be freed */
+}
+
+/**
+ * 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, uint8_t count) {
+ /* TODO:IMPLEMENT */
+ return NOT_IMPLEMENTED_ERROR;
+}
+
+/**
+ * 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,
+ uint8_t count) {
+ /* TODO:IMPLEMENT */
+ return NOT_IMPLEMENTED_ERROR;
+}
+
+/**
+ * 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) {
+ /* TODO:IMPLEMENT */
+}
diff --git a/sensirion_i2c_hal.h b/sensirion_i2c_hal.h
new file mode 100644
index 0000000..d6267b8
--- /dev/null
+++ b/sensirion_i2c_hal.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018, 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_HAL_H
+#define SENSIRION_I2C_HAL_H
+
+#include "sensirion_config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * 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);
+
+/**
+ * Initialize all hard- and software components that are needed for the I2C
+ * communication.
+ */
+void sensirion_i2c_hal_init(void);
+
+/**
+ * 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, uint8_t count);
+
+/**
+ * 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,
+ uint8_t count);
+
+/**
+ * Sleep for a given number of microseconds. The function should delay the
+ * execution approximately, but no less than, the given time.
+ *
+ * When using hardware i2c:
+ * Despite the unit, a <10 millisecond precision is sufficient.
+ *
+ * When using software i2c:
+ * The precision needed depends on the desired i2c frequency, i.e. should be
+ * exact to about half a clock cycle (defined in
+ * `SENSIRION_I2C_CLOCK_PERIOD_USEC` in `sensirion_sw_i2c_gpio.h`).
+ *
+ * Example with 400kHz requires a precision of 1 / (2 * 400kHz) == 1.25usec.
+ *
+ * @param useconds the sleep time in microseconds
+ */
+void sensirion_i2c_hal_sleep_usec(uint32_t useconds);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* SENSIRION_I2C_HAL_H */
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..5467dcc
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,53 @@
+
+# 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
+
+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
+
+sen66_sources = $(driver_dir)/sen66_i2c.h $(driver_dir)/sen66_i2c.c
+
+CXXFLAGS ?= $(CFLAGS) -fsanitize=address -I$(mux_dir) -I$(driver_dir) -I$(sw_i2c_dir) ${macros}
+ifdef CI
+ CXXFLAGS += -Werror
+endif
+LDFLAGS ?= -lasan -lstdc++ -lCppUTest -lCppUTestExt
+
+.PHONY: clean test
+
+
+sen66_test_binaries := sen66_test_hw_i2c sen66_test_sw_i2c
+
+all: $(sen66_test_binaries)
+
+sen66_test_hw_i2c: sen66_i2c_test.cpp $(sen66_sources) $(sensirion_test_sources) $(i2c_sources) $(hw_i2c_impl_src) $(common_sources)
+ $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
+
+sen66_test_sw_i2c: sen66_i2c_test.cpp $(sen66_sources) $(sensirion_test_sources) $(i2c_sources) $(sw_i2c_impl_src) $(common_sources)
+ $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
+
+test: $(sen66_test_binaries)
+ set -ex; for test in $(sen66_test_binaries); do echo $${test}; ./$${test}; echo; done;
+
+clean:
+ $(RM) $(sen66_test_binaries)
diff --git a/tests/sen66_i2c_test.cpp b/tests/sen66_i2c_test.cpp
new file mode 100644
index 0000000..5399ef3
--- /dev/null
+++ b/tests/sen66_i2c_test.cpp
@@ -0,0 +1,126 @@
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED
+ *
+ * Generator: sensirion-driver-generator 1.0.1
+ * Product: sen66
+ * Model-Version: 1.2.0
+ */
+
+#include "sen66_i2c.h"
+#include "sensirion_common.h"
+#include "sensirion_i2c.h"
+#include "sensirion_i2c_hal.h"
+#include "sensirion_test_setup.h"
+#include
+#include
+
+#define sensirion_hal_sleep_us sensirion_i2c_hal_sleep_usec
+
+void print_byte_array(uint8_t* array, uint16_t len) {
+ uint16_t i = 0;
+ printf("0x");
+ for (; i < len; i++) {
+ printf("%02x", array[i]);
+ }
+}
+
+TEST_GROUP (SEN66_Tests) {
+ void setup() {
+ sen66_init(0x6B);
+ }
+};
+
+TEST (SEN66_Tests, test_perform_forced_co2_recalibration1) {
+ int16_t local_error = 0;
+ uint16_t correction = 0;
+ local_error = sen66_perform_forced_co2_recalibration(600, &correction);
+ CHECK_EQUAL_ZERO_TEXT(local_error, "perform_forced_co2_recalibration");
+ printf("correction: %u\n", correction);
+}
+
+TEST (SEN66_Tests, test_get_product_name1) {
+ int16_t local_error = 0;
+ uint8_t product_name[32] = {0};
+ local_error = sen66_get_product_name(product_name, 32);
+ CHECK_EQUAL_ZERO_TEXT(local_error, "get_product_name");
+ printf("product_name: ");
+ print_byte_array(product_name, 32);
+ printf("\n");
+}
+
+TEST (SEN66_Tests, test_get_serial_number1) {
+ int16_t local_error = 0;
+ uint8_t serial_number[32] = {0};
+ local_error = sen66_get_serial_number(serial_number, 32);
+ CHECK_EQUAL_ZERO_TEXT(local_error, "get_serial_number");
+ printf("serial_number: ");
+ print_byte_array(serial_number, 32);
+ printf("\n");
+}
+
+TEST (SEN66_Tests, test_get_version1) {
+ int16_t local_error = 0;
+ uint8_t firmware_major = 0;
+ uint8_t firmware_minor = 0;
+ bool firmware_debug = false;
+ uint8_t hardware_major = 0;
+ uint8_t hardware_minor = 0;
+ uint8_t protocol_major = 0;
+ uint8_t protocol_minor = 0;
+ uint8_t padding = 0;
+ local_error = sen66_get_version(
+ &firmware_major, &firmware_minor, &firmware_debug, &hardware_major,
+ &hardware_minor, &protocol_major, &protocol_minor, &padding);
+ CHECK_EQUAL_ZERO_TEXT(local_error, "get_version");
+ printf("firmware_major: %u ", firmware_major);
+ printf("firmware_minor: %u ", firmware_minor);
+ printf("firmware_debug: %d ", firmware_debug);
+ printf("hardware_major: %u ", hardware_major);
+ printf("hardware_minor: %u ", hardware_minor);
+ printf("protocol_major: %u ", protocol_major);
+ printf("protocol_minor: %u ", protocol_minor);
+ printf("padding: %u\n", padding);
+}
+
+TEST (SEN66_Tests, test_device_reset1) {
+ int16_t local_error = 0;
+ local_error = sen66_device_reset();
+ CHECK_EQUAL_ZERO_TEXT(local_error, "device_reset");
+}
+
+TEST (SEN66_Tests, test_start_continuous_measurement1) {
+ int16_t local_error = 0;
+ uint16_t mass_concentration_pm1p0 = 0;
+ uint16_t mass_concentration_pm2p5 = 0;
+ uint16_t mass_concentration_pm4p0 = 0;
+ uint16_t mass_concentration_pm10p0 = 0;
+ int16_t ambient_humidity = 0;
+ int16_t ambient_temperature = 0;
+ int16_t voc_index = 0;
+ int16_t nox_index = 0;
+ uint16_t co2 = 0;
+ uint8_t padding = 0;
+ bool data_ready = false;
+ local_error = sen66_start_continuous_measurement();
+ CHECK_EQUAL_ZERO_TEXT(local_error, "start_continuous_measurement");
+ local_error = sen66_read_measured_values_as_integers(
+ &mass_concentration_pm1p0, &mass_concentration_pm2p5,
+ &mass_concentration_pm4p0, &mass_concentration_pm10p0,
+ &ambient_humidity, &ambient_temperature, &voc_index, &nox_index, &co2);
+ CHECK_EQUAL_ZERO_TEXT(local_error, "read_measured_values_as_integers");
+ printf("mass_concentration_pm1p0: %u ", mass_concentration_pm1p0);
+ printf("mass_concentration_pm2p5: %u ", mass_concentration_pm2p5);
+ printf("mass_concentration_pm4p0: %u ", mass_concentration_pm4p0);
+ printf("mass_concentration_pm10p0: %u ", mass_concentration_pm10p0);
+ printf("ambient_humidity: %i ", ambient_humidity);
+ printf("ambient_temperature: %i ", ambient_temperature);
+ printf("voc_index: %i ", voc_index);
+ printf("nox_index: %i ", nox_index);
+ printf("co2: %u\n", co2);
+ local_error = sen66_get_data_ready(&padding, &data_ready);
+ CHECK_EQUAL_ZERO_TEXT(local_error, "get_data_ready");
+ printf("padding: %u ", padding);
+ printf("data_ready: %d\n", data_ready);
+ local_error = sen66_stop_measurement();
+ CHECK_EQUAL_ZERO_TEXT(local_error, "stop_measurement");
+}
diff --git a/tests/sensirion_test_setup.cpp b/tests/sensirion_test_setup.cpp
new file mode 100644
index 0000000..e47f022
--- /dev/null
+++ b/tests/sensirion_test_setup.cpp
@@ -0,0 +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) {
+ 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;
+}
diff --git a/tests/sensirion_test_setup.h b/tests/sensirion_test_setup.h
new file mode 100644
index 0000000..448eccc
--- /dev/null
+++ b/tests/sensirion_test_setup.h
@@ -0,0 +1,20 @@
+#ifndef SENSIRION_TEST_SETUP_H
+#define SENSIRION_TEST_SETUP_H
+
+#include
+
+#include "CppUTest/TestHarness.h"
+
+// provide macros which will only be available in a future cpputest release
+// See https://github.com/cpputest/cpputest/pull/1423
+#ifndef CHECK_EQUAL_ZERO
+#define CHECK_EQUAL_ZERO(actual) CHECK_EQUAL(0, (actual))
+#endif
+#ifndef CHECK_EQUAL_ZERO_TEXT
+#define CHECK_EQUAL_ZERO_TEXT(actual, text) \
+ CHECK_EQUAL_TEXT(0, (actual), (text))
+#endif
+
+int main(int argc, char** argv);
+
+#endif /* SENSIRION_TEST_SETUP_H */