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/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/.github/workflows/raspberry_quality_check.yml b/.github/workflows/raspberry_quality_check.yml
new file mode 100644
index 0000000..95e97ee
--- /dev/null
+++ b/.github/workflows/raspberry_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/.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..45d1857
--- /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/raspberry-pi-i2c-sen66/compare/0.1.0...HEAD
+[0.1.0]: https://github.com/Sensirion/raspberry-pi-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..997df08
--- /dev/null
+++ b/README.md
@@ -0,0 +1,167 @@
+# Sensirion Raspberry Pi I²C SEN66 Driver
+
+The repository provides a driver for setting up a SEN66 sensor to run on a Raspberry Pi over I²C.
+
+
+
+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**.
+
+
+
+## Connect the sensor
+
+Your sensor has 4 different connectors: VDD, GND, SDA, SCL.
+Use the following pins to connect your SEN66:
+
+| *SEN66* | *Cable Color* | *Raspberry Pi* |
+| :----------------: | -------------- | ------------------ |
+| VDD | red | Pin 1
+| GND | black | Pin 6
+| SDA | green | Pin 3
+| SCL | yellow | Pin 5
+
+
+
+
+
+### Detailed sensor pinout
+
+
+
+| *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 |
+
+
+
+## Quick start example
+
+- [Install the Raspberry Pi OS on to your Raspberry Pi](https://projects.raspberrypi.org/en/projects/raspberry-pi-setting-up)
+- [Enable the I²C interface in the raspi-config](https://www.raspberrypi.org/documentation/configuration/raspi-config.md)
+- Download the SEN66 driver from [Github](https://github.com/Sensirion/raspberry-pi-i2c-sen66) and extract the `.zip` on your Raspberry Pi
+- Connect the SEN66 sensor as explained in the [section above](#connect-the-sensor)
+
+- Compile the driver
+ 1. Open a [terminal](https://projects.raspberrypi.org/en/projects/raspberry-pi-using/8)
+ 2. Navigate to the driver directory. E.g. `cd ~/raspberry-pi-i2c-sen66`
+ 3. Navigate to the subdirectory example-usage.
+ 4. Run the `make` command to compile the driver
+
+ Output:
+ ```
+ rm -f sen66_i2c_example_usage
+ cc -Os -Wall -fstrict-aliasing -Wstrict-aliasing=1 -Wsign-conversion -fPIC -I. -o sen66_i2c_example_usage sen66_i2c.h sen66_i2c.c sensirion_i2c_hal.h sensirion_i2c.h sensirion_i2c.c \
+ sensirion_i2c_hal.c sensirion_config.h sensirion_common.h sensirion_common.c sen66_i2c_example_usage.c
+ ```
+- Test your connected sensor
+ - Run `./sen66_i2c_example_usage` in the same directory you used to
+ compile the driver. You should see the measurement values in the console.
+
+## Troubleshooting
+
+### Building driver failed
+
+If the execution of `make` in the compilation step 3 fails with something like
+
+```bash
+ make: command not found
+```
+
+your RaspberryPi likely does not have the build tools installed. Proceed as follows:
+
+```
+$ sudo apt-get update
+$ sudo apt-get upgrade
+$ sudo apt-get install build-essential
+```
+
+
+### Initialization failed
+
+If you run `./sen66_i2c_example_usage` but do not get sensor readings but something like this instead
+
+```
+Error executing device_reset(): -1
+Error executing get_serial_number(): -1
+Error executing start_continuous_measurement(): -1
+...
+```
+then go through the below troubleshooting steps.
+
+
+- Ensure that you connected the sensor correctly: All cables are fully
+ plugged in and connected to the correct pin.
+- Ensure that I²C is enabled on the Raspberry Pi. For this redo the steps on
+ "Enable the I²C interface in the raspi-config" in the guide above.
+- Ensure that your user account has read and write access to the I²C device.
+ If it only works with user root (`sudo ./sen66_i2c_example_usage`), it's
+ typically due to wrong permission settings. See the next chapter how to solve this.
+
+### Missing I²C permissions
+
+If your user is missing access to the I²C interface you should first verfiy
+the user belongs to the `i2c` group.
+
+```
+$ groups
+users input some other groups etc
+```
+If `i2c` is missing in the list add the user and restart the Raspberry Pi.
+
+```
+$ sudo adduser your-user i2c
+Adding user `your-user' to group `i2c' ...
+Adding user your-user to group i2c
+Done.
+$ sudo reboot
+```
+
+If that did not help you can make globally accessible hardware interfaces
+with a udev rule. Only do this if everything else failed and you are
+reasoably confident you are the only one having access to your Pi.
+
+Go into the `/etc/udev/rules.d` folder and add a new file named
+`local.rules`.
+```
+$ cd /etc/udev/rules.d/
+$ sudo touch local.rules
+```
+Then add a single line `ACTION=="add", KERNEL=="i2c-[0-1]*", MODE="0666"`
+to the file with your favorite editor.
+```
+$ sudo vi local.rules
+```
+
+## 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..922e729
--- /dev/null
+++ b/example-usage/sen66_i2c_example_usage.c
@@ -0,0 +1,115 @@
+/*
+ * 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;
+ }
+ float mass_concentration_pm1p0 = 0.0;
+ float mass_concentration_pm2p5 = 0.0;
+ float mass_concentration_pm4p0 = 0.0;
+ float mass_concentration_pm10p0 = 0.0;
+ float humidity = 0.0;
+ float temperature = 0.0;
+ float voc_index = 0.0;
+ float nox_index = 0.0;
+ float co2 = 0.0;
+ uint16_t repetition = 0;
+ for (repetition = 0; repetition < 50; repetition++) {
+ sensirion_hal_sleep_us(1000000);
+ error = sen66_read_measured_values(
+ &mass_concentration_pm1p0, &mass_concentration_pm2p5,
+ &mass_concentration_pm4p0, &mass_concentration_pm10p0, &humidity,
+ &temperature, &voc_index, &nox_index, &co2);
+ if (error != NO_ERROR) {
+ printf("error executing read_measured_values(): %i\n", error);
+ continue;
+ }
+ printf("mass_concentration_pm1p0: %.2f ", mass_concentration_pm1p0);
+ printf("mass_concentration_pm2p5: %.2f ", mass_concentration_pm2p5);
+ printf("mass_concentration_pm4p0: %.2f ", mass_concentration_pm4p0);
+ printf("mass_concentration_pm10p0: %.2f ", mass_concentration_pm10p0);
+ printf("humidity: %.2f ", humidity);
+ printf("temperature: %.2f ", temperature);
+ printf("voc_index: %.2f ", voc_index);
+ printf("nox_index: %.2f ", nox_index);
+ printf("co2: %.2f\n", co2);
+ }
+
+ error = sen66_stop_measurement();
+ if (error != NO_ERROR) {
+ return error;
+ }
+ return NO_ERROR;
+}
diff --git a/images/raspi-i2c-pinout-3.3V.png b/images/raspi-i2c-pinout-3.3V.png
new file mode 100644
index 0000000..c4cef1e
Binary files /dev/null and b/images/raspi-i2c-pinout-3.3V.png differ
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/sen66_i2c.c b/sen66_i2c.c
new file mode 100644
index 0000000..8ef547d
--- /dev/null
+++ b/sen66_i2c.c
@@ -0,0 +1,351 @@
+/*
+ * 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;
+}
+
+float sen66_signal_mass_concentration_pm1p0(
+ uint16_t mass_concentration_pm1p0_raw) {
+ float mass_concentration_pm1p0 = 0.0;
+ mass_concentration_pm1p0 = mass_concentration_pm1p0_raw / 10.0;
+ return mass_concentration_pm1p0;
+}
+
+float sen66_signal_mass_concentration_pm2p5(
+ uint16_t mass_concentration_pm2p5_raw) {
+ float mass_concentration_pm2p5 = 0.0;
+ mass_concentration_pm2p5 = mass_concentration_pm2p5_raw / 10.0;
+ return mass_concentration_pm2p5;
+}
+
+float sen66_signal_mass_concentration_pm4p0(
+ uint16_t mass_concentration_pm4p0_raw) {
+ float mass_concentration_pm4p0 = 0.0;
+ mass_concentration_pm4p0 = mass_concentration_pm4p0_raw / 10.0;
+ return mass_concentration_pm4p0;
+}
+
+float sen66_signal_mass_concentration_pm10p0(
+ uint16_t mass_concentration_pm10p0_raw) {
+ float mass_concentration_pm10p0 = 0.0;
+ mass_concentration_pm10p0 = mass_concentration_pm10p0_raw / 10.0;
+ return mass_concentration_pm10p0;
+}
+
+float sen66_signal_temperature(int16_t temperature_raw) {
+ float temperature = 0.0;
+ temperature = temperature_raw / 200.0;
+ return temperature;
+}
+
+float sen66_signal_humidity(int16_t humidity_raw) {
+ float humidity = 0.0;
+ humidity = humidity_raw / 100.0;
+ return humidity;
+}
+
+float sen66_signal_voc_index(int16_t voc_index_raw) {
+ float voc_index = 0.0;
+ voc_index = voc_index_raw / 10.0;
+ return voc_index;
+}
+
+float sen66_signal_nox_index(int16_t nox_index_raw) {
+ float nox_index = 0.0;
+ nox_index = nox_index_raw / 10.0;
+ return nox_index;
+}
+
+float sen66_signal_co2(uint16_t co2_raw) {
+ float co2 = 0.0;
+ co2 = co2_raw / 1.0;
+ return co2;
+}
+
+int16_t sen66_read_measured_values(float* mass_concentration_pm1p0,
+ float* mass_concentration_pm2p5,
+ float* mass_concentration_pm4p0,
+ float* mass_concentration_pm10p0,
+ float* humidity, float* temperature,
+ float* voc_index, float* nox_index,
+ float* co2) {
+ uint16_t mass_concentration_pm1p0_raw = 0;
+ uint16_t mass_concentration_pm2p5_raw = 0;
+ uint16_t mass_concentration_pm4p0_raw = 0;
+ uint16_t mass_concentration_pm10p0_raw = 0;
+ int16_t humidity_raw = 0;
+ int16_t temperature_raw = 0;
+ int16_t voc_index_raw = 0;
+ int16_t nox_index_raw = 0;
+ uint16_t co2_raw = 0;
+ int16_t local_error = 0;
+ local_error = sen66_read_measured_values_as_integers(
+ &mass_concentration_pm1p0_raw, &mass_concentration_pm2p5_raw,
+ &mass_concentration_pm4p0_raw, &mass_concentration_pm10p0_raw,
+ &humidity_raw, &temperature_raw, &voc_index_raw, &nox_index_raw,
+ &co2_raw);
+ if (local_error != NO_ERROR) {
+ return local_error;
+ }
+ *mass_concentration_pm1p0 =
+ sen66_signal_mass_concentration_pm1p0(mass_concentration_pm1p0_raw);
+ *mass_concentration_pm2p5 =
+ sen66_signal_mass_concentration_pm2p5(mass_concentration_pm2p5_raw);
+ *mass_concentration_pm4p0 =
+ sen66_signal_mass_concentration_pm4p0(mass_concentration_pm4p0_raw);
+ *mass_concentration_pm10p0 =
+ sen66_signal_mass_concentration_pm10p0(mass_concentration_pm10p0_raw);
+ *humidity = sen66_signal_humidity(humidity_raw);
+ *temperature = sen66_signal_temperature(temperature_raw);
+ *voc_index = sen66_signal_voc_index(voc_index_raw);
+ *nox_index = sen66_signal_nox_index(nox_index_raw);
+ *co2 = sen66_signal_co2(co2_raw);
+ return local_error;
+}
+
+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..e07b047
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..74a7f57
--- /dev/null
+++ b/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/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 */