diff --git a/BUILDS.md b/BUILDS.md
index 12802b096..e5dadfaa6 100644
--- a/BUILDS.md
+++ b/BUILDS.md
@@ -118,6 +118,7 @@ Note: `minimal` variant is not listed as it shouldn't be used outside of the [up
| USE_MGS | - | - / x | - | x | - | - |
| USE_SGP30 | - | - / x | - | x | - | - |
| USE_SGP40 | - | - / x | - | x | - | - |
+| USE_SEN5X | - | - / x | - | x | - | - |
| USE_SI1145 | - | - / - | - | - | - | - |
| USE_LM75AD | - | - / x | - | x | - | - |
| USE_APDS9960 | - | - / - | - | - | - | - |
diff --git a/CODE_OWNERS.md b/CODE_OWNERS.md
index 37851c86d..9e5b35411 100644
--- a/CODE_OWNERS.md
+++ b/CODE_OWNERS.md
@@ -198,6 +198,7 @@ In addition to @arendst the following code is mainly owned by:
| xsns_100_ina3221 | @barbudor
| xsns_101_hmc5883l | Andreas Achtzehn
| xsns_102_ld2410 | @arendst
+| xsns_103_sen5x | @tyeth
| |
| Libraries |
| |
diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 171b04fd7..28818c562 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -111,3 +111,4 @@ Index | Define | Driver | Device | Address(es) | Description
73 | USE_HMC5883L | xsns_101 | HMC5883L | 0x1E | 3-channels Magnetic Field Sensor
74 | USE_DISPLAY_TM1650 | xdsp_20 | TM1650 | 0x24 - 0x27, 0x34 - 0x37 | Four-digit seven-segment LED controller
75 | USE_PCA9632 | xdrv_64 | PCA9632 | 0x60 | 4-channel 4-bit pwm driver
+ 76 | USE_SEN5X | xsns_103 | SEN5X | 0x69 | Gas (VOC/NOx index) and air quality (PPM <1,<2.5,<4,<10)
diff --git a/lib/lib_i2c/Sensirion_Core/.clang-format b/lib/lib_i2c/Sensirion_Core/.clang-format
new file mode 100644
index 000000000..047f2adf1
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/.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/lib/lib_i2c/Sensirion_Core/CHANGELOG.rst b/lib/lib_i2c/Sensirion_Core/CHANGELOG.rst
new file mode 100644
index 000000000..8d7aa66ff
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/CHANGELOG.rst
@@ -0,0 +1,161 @@
+Changelog
+=========
+
+All notable changes to this project will be documented in this file.
+
+The format is based on `Keep a Changelog `_
+and this project adheres to `Semantic Versioning `_.
+
+`Unreleased`_
+-------------
+
+
+`0.6.0`_ 2022-06-22
+-------------------
+
+- Fix compiler warnings in SensirionErrors.cpp
+- Allow drivers to choose CRC function
+
+`0.5.3`_ 2021-10-19
+-------------------
+
+- Add support for sensor specific errors
+- Update keywords.txt
+
+
+`0.5.2`_ 2021-08-03
+-------------------
+
+Fixed
+.....
+
+- Fix CRC insertion in ``SensirionI2CTxFrame`` when more then one parameter
+ is sent to the sensor.
+
+`0.5.1`_ 2021-07-08
+-------------------
+
+Changed
+.......
+
+- Adjusted deprecation warnings
+
+`0.5.0`_ 2021-07-07
+-------------------
+
+Added
+.....
+
+- Enable SensirionTxFrame to incorporate Uint8 and Uint16 commands
+
+
+`0.4.3`_ 2021-02-12
+-------------------
+
+Added
+.....
+
+- Added ``const`` modifier to functions which process MOSI array data.
+
+`0.4.2`_ 2021-01-29
+-------------------
+
+Changed
+.......
+
+- Renamed the library header from ``SensirionCoreArduinoLibrary.h`` to ``SensirionCore.h``.
+ We keep the old header for legacy support.
+
+`0.4.1`_ 2021-01-28
+-------------------
+
+Fixed
+.....
+
+- Properly handle I2C write errors
+
+
+`0.4.0`_ 2021-01-20
+-------------------
+
+Added
+.....
+
+- Documentation for all functions.
+
+Breaking
+........
+
+- Change interface of ``errorToString()`` function to include length of the
+ provided buffer.
+
+Removed
+.......
+
+- Removed ``reset()`` function from ``SensirionI2CTxFrame`` since the
+ functionality is not needed.
+
+
+`0.3.0`_ 2021-01-13
+-------------------
+
+Added
+.....
+
+- Core implementation for I2C communication. This includes a RX and TX frame
+ and a I2C communication class.
+
+Changed
+.......
+
+- SHDLC and I2C RX frame inherit from a RX frame base class.
+- ESP8266 test board from esp8266:esp8266:arduino to esp8266:esp8266:generic.
+- Sorted errors into general, SHDLC and I2C errors.
+- Replace C style casts with ``static_cast``.
+
+
+`0.2.0`_ 2021-01-11
+-------------------
+
+Added
+.....
+
+- Explanation what SHDLC is in README.
+- ``SensirionErrors.h`` to ``SensirionCoreArduinoLibrary.h``.
+- ``sendAndReceiveFrame()`` function to ``SensirionShdlcCommunication``. This
+ function combines ``sendFrame()`` and ``receiveFrame()`` into one function and
+ adds additional error checking.
+
+Changed
+.......
+
+- Rename DeviceError to ExecutionError.
+- Move check for execution error after the whole frame is read and checksum is
+ checked. This prevents that a wrong checksum can't be displayed as an
+ execution error.
+
+Removed
+.......
+
+- ``reset()`` function from ``SensirionShdlcTxFrame`` and ``SensirionShdlcRxFrame``,
+ since one can just create a new frame object which has the same effect.
+
+`0.1.0`_ 2021-01-07
+-------------------
+
+- Initial release
+
+
+.. _Unreleased: https://github.com/Sensirion/arduino-core/compare/0.6.0...main
+.. _0.6.0: https://github.com/Sensirion/arduino-core/compare/0.6.0...0.5.3
+.. _0.5.3: https://github.com/Sensirion/arduino-core/compare/0.5.2...0.5.3
+.. _0.5.2: https://github.com/Sensirion/arduino-core/compare/0.5.1...0.5.2
+.. _0.5.1: https://github.com/Sensirion/arduino-core/compare/0.5.0...0.5.1
+.. _0.5.0: https://github.com/Sensirion/arduino-core/compare/0.4.3...0.5.0
+.. _0.4.3: https://github.com/Sensirion/arduino-core/compare/0.4.2...0.4.3
+.. _0.4.2: https://github.com/Sensirion/arduino-core/compare/0.4.1...0.4.2
+.. _0.4.1: https://github.com/Sensirion/arduino-core/compare/0.4.0...0.4.1
+.. _0.4.0: https://github.com/Sensirion/arduino-core/compare/0.3.0...0.4.0
+.. _0.3.0: https://github.com/Sensirion/arduino-core/compare/0.2.0...0.3.0
+.. _0.2.0: https://github.com/Sensirion/arduino-core/compare/0.1.0...0.2.0
+.. _0.1.0: https://github.com/Sensirion/arduino-core/releases/tag/0.1.0
diff --git a/lib/lib_i2c/Sensirion_Core/LICENSE b/lib/lib_i2c/Sensirion_Core/LICENSE
new file mode 100644
index 000000000..fad1acd6e
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2020, 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/lib/lib_i2c/Sensirion_Core/README.md b/lib/lib_i2c/Sensirion_Core/README.md
new file mode 100644
index 000000000..1a8cf2c71
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/README.md
@@ -0,0 +1,139 @@
+
+# Sensirion Arduino Core Library
+
+This library provides SHDLC and I2C protocol implementations for Sensirion
+sensors. There shouldn't be a reason to use it directly, but is required by the
+sensor driver libraries provided here:
+
+- [SCD4x](https://github.com/Sensirion/arduino-i2c-scd4x)
+- [SVM40-I2C](https://github.com/Sensirion/arduino-i2c-svm40)
+- [SVM40-UART](https://github.com/Sensirion/arduino-uart-svm40)
+- [SFA3x-I2C](https://github.com/Sensirion/arduino-i2c-sfa3x)
+- [SFA3x-UART](https://github.com/Sensirion/arduino-uart-sfa3x)
+
+# More Drivers
+
+Not looking for Arduino drivers? Check out our other drivers here:
+
+- [Embedded](https://github.com/Sensirion/info#repositories)
+- [Python](https://github.com/Sensirion/info#python-drivers)
+
+# Usage
+
+## SHDLC
+
+SHDLC (Sensirion High-Level Data Link Control) is a byte-oriented master-slave
+communication protocol based on [ISO
+HDLC](https://en.wikipedia.org/wiki/High-Level_Data_Link_Control). It is used
+to control some of Sensirion’s devices (for example mass flow controllers). The
+detailed protocol documentation is not publicly available (yet). If you need
+it, please contact our [customer
+support](https://www.sensirion.com/en/about-us/contact/).
+
+This library provides the following classes for communication with Sensirion
+Sensors using the SHDLC protocol.
+- `SensirionShdlcTxFrame`
+- `SensirionShdlcRxFrame`
+- `SensirionShdlcCommunication`
+
+### Example Usage
+First initialize an instance of `SensirionShdlcTxFrame` and
+`SensirionShdlcRxFrame` with a properly sized buffer. A good worst case
+estimation for the buffer size is `2 * (n+6)` where `n` is the number of bytes
+you want to send. After that you can build your frame by first calling
+`begin()`. Information about the correct COMMAND and ADDRESS can be found on
+the data sheet of your sensor. Then you can add data to the frame by using
+different add member functions. See the code below for examples. After adding
+your data finish the frame by calling `finish()`.
+
+To send this frame to the sensor you first need to initialize the correct
+Stream object (Serial,UART,...) to talk to your sensor. Don't forget to also
+call the `.begin()` function with the right configuration. Then call the static
+function `sendAndReceiveFrame()` from `SensirionShdlcCommunication` as shown
+below. You need to replace `STREAMOBJECT` with the initialized Stream object of
+your choice. Additionally you need to provide a timeout for to receive data
+back, consult the data sheet of your sensor for information on the best timeout
+value.
+
+You can decode the frame by using the different get members to convert the
+received data to desired data types.
+
+All functions return a error code if an error occurs during execution and zero
+otherwise.
+
+```cpp
+uint8_t txBuffer[256];
+uint8_t rxBuffer[256];
+
+SensirionShdlcTxFrame txFrame(txBuffer, 256);
+SensirionShdlcRxFrame rxFrame(rxBuffer, 256);
+
+txFrame.begin(COMMAND, ADDRESS, DATALENGTH);
+
+txFrame.addUInt8(UINT8);
+txFrame.addUInt32(UINT32);
+
+txFrame.finish();
+
+SensirionShdlcCommunication::sendAndReceiveFrame(STREAMOBJECT, txFrame, rxFrame, TIMEOUT);
+
+rxFrame.getUInt16(UINT16);
+rxFrame.getFloat(FLOAT);
+
+```
+
+## I2C
+
+This library provides the following classes for communication with Sensirion
+Sensors using the I2C protocol.
+- `SensirionI2cTxFrame`
+- `SensirionI2cRxFrame`
+- `SensirionI2cCommunication`
+
+### Example Usage
+
+First initialize an instance of `SensirionI2CTxFrame` and `SensirionI2CRxFrame`
+with a buffer sized the amount of data to read times 1.5. This is needed to
+account for the CRC which is added after every second byte. After that you can
+build your frame by first calling `addCommand()` to add the command at the
+beginning of the frame. Information about the different COMMANDs can be found
+on the data sheet of your sensor. Then you can add data to the frame by using
+different add member functions. See the code below for examples.
+
+To send this frame to the sensor you first need to initialize a Wire object.
+Don't forget to also call the `.begin()` function with the right configuration.
+Then call the static function `sendFrame()` form `SensirionI2CCommunication` as
+shown below. You can find the ADDRESS on the data sheet of the sensor. You also
+need to replace `WIREOBJECT` with the initialized Wire object. Then wait the in
+the data sheet documented `READ_DELAY` before receiving the reply from the
+sensor by calling `receiveFrame()` with the same Wire object.
+
+You then can decode the frame by using the different get members to convert the
+received data to desired data types.
+
+All functions return a error code if an error occurs during execution and zero
+otherwise.
+
+```cpp
+uint8_t txBuffer[256];
+uint8_t rxBuffer[256];
+
+SensirionShdlcTxFrame txFrame(txBuffer, 256);
+SensirionShdlcRxFrame rxFrame(rxBuffer, 256);
+
+txFrame.addCommand(COMMAND);
+
+txFrame.addUInt8(UINT8);
+txFrame.addUInt32(UINT32);
+
+SensirionShdlcCommunication::sendFrame(ADDRESS, txFrame, WIREOBJECT);
+
+delay(READ_DELAY);
+
+SensirionShdlcCommunication::receiveFrame(ADDRESS, rxFrame, WIREOBJECT);
+
+rxFrame.getUInt16(UINT16);
+rxFrame.getFloat(FLOAT);
+
+```
diff --git a/lib/lib_i2c/Sensirion_Core/examples/AllCommandsI2c/AllCommandsI2c.ino b/lib/lib_i2c/Sensirion_Core/examples/AllCommandsI2c/AllCommandsI2c.ino
new file mode 100644
index 000000000..49b10d868
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/examples/AllCommandsI2c/AllCommandsI2c.ino
@@ -0,0 +1,62 @@
+#include
+#include
+#include
+
+uint8_t txBuffer[256];
+uint8_t rxBuffer[256];
+
+SensirionI2CTxFrame txFrame(txBuffer, 256);
+SensirionI2CRxFrame rxFrame(rxBuffer, 256);
+
+void setup() {
+ Wire.begin();
+}
+
+void loop() {
+ uint16_t mockCommand = 42;
+ uint16_t error = txFrame.addCommand(mockCommand);
+
+ uint32_t mockUInt32 = 42;
+ error |= txFrame.addUInt32(mockUInt32);
+
+ int32_t mockInt32 = 42;
+ error |= txFrame.addInt32(mockInt32);
+
+ uint16_t mockUInt16 = 42;
+ error |= txFrame.addUInt16(mockUInt16);
+
+ int16_t mockInt16 = 42;
+ error |= txFrame.addInt16(mockInt16);
+
+ uint8_t mockUInt8 = 42;
+ error |= txFrame.addUInt8(mockUInt8);
+
+ int8_t mockInt8 = 42;
+ error |= txFrame.addInt8(mockInt8);
+
+ float mockFloat = 42.0f;
+ error |= txFrame.addFloat(mockFloat);
+
+ bool mockBool = true;
+ error |= txFrame.addBool(mockBool);
+
+ uint8_t mockBytes[] = {42, 42, 42, 42};
+ error |= txFrame.addBytes(mockBytes, 4);
+
+ uint8_t mockAddress = 42;
+
+ error |= SensirionI2CCommunication::sendFrame(mockAddress, txFrame, Wire);
+
+ size_t mockNumBytes = 42;
+ error |= SensirionI2CCommunication::receiveFrame(mockAddress, mockNumBytes,
+ rxFrame, Wire);
+
+ error |= rxFrame.getUInt32(mockUInt32);
+ error |= rxFrame.getInt32(mockInt32);
+ error |= rxFrame.getUInt16(mockUInt16);
+ error |= rxFrame.getInt16(mockInt16);
+ error |= rxFrame.getUInt8(mockUInt8);
+ error |= rxFrame.getInt8(mockInt8);
+ error |= rxFrame.getFloat(mockFloat);
+ error |= rxFrame.getBytes(mockBytes, 4);
+}
diff --git a/lib/lib_i2c/Sensirion_Core/examples/AllCommandsShdlc/AllCommandsShdlc.ino b/lib/lib_i2c/Sensirion_Core/examples/AllCommandsShdlc/AllCommandsShdlc.ino
new file mode 100644
index 000000000..2ff07bb94
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/examples/AllCommandsShdlc/AllCommandsShdlc.ino
@@ -0,0 +1,73 @@
+#include
+#include
+
+uint8_t txBuffer[256];
+uint8_t rxBuffer[256];
+
+SensirionShdlcTxFrame txFrame(txBuffer, 256);
+SensirionShdlcRxFrame rxFrame(rxBuffer, 256);
+
+void setup() {
+ Serial.begin(115200);
+}
+
+void loop() {
+ uint8_t mockCommand = 42;
+ uint8_t mockAddress = 42;
+ uint8_t mockDataLength = 42;
+ uint16_t error = txFrame.begin(mockCommand, mockAddress, mockDataLength);
+
+ uint32_t mockUInt32 = 42;
+ error |= txFrame.addUInt32(mockUInt32);
+
+ int32_t mockInt32 = 42;
+ error |= txFrame.addInt32(mockInt32);
+
+ uint16_t mockUInt16 = 42;
+ error |= txFrame.addUInt16(mockUInt16);
+
+ int16_t mockInt16 = 42;
+ error |= txFrame.addInt16(mockInt16);
+
+ uint8_t mockUInt8 = 42;
+ error |= txFrame.addUInt8(mockUInt8);
+
+ int8_t mockInt8 = 42;
+ error |= txFrame.addInt8(mockInt8);
+
+ float mockFloat = 42.0f;
+ error |= txFrame.addFloat(mockFloat);
+
+ bool mockBool = true;
+ error |= txFrame.addBool(mockBool);
+
+ uint8_t mockBytes[] = {42, 42, 42, 42};
+ error |= txFrame.addBytes(mockBytes, 4);
+
+ error |= txFrame.finish();
+
+ error |= SensirionShdlcCommunication::sendFrame(txFrame, Serial);
+
+ error |= SensirionShdlcCommunication::sendAndReceiveFrame(
+ Serial, txFrame, rxFrame, 10000000);
+
+ error |=
+ SensirionShdlcCommunication::receiveFrame(rxFrame, Serial, 1000000);
+
+ error |= rxFrame.getUInt32(mockUInt32);
+ error |= rxFrame.getInt32(mockInt32);
+ error |= rxFrame.getUInt16(mockUInt16);
+ error |= rxFrame.getInt16(mockInt16);
+ error |= rxFrame.getUInt8(mockUInt8);
+ error |= rxFrame.getInt8(mockInt8);
+ error |= rxFrame.getFloat(mockFloat);
+ error |= rxFrame.getBytes(mockBytes, 4);
+
+ mockCommand = rxFrame.getCommand();
+ mockAddress = rxFrame.getAddress();
+ mockDataLength = rxFrame.getDataLength();
+ uint8_t mockState = rxFrame.getState();
+ if (mockState) {
+ // There is an error in the device.
+ }
+}
diff --git a/lib/lib_i2c/Sensirion_Core/keywords.txt b/lib/lib_i2c/Sensirion_Core/keywords.txt
new file mode 100644
index 000000000..910b12264
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/keywords.txt
@@ -0,0 +1,56 @@
+#######################################
+# Syntax Coloring Map
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+SensirionShdlcCommunication KEYWORD1
+SensirionShdlcRxFrame KEYWORD1
+SensirionShdlcTxFrame KEYWORD1
+SensirionI2CTxFrame KEYWORD1
+SensirionI2CRxFrame KEYWORD1
+SensirionI2CCommunication KEYWORD1
+
+# SensirionErrors.h
+HighLevelError KEYWORD1
+LowLevelError KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+sendFrame KEYWORD2
+receiveFrame KEYWORD2
+sendAndReceiveFrame KEYWORD2
+addUInt32 KEYWORD2
+addInt32 KEYWORD2
+addUInt16 KEYWORD2
+addInt16 KEYWORD2
+addUInt8 KEYWORD2
+addInt8 KEYWORD2
+addFloat KEYWORD2
+addBytes KEYWORD2
+addBool KEYWORD2
+addCommand KEYWORD2
+begin KEYWORD2
+finish KEYWORD2
+reset KEYWORD2
+getUInt32 KEYWORD2
+getInt32 KEYWORD2
+getUInt16 KEYWORD2
+getInt16 KEYWORD2
+getUInt8 KEYWORD2
+getInt8 KEYWORD2
+getFloat KEYWORD2
+getBytes KEYWORD2
+getCommand KEYWORD2
+getAddress KEYWORD2
+getDataLength KEYWORD2
+getState KEYWORD2
+
+# SensirionErrors.h
+errorToString KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
diff --git a/lib/lib_i2c/Sensirion_Core/library.properties b/lib/lib_i2c/Sensirion_Core/library.properties
new file mode 100644
index 000000000..9c5934fa1
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/library.properties
@@ -0,0 +1,9 @@
+name=Sensirion Core
+version=0.6.0
+author=Sensirion
+maintainer=Sensirion
+sentence=Library containing code base for Sensirion Sensor Libraries.
+paragraph=All Libraries for Sensirion Sensors use this library as a code base. In this library the Sensirion specific parts for I2C and UART communication are implemented. It provides dynamic frame construction, checksum calculation and buffer handling.
+category=Communication
+url=https://github.com/Sensirion/arduino-core/
+includes=SensirionCore.h
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionCore.h b/lib/lib_i2c/Sensirion_Core/src/SensirionCore.h
new file mode 100644
index 000000000..9222b61dd
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionCore.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021, Sensirion AG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Sensirion AG nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _SENSIRION_CORE_H_
+#define _SENSIRION_CORE_H_
+
+#include "SensirionCrc.h"
+#include "SensirionErrors.h"
+#include "SensirionRxFrame.h"
+
+#include "SensirionShdlcCommunication.h"
+#include "SensirionShdlcRxFrame.h"
+#include "SensirionShdlcTxFrame.h"
+
+#include "SensirionI2CCommunication.h"
+#include "SensirionI2CRxFrame.h"
+#include "SensirionI2CTxFrame.h"
+
+#endif /* _SENSIRION_CORE_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionCoreArduinoLibrary.h b/lib/lib_i2c/Sensirion_Core/src/SensirionCoreArduinoLibrary.h
new file mode 100644
index 000000000..fcfeb6dca
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionCoreArduinoLibrary.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * THIS IS A LEGACY FILE AND WILL BE REMOVED SOON.
+ *
+ * Copyright (c) 2020, 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_CORE_ARDUINO_LIBRARY_H_
+#define _SENSIRION_CORE_ARDUINO_LIBRARY_H_
+
+#pragma GCC warning \
+ "Legacy file SensirionCoreArdunioLibrary.h included. Please include SensirionCore.h instead."
+
+#include "SensirionErrors.h"
+#include "SensirionRxFrame.h"
+
+#include "SensirionShdlcCommunication.h"
+#include "SensirionShdlcRxFrame.h"
+#include "SensirionShdlcTxFrame.h"
+
+#include "SensirionI2CCommunication.h"
+#include "SensirionI2CRxFrame.h"
+#include "SensirionI2CTxFrame.h"
+
+#endif /* _SENSIRION_CORE_ARDUION_LIBRARY_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionCrc.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionCrc.cpp
new file mode 100644
index 000000000..25b8234aa
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionCrc.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022, 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 "SensirionCrc.h"
+
+uint8_t generateCRCGeneric(const uint8_t* data, size_t count, uint8_t init,
+ uint8_t polynomial) {
+ uint8_t crc = init;
+
+ /* calculates 8-Bit checksum with given polynomial */
+ for (size_t current_byte = 0; current_byte < count; ++current_byte) {
+ crc ^= (data[current_byte]);
+ for (uint8_t crc_bit = 8; crc_bit > 0; --crc_bit) {
+ if (crc & 0x80)
+ crc = (crc << 1) ^ polynomial;
+ else
+ crc = (crc << 1);
+ }
+ }
+ return crc;
+}
+
+uint8_t generateCRC31_ff(const uint8_t* data, size_t count) {
+ return generateCRCGeneric(data, count, 0xff, 0x31);
+}
+
+uint8_t generateCRC31_00(const uint8_t* data, size_t count) {
+ return generateCRCGeneric(data, count, 0x00, 0x31);
+}
+
+uint8_t generateCRC(const uint8_t* data, size_t count, CrcPolynomial type) {
+ if (CRC31_00 == type) {
+ return generateCRC31_00(data, count);
+ }
+ return generateCRC31_ff(data, count);
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionCrc.h b/lib/lib_i2c/Sensirion_Core/src/SensirionCrc.h
new file mode 100644
index 000000000..904fc9286
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionCrc.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022, 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_CRC_H_
+#define _SENSIRION_CRC_H_
+
+#include
+#include
+
+enum CrcPolynomial : uint8_t {
+ CRC31_00 = 0x0,
+ CRC31_ff = 0x1,
+};
+
+uint8_t generateCRCGeneric(const uint8_t* data, size_t count, uint8_t init,
+ uint8_t polynomial);
+
+uint8_t generateCRC31_ff(const uint8_t* data, size_t count);
+
+uint8_t generateCRC31_00(const uint8_t* data, size_t count);
+
+/**
+ * @brief Generate a crc for data given a polynomial type
+ *
+ * @param data data to calculate CRC for
+ * @param count the array size of data
+ * @param poly CRC polynomal to use
+ */
+uint8_t generateCRC(const uint8_t* data, size_t count, CrcPolynomial type);
+
+#endif /* _SENSIRION_CRC_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionErrors.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionErrors.cpp
new file mode 100644
index 000000000..b0d7ffe62
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionErrors.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2020, 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 "SensirionErrors.h"
+
+#include
+#include
+#include
+
+void errorToString(uint16_t error, char errorMessage[],
+ size_t errorMessageSize) {
+
+ uint16_t highLevelError = error & 0xFF00;
+ uint16_t lowLevelError = error & 0x00FF;
+
+ if (error & HighLevelError::SensorSpecificError) {
+ snprintf(errorMessage, errorMessageSize, "Sensor specific error: 0x%2x",
+ lowLevelError);
+ return;
+ }
+
+ switch (highLevelError) {
+ case HighLevelError::NoError:
+ if (!error) {
+ strncpy(errorMessage, "No error", errorMessageSize);
+ return;
+ }
+ break;
+ case HighLevelError::WriteError:
+ switch (lowLevelError) {
+ case LowLevelError::SerialWriteError:
+ strncpy(errorMessage, "Error writing to serial",
+ errorMessageSize);
+ return;
+ case LowLevelError::InternalBufferSizeError:
+ strncpy(errorMessage,
+ "Data too long to fit in transmit buffer",
+ errorMessageSize);
+ return;
+ case LowLevelError::I2cAddressNack:
+ strncpy(errorMessage,
+ "Received NACK on transmit of address",
+ errorMessageSize);
+ return;
+ case LowLevelError::I2cDataNack:
+ strncpy(errorMessage, "Received NACK on transmit of data",
+ errorMessageSize);
+ return;
+ case LowLevelError::I2cOtherError:
+ strncpy(errorMessage, "Error writing to I2C bus",
+ errorMessageSize);
+ return;
+ }
+ break;
+ case HighLevelError::ReadError:
+ switch (lowLevelError) {
+ case LowLevelError::NonemptyFrameError:
+ strncpy(errorMessage, "Frame already contains data",
+ errorMessageSize);
+ return;
+ case LowLevelError::TimeoutError:
+ strncpy(errorMessage, "Timeout while reading data",
+ errorMessageSize);
+ return;
+ case LowLevelError::ChecksumError:
+ strncpy(errorMessage, "Checksum is wrong",
+ errorMessageSize);
+ return;
+ case LowLevelError::CRCError:
+ strncpy(errorMessage, "Wrong CRC found", errorMessageSize);
+ return;
+ case LowLevelError::WrongNumberBytesError:
+ strncpy(errorMessage, "Number of bytes not a multiple of 3",
+ errorMessageSize);
+ return;
+ case LowLevelError::NotEnoughDataError:
+ strncpy(errorMessage, "Not enough data received",
+ errorMessageSize);
+ return;
+ case LowLevelError::InternalBufferSizeError:
+ strncpy(errorMessage, "Internal I2C buffer too small",
+ errorMessageSize);
+ return;
+ }
+ break;
+ case HighLevelError::ExecutionError: {
+ char format[] = "Execution error, status register: 0x%x";
+ snprintf(errorMessage, errorMessageSize, format, lowLevelError);
+ return;
+ }
+ case HighLevelError::TxFrameError:
+ switch (lowLevelError) {
+ case LowLevelError::BufferSizeError:
+ strncpy(errorMessage, "Not enough space in buffer",
+ errorMessageSize);
+ return;
+ }
+ break;
+ case HighLevelError::RxFrameError:
+ switch (lowLevelError) {
+ case LowLevelError::BufferSizeError:
+ strncpy(errorMessage, "Not enough space in buffer",
+ errorMessageSize);
+ return;
+ case LowLevelError::NoDataError:
+ strncpy(errorMessage, "No more data in frame",
+ errorMessageSize);
+ return;
+ case LowLevelError::RxAddressError:
+ strncpy(errorMessage, "Wrong address in return frame",
+ errorMessageSize);
+ return;
+ case LowLevelError::RxCommandError:
+ strncpy(errorMessage, "Wrong command in return frame",
+ errorMessageSize);
+ return;
+ }
+ }
+ strncpy(errorMessage, "Error processing error", errorMessageSize);
+ return;
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionErrors.h b/lib/lib_i2c/Sensirion_Core/src/SensirionErrors.h
new file mode 100644
index 000000000..268d9754f
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionErrors.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020, 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_ERRORS_H_
+#define _SENSIRION_ERRORS_H_
+
+#include
+#include
+
+enum HighLevelError : uint16_t {
+ // general errors
+ NoError = 0,
+ WriteError = 0x0100,
+ ReadError = 0x0200,
+ TxFrameError = 0x0300,
+ RxFrameError = 0x0400,
+ // shdlc errors
+ ExecutionError = 0x0500,
+ // i2c errors
+
+ // Sensor specific errors. All errors higher than that are depending on the
+ // sensor used.
+ SensorSpecificError = 0x8000,
+};
+
+enum LowLevelError : uint16_t {
+ // general errors
+ NonemptyFrameError,
+ NoDataError,
+ BufferSizeError,
+ // shdlc errors
+ StopByteError,
+ ChecksumError,
+ TimeoutError,
+ RxCommandError,
+ RxAddressError,
+ SerialWriteError,
+ // i2c errors
+ WrongNumberBytesError,
+ CRCError,
+ I2cAddressNack,
+ I2cDataNack,
+ I2cOtherError,
+ NotEnoughDataError,
+ InternalBufferSizeError,
+};
+
+/**
+ * errorToString() - Convert error code to a human readable error message
+ *
+ * @param error Error code to be converted.
+ * @param errorMessage String where the error text can be
+ * stored.
+ * @param errorMessageSize Size in bytes of the string buffer for the error
+ * message.
+ */
+void errorToString(uint16_t error, char errorMessage[],
+ size_t errorMessageSize);
+
+#endif /* _SENSIRION_ERRORS_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionI2CCommunication.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CCommunication.cpp
new file mode 100644
index 000000000..2d8b7acab
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CCommunication.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2020, 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 "SensirionI2CCommunication.h"
+
+#include
+#include
+
+#include "Arduino.h"
+#include "SensirionCrc.h"
+#include "SensirionErrors.h"
+#include "SensirionI2CRxFrame.h"
+#include "SensirionI2CTxFrame.h"
+
+static void clearRxBuffer(TwoWire& i2cBus) {
+ while (i2cBus.available()) {
+ (void)i2cBus.read();
+ }
+}
+
+uint16_t SensirionI2CCommunication::sendFrame(uint8_t address,
+ SensirionI2CTxFrame& frame,
+ TwoWire& i2cBus) {
+ i2cBus.beginTransmission(address);
+ size_t writtenBytes = i2cBus.write(frame._buffer, frame._index);
+ uint8_t i2c_error = i2cBus.endTransmission();
+ if (writtenBytes != frame._index) {
+ return WriteError | I2cOtherError;
+ }
+ // translate Arduino errors, see
+ // https://www.arduino.cc/en/Reference/WireEndTransmission
+ switch (i2c_error) {
+ case 0:
+ return NoError;
+ case 1:
+ return WriteError | InternalBufferSizeError;
+ case 2:
+ return WriteError | I2cAddressNack;
+ case 3:
+ return WriteError | I2cDataNack;
+ default:
+ return WriteError | I2cOtherError;
+ }
+}
+
+uint16_t SensirionI2CCommunication::receiveFrame(uint8_t address,
+ size_t numBytes,
+ SensirionI2CRxFrame& frame,
+ TwoWire& i2cBus,
+ CrcPolynomial poly) {
+ size_t readAmount;
+ size_t i = 0;
+
+#ifdef I2C_BUFFER_LENGTH
+ const uint8_t sizeBuffer =
+ (static_cast(I2C_BUFFER_LENGTH) / static_cast(3)) * 3;
+#elif defined(BUFFER_LENGTH)
+ const uint8_t sizeBuffer =
+ (static_cast(BUFFER_LENGTH) / static_cast(3)) * 3;
+#else
+ const uint8_t sizeBuffer = 30;
+#endif
+
+ if (numBytes % 3) {
+ return ReadError | WrongNumberBytesError;
+ }
+ if ((numBytes / 3) * 2 > frame._bufferSize) {
+ return ReadError | BufferSizeError;
+ }
+ if (numBytes > sizeBuffer) {
+ return ReadError | InternalBufferSizeError;
+ }
+
+ readAmount = i2cBus.requestFrom(address, static_cast(numBytes),
+ static_cast(true));
+ if (numBytes != readAmount) {
+ return ReadError | NotEnoughDataError;
+ }
+ do {
+ frame._buffer[i++] = i2cBus.read();
+ frame._buffer[i++] = i2cBus.read();
+ uint8_t actualCRC = i2cBus.read();
+ uint8_t expectedCRC = generateCRC(&frame._buffer[i - 2], 2, poly);
+ if (actualCRC != expectedCRC) {
+ clearRxBuffer(i2cBus);
+ return ReadError | CRCError;
+ }
+ readAmount -= 3;
+ } while (readAmount > 0);
+ frame._numBytes = i;
+ return NoError;
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionI2CCommunication.h b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CCommunication.h
new file mode 100644
index 000000000..98aead75a
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CCommunication.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020, 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_COMMUNICATION_H_
+#define SENSIRION_I2C_COMMUNICATION_H_
+
+#include
+#include
+
+#include "Arduino.h"
+#include "Wire.h"
+
+#include "SensirionI2CRxFrame.h"
+#include "SensirionI2CTxFrame.h"
+
+class SensirionI2CTxFrame;
+class SensirionI2CRxFrame;
+
+/*
+ * SensirionI2CCommunication - Class which is responsible for the communication
+ * via a I2C bus. It provides functionality to send and receive frames from a
+ * Sensirion sensor. The data is sent and received in a SensirionI2cTxFrame or
+ * SensirionI2cRxFrame respectively.
+ */
+class SensirionI2CCommunication {
+ public:
+ /**
+ * sendFrame() - Sends frame to sensor
+ *
+ * @param address I2C address of the sensor.
+ * @param frame Tx frame object containing a finished frame to send to
+ * the sensor.
+ * @param i2cBus TwoWire object to communicate with the sensor.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ static uint16_t sendFrame(uint8_t address, SensirionI2CTxFrame& frame,
+ TwoWire& i2cBus);
+
+ /**
+ * receiveFrame() - Receive Frame from sensor
+ *
+ * @param address I2C address of the sensor.
+ * @param numBytes Number of bytes to receive.
+ * @param frame Rx frame to store the received data in.
+ * @param i2cBus TwoWire object to communicate with the sensor.
+ * @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ static uint16_t receiveFrame(uint8_t address, size_t numBytes,
+ SensirionI2CRxFrame& frame, TwoWire& i2cBus,
+ CrcPolynomial poly = CRC31_ff);
+};
+
+#endif /* SENSIRION_I2C_COMMUNICATION_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionI2CRxFrame.h b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CRxFrame.h
new file mode 100644
index 000000000..e15cfa28b
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CRxFrame.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, 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_RX_FRAME_H_
+#define SENSIRION_I2C_RX_FRAME_H_
+
+#include
+#include
+
+#include "SensirionI2CCommunication.h"
+#include "SensirionRxFrame.h"
+
+/**
+ * SenirionI2CRxFrame - Class which decodes the through I2C received data into
+ * common data types. It contains a buffer which is filled by the
+ * SensirionI2CCommunication class. By calling the different decode function
+ * inherited from the SensirionRxFrame base class the raw data can be decoded
+ * into different data types.
+ */
+class SensirionI2CRxFrame : public SensirionRxFrame {
+
+ friend class SensirionI2CCommunication;
+
+ public:
+ /**
+ * Constructor
+ *
+ * @param buffer Buffer in which the receive frame will be
+ * stored.
+ * @param bufferSize Number of bytes in the buffer for the receive frame.
+ *
+ */
+ SensirionI2CRxFrame(uint8_t buffer[], size_t bufferSize)
+ : SensirionRxFrame(buffer, bufferSize){};
+};
+
+#endif /* SENSIRION_I2C_RX_FRAME_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionI2CTxFrame.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CTxFrame.cpp
new file mode 100644
index 000000000..67999f86c
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CTxFrame.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2020, 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,
+ * 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
+ * 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 "SensirionI2CTxFrame.h"
+
+#include
+#include
+
+#include "SensirionCrc.h"
+#include "SensirionErrors.h"
+
+SensirionI2CTxFrame::SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
+ size_t numCommandBytes,
+ CrcPolynomial poly)
+ : _buffer(buffer), _bufferSize(bufferSize), _index(numCommandBytes),
+ _numCommandBytes(numCommandBytes), _polynomial_type(poly) {
+}
+
+SensirionI2CTxFrame::SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
+ CrcPolynomial poly)
+ : SensirionI2CTxFrame(buffer, bufferSize, 2, poly) {
+}
+
+SensirionI2CTxFrame SensirionI2CTxFrame::createWithUInt8Command(
+ uint8_t command, uint8_t buffer[], size_t bufferSize, CrcPolynomial poly) {
+ SensirionI2CTxFrame instance =
+ SensirionI2CTxFrame(buffer, bufferSize, 1, poly);
+ instance._buffer[0] = command;
+ return instance;
+}
+
+SensirionI2CTxFrame SensirionI2CTxFrame::createWithUInt16Command(
+ uint16_t command, uint8_t buffer[], size_t bufferSize, CrcPolynomial poly) {
+ SensirionI2CTxFrame instance =
+ SensirionI2CTxFrame(buffer, bufferSize, 2, poly);
+ instance._buffer[0] = static_cast((command & 0xFF00) >> 8);
+ instance._buffer[1] = static_cast((command & 0x00FF) >> 0);
+ return instance;
+}
+
+uint16_t SensirionI2CTxFrame::addCommand(uint16_t command) {
+ if (_bufferSize < 2) {
+ return TxFrameError | BufferSizeError;
+ }
+ _buffer[0] = static_cast((command & 0xFF00) >> 8);
+ _buffer[1] = static_cast((command & 0x00FF) >> 0);
+ return NoError;
+}
+
+uint16_t SensirionI2CTxFrame::addUInt32(uint32_t data) {
+ uint16_t error = _addByte(static_cast((data & 0xFF000000) >> 24));
+ error |= _addByte(static_cast((data & 0x00FF0000) >> 16));
+ error |= _addByte(static_cast((data & 0x0000FF00) >> 8));
+ error |= _addByte(static_cast((data & 0x000000FF) >> 0));
+ return error;
+}
+
+uint16_t SensirionI2CTxFrame::addInt32(int32_t data) {
+ return addUInt32(static_cast(data));
+}
+
+uint16_t SensirionI2CTxFrame::addUInt16(uint16_t data) {
+ uint16_t error = _addByte(static_cast((data & 0xFF00) >> 8));
+ error |= _addByte(static_cast((data & 0x00FF) >> 0));
+ return error;
+}
+
+uint16_t SensirionI2CTxFrame::addInt16(int16_t data) {
+ return addUInt16(static_cast(data));
+}
+
+uint16_t SensirionI2CTxFrame::addUInt8(uint8_t data) {
+ return _addByte(data);
+}
+
+uint16_t SensirionI2CTxFrame::addInt8(int8_t data) {
+ return _addByte(static_cast(data));
+}
+
+uint16_t SensirionI2CTxFrame::addBool(bool data) {
+ return _addByte(static_cast(data));
+}
+
+uint16_t SensirionI2CTxFrame::addFloat(float data) {
+ union {
+ uint32_t uInt32Data;
+ float floatData;
+ } convert;
+
+ convert.floatData = data;
+ return addUInt32(convert.uInt32Data);
+}
+
+uint16_t SensirionI2CTxFrame::addBytes(const uint8_t data[],
+ size_t dataLength) {
+ uint16_t error = 0;
+ for (size_t i = 0; i < dataLength; i++) {
+ error |= _addByte(data[i]);
+ }
+ return error;
+}
+
+uint16_t SensirionI2CTxFrame::_addByte(uint8_t data) {
+ if (_bufferSize <= _index) {
+ return TxFrameError | BufferSizeError;
+ }
+ _buffer[_index++] = data;
+ if ((_index - _numCommandBytes) % 3 == 2) {
+ if (_bufferSize <= _index) {
+ return TxFrameError | BufferSizeError;
+ }
+ uint8_t crc = generateCRC(&_buffer[_index - 2], 2, _polynomial_type);
+ _buffer[_index++] = crc;
+ }
+ return NoError;
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionI2CTxFrame.h b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CTxFrame.h
new file mode 100644
index 000000000..3e5cee3df
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionI2CTxFrame.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2020, 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_TX_FRAME_H_
+#define SENSIRION_I2C_TX_FRAME_H_
+
+#include
+#include
+
+#include "SensirionCrc.h"
+#include "SensirionI2CCommunication.h"
+
+/*
+ * SensirionI2CTxFrame - Class which helps to build a correct I2C frame for
+ * Sensirion Sensors. The different addDatatype() functions add the frame data
+ * and the addCommand() function writes the command at the beginning. Using
+ * these functions one can easily construct a I2C frame for Sensirion sensors.
+ */
+class SensirionI2CTxFrame {
+
+ friend class SensirionI2CCommunication;
+
+ public:
+ /**
+ * Factory to create a SensirionI2CTxFrame using a UInt8 command.
+ *
+ * @param command Command to add to the send frame.
+ * @param buffer Buffer in which the send frame will be stored.
+ * @param bufferSize Number of bytes in the buffer for the send frame.
+ * @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
+ *
+ * @return the constructed SensirionI2CTxFrame.
+ */
+ static SensirionI2CTxFrame
+ createWithUInt8Command(uint8_t command, uint8_t buffer[], size_t bufferSize,
+ CrcPolynomial poly = CRC31_ff);
+
+ /**
+ * Factory to create a SensirionI2CTxFrame using a UInt16 command.
+ *
+ * @param command Command to add to the send frame.
+ * @param buffer Buffer in which the send frame will be stored.
+ * @param bufferSize Number of bytes in the buffer for the send frame.
+ * @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
+ *
+ * @return the constructed SensirionI2CTxFrame.
+ */
+ static SensirionI2CTxFrame
+ createWithUInt16Command(uint16_t command, uint8_t buffer[],
+ size_t bufferSize, CrcPolynomial poly = CRC31_ff);
+
+ /**
+ * Constructor
+ *
+ * @param buffer Buffer in which the send frame will be stored.
+ * @param bufferSize Number of bytes in the buffer for the send frame.
+ * @param poly CRC polynomal to use. Defaults to 0x31 with init 0xFF
+ *
+ * @deprecated Use createWithUInt16Command() instead
+ */
+ SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
+ CrcPolynomial poly = CRC31_ff);
+
+ /**
+ * addCommand() - Add command to the send frame.
+ *
+ * @param command Command to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ *
+ * @deprecated Use createWithUInt16Command() instead
+ */
+ uint16_t addCommand(uint16_t command);
+
+ /**
+ * addUInt32() - Add unsigned 32bit integer to the send frame.
+ *
+ * @param data Unsigned 32bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addUInt32(uint32_t data);
+
+ /**
+ * addInt32() - Add signed 32bit integer to the send frame.
+ *
+ * @param data Signed 32bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addInt32(int32_t data);
+
+ /**
+ * addUInt16() - Add unsigned 16bit integer to the send frame.
+ *
+ * @param data Unsigned 16bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addUInt16(uint16_t data);
+
+ /**
+ * addInt16() - Add signed 16bit integer to the send frame.
+ *
+ * @param data Signed 16bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addInt16(int16_t data);
+
+ /**
+ * addUInt8() - Add unsigned 8bit integer to the send frame.
+ *
+ * @param data Unsigned 8bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addUInt8(uint8_t data);
+
+ /**
+ * addInt8() - Add signed 8bit integer to the send frame.
+ *
+ * @param data Signed 8bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addInt8(int8_t data);
+
+ /**
+ * addBool() - Add boolean to the send frame.
+ *
+ * @param data Boolean to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addBool(bool data);
+
+ /**
+ * addFloat() - Add float to the send frame.
+ *
+ * @param data Float to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addFloat(float data);
+
+ /**
+ * addBytes() - Add byte array to the send frame.
+ *
+ * @param data Byte array to add to the send frame.
+ * @param dataLength Number of bytes to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addBytes(const uint8_t data[], size_t dataLength);
+
+ private:
+ SensirionI2CTxFrame(uint8_t buffer[], size_t bufferSize,
+ size_t numCommandBytes, CrcPolynomial poly = CRC31_ff);
+
+ uint16_t _addByte(uint8_t data);
+
+ uint8_t* _buffer;
+ size_t _bufferSize;
+ size_t _index;
+ size_t _numCommandBytes;
+ CrcPolynomial _polynomial_type;
+};
+
+#endif /* SENSIRION_I2C_TX_FRAME_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionRxFrame.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionRxFrame.cpp
new file mode 100644
index 000000000..c653c250d
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionRxFrame.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2020, 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 "SensirionRxFrame.h"
+
+#include
+#include
+
+#include "SensirionErrors.h"
+
+SensirionRxFrame::SensirionRxFrame(uint8_t buffer[], size_t bufferSize)
+ : _buffer(buffer), _bufferSize(bufferSize), _index(0), _numBytes(0) {
+}
+
+uint16_t SensirionRxFrame::getUInt32(uint32_t& data) {
+ if (_numBytes < 4) {
+ return RxFrameError | NoDataError;
+ }
+ data = static_cast(_buffer[_index++]) << 24;
+ data |= static_cast(_buffer[_index++]) << 16;
+ data |= static_cast(_buffer[_index++]) << 8;
+ data |= static_cast(_buffer[_index++]);
+ _numBytes -= 4;
+ return NoError;
+}
+
+uint16_t SensirionRxFrame::getInt32(int32_t& data) {
+ uint32_t ret;
+ uint16_t error = getUInt32(ret);
+ data = static_cast(ret);
+ return error;
+}
+
+uint16_t SensirionRxFrame::getUInt16(uint16_t& data) {
+ if (_numBytes < 2) {
+ return RxFrameError | NoDataError;
+ }
+ data = static_cast(_buffer[_index++]) << 8;
+ data |= static_cast(_buffer[_index++]);
+ _numBytes -= 2;
+ return NoError;
+}
+
+uint16_t SensirionRxFrame::getInt16(int16_t& data) {
+ uint16_t ret;
+ uint16_t error = getUInt16(ret);
+ data = static_cast(ret);
+ return error;
+}
+
+uint16_t SensirionRxFrame::getUInt8(uint8_t& data) {
+ if (_numBytes < 1) {
+ return RxFrameError | NoDataError;
+ }
+ data = _buffer[_index++];
+ _numBytes -= 1;
+ return NoError;
+}
+
+uint16_t SensirionRxFrame::getInt8(int8_t& data) {
+ if (_numBytes < 1) {
+ return RxFrameError | NoDataError;
+ }
+ data = static_cast(_buffer[_index++]);
+ _numBytes -= 1;
+ return NoError;
+}
+
+uint16_t SensirionRxFrame::getBool(bool& data) {
+ if (_numBytes < 1) {
+ return RxFrameError | NoDataError;
+ }
+ data = static_cast(_buffer[_index++]);
+ _numBytes -= 1;
+ return NoError;
+}
+
+uint16_t SensirionRxFrame::getFloat(float& data) {
+ union {
+ uint32_t uInt32Data;
+ float floatData;
+ } convert;
+ uint16_t error = getUInt32(convert.uInt32Data);
+ data = convert.floatData;
+ return error;
+}
+
+uint16_t SensirionRxFrame::getBytes(uint8_t data[], size_t maxBytes) {
+ if (_numBytes < 1) {
+ return RxFrameError | NoDataError;
+ }
+ size_t readAmount = maxBytes;
+ if (_numBytes < maxBytes) {
+ readAmount = _numBytes;
+ }
+ for (size_t i = 0; i < readAmount; i++) {
+ data[i] = _buffer[_index++];
+ }
+ _numBytes -= readAmount;
+ return NoError;
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionRxFrame.h b/lib/lib_i2c/Sensirion_Core/src/SensirionRxFrame.h
new file mode 100644
index 000000000..a4384fb6c
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionRxFrame.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2020, 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_RX_FRAME_H_
+#define SENSIRION_RX_FRAME_H_
+
+#include
+#include
+
+/**
+ * SenirionRxFrame - Base class for SensirionShdlcRxFrame and
+ * SensirionI2cRxFrame. It decodes received data into common data types. The
+ * data is contained in a buffer which is filled by the one of the two
+ * communication classes. By calling the different decode function the raw data
+ * can be decoded into different data types.
+ */
+class SensirionRxFrame {
+
+ friend class SensirionI2CCommunication;
+ friend class SensirionShdlcCommunication;
+
+ public:
+ /**
+ * Constructor
+ *
+ * @param buffer Buffer in which the receive frame will be stored.
+ * @param bufferSize Number of bytes in the buffer for the receive frame.
+ */
+ SensirionRxFrame(uint8_t buffer[], size_t bufferSize);
+
+ /**
+ * getUInt32() - Get unsigned 32bit integer from the received data.
+ *
+ * @param data Memory to store unsigned 32bit integer in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getUInt32(uint32_t& data);
+
+ /**
+ * getInt32() - Get signed 32bit integer from the received data.
+ *
+ * @param data Memory to store signed 32bit integer in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getInt32(int32_t& data);
+
+ /**
+ * getUInt16() - Get unsigned 16bit integer from the received data.
+ *
+ * @param data Memory to store unsigned 16bit integer in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getUInt16(uint16_t& data);
+
+ /**
+ * getInt16() - Get signed 16bit integer from the received data.
+ *
+ * @param data Memory to store signed 16bit integer in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getInt16(int16_t& data);
+
+ /**
+ * getUInt8() - Get unsigned 8bit integer from the received data.
+ *
+ * @param data Memory to store unsigned 8bit integer in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getUInt8(uint8_t& data);
+
+ /**
+ * getInt8() - Get signed 8bit integer from the received data.
+ *
+ * @param data Memory to store signed 8bit integer in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getInt8(int8_t& data);
+
+ /**
+ * getBool() - Get Boolean from the received data.
+ *
+ * @param data Memory to store boolean in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getBool(bool& data);
+
+ /**
+ * getFloat() - Get float from the received data.
+ *
+ * @param data Memory to store float in.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getFloat(float& data);
+
+ /**
+ * getBytes() - Get an array of bytes from the received data.
+ *
+ * @param data Buffer to store the bytes in.
+ * @param maxBytes Maximal amount of bytes to read from the received data.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t getBytes(uint8_t data[], size_t maxBytes);
+
+ private:
+ uint8_t* _buffer = 0;
+ size_t _bufferSize = 0;
+ size_t _index = 0;
+ size_t _numBytes = 0;
+};
+
+#endif /* SENSIRION_RX_FRAME_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcCommunication.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcCommunication.cpp
new file mode 100644
index 000000000..300546ee8
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcCommunication.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2020, 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 "SensirionShdlcCommunication.h"
+
+#include
+#include
+
+#include "Arduino.h"
+#include "SensirionErrors.h"
+#include "SensirionShdlcRxFrame.h"
+#include "SensirionShdlcTxFrame.h"
+
+static uint16_t readByte(uint8_t& data, Stream& serial, unsigned long startTime,
+ unsigned long timeoutMicros) {
+ do {
+ if (micros() - startTime > timeoutMicros) {
+ return ReadError | TimeoutError;
+ }
+ } while (!serial.available());
+ data = serial.read();
+ return NoError;
+}
+
+static uint16_t unstuffByte(uint8_t& data, Stream& serial,
+ unsigned long startTime,
+ unsigned long timeoutMicros) {
+ uint16_t error = readByte(data, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ if (data == 0x7d) {
+ error = readByte(data, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ data = data ^ (1 << 5);
+ }
+ return NoError;
+}
+
+uint16_t SensirionShdlcCommunication::sendFrame(SensirionShdlcTxFrame& frame,
+ Stream& serial) {
+ size_t writtenBytes = serial.write(&frame._buffer[0], frame._index);
+ if (writtenBytes != frame._index) {
+ return WriteError | SerialWriteError;
+ }
+ return NoError;
+}
+
+uint16_t SensirionShdlcCommunication::receiveFrame(
+ SensirionShdlcRxFrame& frame, Stream& serial, unsigned long timeoutMicros) {
+ unsigned long startTime = micros();
+ uint16_t error;
+ uint8_t dataLength;
+ uint8_t current = 0;
+
+ if (frame._numBytes) {
+ return ReadError | NonemptyFrameError;
+ }
+
+ // Wait for start byte and ignore all other bytes in case a partial frame
+ // is still in the receive buffer due to a previous error.
+ do {
+ error = readByte(current, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ } while (current != 0x7e);
+
+ // Handle a repeated start byte which may happen
+ do {
+ error = unstuffByte(current, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ } while (current == 0x7e);
+
+ frame._address = current;
+ error = unstuffByte(frame._command, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ error = unstuffByte(frame._state, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ error = unstuffByte(dataLength, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+
+ uint8_t checksum =
+ frame._address + frame._command + frame._state + dataLength;
+
+ if (dataLength > frame._bufferSize) {
+ return RxFrameError | BufferSizeError;
+ }
+
+ size_t i = 0;
+ while (i < dataLength) {
+ error = unstuffByte(current, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ frame._buffer[i] = current;
+ checksum += current;
+ i++;
+ }
+
+ uint8_t expectedChecksum = ~checksum;
+ uint8_t actualChecksum;
+ error = unstuffByte(actualChecksum, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ if (expectedChecksum != actualChecksum) {
+ return ReadError | ChecksumError;
+ }
+
+ uint8_t stop;
+ error = readByte(stop, serial, startTime, timeoutMicros);
+ if (error) {
+ return error;
+ }
+ if (stop != 0x7e) {
+ return ReadError | StopByteError;
+ }
+ if (frame._state & 0x7F) {
+ return ExecutionError | frame._state;
+ }
+ frame._dataLength = dataLength;
+ frame._numBytes = dataLength;
+ return NoError;
+}
+
+uint16_t SensirionShdlcCommunication::sendAndReceiveFrame(
+ Stream& serial, SensirionShdlcTxFrame& txFrame,
+ SensirionShdlcRxFrame& rxFrame, unsigned long rxTimeoutMicros) {
+ uint16_t error;
+ error = SensirionShdlcCommunication::sendFrame(txFrame, serial);
+ if (error) {
+ return error;
+ }
+ error = SensirionShdlcCommunication::receiveFrame(rxFrame, serial,
+ rxTimeoutMicros);
+ if (error) {
+ return error;
+ }
+ if (rxFrame.getCommand() != txFrame.getCommand()) {
+ return RxFrameError | RxCommandError;
+ }
+ if (rxFrame.getAddress() != txFrame.getAddress()) {
+ return RxFrameError | RxAddressError;
+ }
+ return NoError;
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcCommunication.h b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcCommunication.h
new file mode 100644
index 000000000..9d2dc35b1
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcCommunication.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020, 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_SHDLC_COMMUNICATION_H_
+#define SENSIRION_SHDLC_COMMUNICATION_H_
+
+#include
+#include
+
+#include "Arduino.h"
+
+#include "SensirionShdlcRxFrame.h"
+#include "SensirionShdlcTxFrame.h"
+
+class SensirionShdlcTxFrame;
+class SensirionShdlcRxFrame;
+
+/*
+ * SensirionShdlcCommunication - Class which is responsible for the
+ * communication via a UART (Serial) interface. It provides functionality to
+ * send and receive frames from a Sensirion sensor. The data is sent and
+ * received in a SensirionShdlcTxFrame or SensirionShdlcRxFrame respectively.
+ */
+class SensirionShdlcCommunication {
+
+ public:
+ /**
+ * sendFrame() - Sends frame to sensor
+ *
+ * @param frame Tx frame object containing a finished frame to send to the
+ * sensor.
+ * @param serial Stream object to communicate with the sensor.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ static uint16_t sendFrame(SensirionShdlcTxFrame& frame, Stream& serial);
+
+ /**
+ * receiveFrame() - Receive Frame from sensor
+ *
+ * @param frame Rx frame to store the received data in.
+ * @param serial Stream object to communicate with the sensor.
+ * @param timeoutMicros Timeout in micro seconds for the receive operation.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ static uint16_t receiveFrame(SensirionShdlcRxFrame& frame, Stream& serial,
+ unsigned long timeoutMicros);
+
+ /**
+ * sendAndReceiveFrame() - Send and receive a frame from sensor.
+ *
+ * @param serial Stream object to communicate with the sensor.
+ * @param txFrame Tx frame object containing a finished frame to
+ * send to the sensor.
+ * @param rxFrame Rx frame to store the received data in.
+ * @param rxTimeoutMicros Timeout in micro seconds for the receive
+ * operation.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ static uint16_t sendAndReceiveFrame(Stream& serial,
+ SensirionShdlcTxFrame& txFrame,
+ SensirionShdlcRxFrame& rxFrame,
+ unsigned long rxTimeoutMicros);
+};
+
+#endif /* SENSIRION_SHDLC_COMMUNICATION_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcRxFrame.h b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcRxFrame.h
new file mode 100644
index 000000000..d4bca0e2a
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcRxFrame.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020, 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_SHDLC_RX_FRAME_H_
+#define SENSIRION_SHDLC_RX_FRAME_H_
+
+#include
+#include
+
+#include "SensirionRxFrame.h"
+#include "SensirionShdlcCommunication.h"
+
+/**
+ * SenirionShdlcRxFrame - Class which decodes the through UART received data
+ * into common data types. It contains a buffer which is filled by the
+ * SensirionShdlcCommunication class. By calling the different decode function
+ * inherited from the SensirionRxFrame base class the raw data can be decoded
+ * into different data types. In addition to that it also stores the four
+ * header bytes defined by the SHDLC protocol state, command, address,
+ * datalength. These bytes can be read out by the corresponding getter method.
+ */
+class SensirionShdlcRxFrame : public SensirionRxFrame {
+
+ friend class SensirionShdlcCommunication;
+
+ public:
+ /**
+ * Constructor
+ *
+ * @param buffer Buffer in which the receive frame will be stored.
+ * @param bufferSize Number of bytes in the buffer for the receive frame.
+ */
+ SensirionShdlcRxFrame(uint8_t buffer[], size_t bufferSize)
+ : SensirionRxFrame(buffer, bufferSize){};
+
+ uint8_t getAddress(void) const {
+ return _address;
+ };
+
+ uint8_t getCommand(void) const {
+ return _command;
+ };
+
+ uint8_t getState(void) const {
+ return _state;
+ };
+
+ uint8_t getDataLength(void) const {
+ return _dataLength;
+ };
+
+ private:
+ uint8_t _address = 0;
+ uint8_t _command = 0;
+ uint8_t _state = 0;
+ uint8_t _dataLength = 0;
+};
+
+#endif /* SENSIRION_SHDLC_RX_FRAME_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcTxFrame.cpp b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcTxFrame.cpp
new file mode 100644
index 000000000..fc8c4c994
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcTxFrame.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2020, 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,
+ * 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
+ * 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 "SensirionShdlcTxFrame.h"
+
+#include
+#include
+
+#include "SensirionErrors.h"
+
+uint16_t SensirionShdlcTxFrame::begin(uint8_t command, uint8_t address,
+ uint8_t dataLength) {
+ _buffer[_index++] = 0x7e;
+ uint16_t error = addUInt8(address);
+ error |= addUInt8(command);
+ error |= addUInt8(dataLength);
+ _command = command;
+ _address = address;
+ return error;
+}
+
+uint16_t SensirionShdlcTxFrame::finish(void) {
+ uint16_t error = addUInt8(~_checksum);
+ if (error) {
+ return error;
+ }
+ if (_index + 1 > _bufferSize) {
+ return TxFrameError | BufferSizeError;
+ }
+ _buffer[_index++] = 0x7e;
+ _isFinished = true;
+ return NoError;
+}
+
+uint16_t SensirionShdlcTxFrame::addUInt32(uint32_t data) {
+ uint16_t error = addUInt8(static_cast((data & 0xFF000000) >> 24));
+ error |= addUInt8(static_cast((data & 0x00FF0000) >> 16));
+ error |= addUInt8(static_cast((data & 0x0000FF00) >> 8));
+ error |= addUInt8(static_cast((data & 0x000000FF) >> 0));
+ return error;
+}
+
+uint16_t SensirionShdlcTxFrame::addInt32(int32_t data) {
+ return addUInt32(static_cast(data));
+}
+
+uint16_t SensirionShdlcTxFrame::addUInt16(uint16_t data) {
+ uint16_t error = addUInt8(static_cast((data & 0xFF00) >> 8));
+ error |= addUInt8(static_cast((data & 0x00FF) >> 0));
+ return error;
+}
+
+uint16_t SensirionShdlcTxFrame::addInt16(int16_t data) {
+ return addUInt16(static_cast(data));
+}
+
+uint16_t SensirionShdlcTxFrame::addUInt8(uint8_t data) {
+ if (_index + 2 > _bufferSize) {
+ return TxFrameError | BufferSizeError;
+ }
+ switch (data) {
+ case 0x11:
+ case 0x13:
+ case 0x7d:
+ case 0x7e:
+ // byte stuffing is done by inserting 0x7d and inverting bit 5
+ _buffer[_index++] = 0x7d;
+ _buffer[_index++] = data ^ (1 << 5);
+ break;
+ default:
+ _buffer[_index++] = data;
+ }
+ _checksum += data;
+ return NoError;
+}
+
+uint16_t SensirionShdlcTxFrame::addInt8(int8_t data) {
+ return addUInt8(static_cast(data));
+}
+
+uint16_t SensirionShdlcTxFrame::addBool(bool data) {
+ return addUInt8(static_cast(data));
+}
+
+uint16_t SensirionShdlcTxFrame::addFloat(float data) {
+ union {
+ uint32_t uInt32Data;
+ float floatData;
+ } convert;
+
+ convert.floatData = data;
+ return addUInt32(convert.uInt32Data);
+}
+
+uint16_t SensirionShdlcTxFrame::addBytes(const uint8_t data[],
+ size_t dataLength) {
+ uint16_t error = 0;
+ for (size_t i = 0; i < dataLength; i++) {
+ error |= addUInt8(data[i]);
+ }
+ return error;
+}
diff --git a/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcTxFrame.h b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcTxFrame.h
new file mode 100644
index 000000000..eb2dbc6e7
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/src/SensirionShdlcTxFrame.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2020, 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_SHDLC_TX_FRAME_H_
+#define SENSIRION_SHDLC_TX_FRAME_H_
+
+#include
+#include
+
+#include "SensirionShdlcCommunication.h"
+
+/*
+ * SensirionShdlcTxFrame - Class which helps to build a correct SHDLC frame.
+ * The begin() functions writes the header. The different addDatatype()
+ * functions add the frame data and the finish() function writes the tail.
+ * Using these functions one can easily construct a SHDLC frame.
+ */
+class SensirionShdlcTxFrame {
+
+ friend class SensirionShdlcCommunication;
+
+ public:
+ /**
+ * Constructor
+ *
+ * @param buffer Buffer in which the send frame will be stored.
+ * @param bufferSize Number of bytes in the buffer for the send frame.
+ */
+ SensirionShdlcTxFrame(uint8_t buffer[], size_t bufferSize)
+ : _buffer(buffer), _bufferSize(bufferSize) {
+ }
+
+ /**
+ * begin() - Begin frame and write header.
+ *
+ * @note This function needs to be called before calling other
+ * data add functions to write the header at the beginning.
+ *
+ * @param command Command byte to add to the send frame.
+ * @param address Address byte to add to the send frame.
+ * @param dataLength Data length byte to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t begin(uint8_t command, uint8_t address, uint8_t dataLength);
+
+ /**
+ * finish() - Finish frame and write tail.
+ *
+ * @note This function needs to be called last, after adding all
+ * data to frame and before sending the frame to the sensor.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t finish(void);
+
+ /**
+ * addUInt32() - Add unsigned 32bit integer to the send frame.
+ *
+ * @param data Unsigned 32bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addUInt32(uint32_t data);
+
+ /**
+ * addInt32() - Add signed 32bit integer to the send frame.
+ *
+ * @param data Signed 32bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addInt32(int32_t data);
+
+ /**
+ * addUInt16() - Add unsigned 16bit integer to the send frame.
+ *
+ * @param data Unsigned 16bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addUInt16(uint16_t data);
+
+ /**
+ * addInt16() - Add signed 16bit integer to the send frame.
+ *
+ * @param data Signed 16bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addInt16(int16_t data);
+
+ /**
+ * addUInt8() - Add unsigned 8bit integer to the send frame.
+ *
+ * @param data Unsigned 8bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addUInt8(uint8_t data);
+
+ /**
+ * addInt8() - Add signed 8bit integer to the send frame.
+ *
+ * @param data Signed 8bit integer to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addInt8(int8_t data);
+
+ /**
+ * addBool() - Add boolean to the send frame.
+ *
+ * @param data Boolean to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addBool(bool data);
+
+ /**
+ * addFloat() - Add float to the send frame.
+ *
+ * @param data Float to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addFloat(float data);
+
+ /**
+ * addBytes() - Add byte array to the send frame.
+ *
+ * @param data Byte array to add to the send frame.
+ * @param dataLength Number of bytes to add to the send frame.
+ *
+ * @return NoError on success, an error code otherwise
+ */
+ uint16_t addBytes(const uint8_t data[], size_t dataLength);
+
+ uint8_t getCommand(void) const {
+ return _command;
+ };
+
+ uint8_t getAddress(void) const {
+ return _address;
+ }
+
+ private:
+ uint8_t* _buffer;
+ size_t _bufferSize;
+ size_t _index = 0;
+ uint8_t _checksum = 0;
+ bool _isFinished = false;
+ uint8_t _command = 0;
+ uint8_t _address = 0;
+};
+
+#endif /* SENSIRION_SHDLC_TX_FRAME_H_ */
diff --git a/lib/lib_i2c/Sensirion_Core/tests/compile_test.py b/lib/lib_i2c/Sensirion_Core/tests/compile_test.py
new file mode 100644
index 000000000..81c543a68
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/tests/compile_test.py
@@ -0,0 +1,45 @@
+#! /usr/bin/env python3
+import subprocess
+import json
+import argparse
+import sys
+import logging
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.description = u'Compile test a sketch for all available boards'
+ parser.add_argument(u'-s', u'--sketch', dest=u'sketch',
+ required=True, help=u'Path to sketch')
+ args = parser.parse_args()
+ test_all_boards(args.sketch)
+
+
+def test_all_boards(sketch):
+ logging.basicConfig(level=logging.INFO,
+ format='%(asctime)s [%(levelname)s] %(message)s')
+ log = logging.getLogger('arduino-compile-test')
+ process = subprocess.run("arduino-cli board listall --format json".split(),
+ stdout=subprocess.PIPE)
+ board_list_json = process.stdout.decode('utf-8')
+ board_list = json.loads(board_list_json)
+ test_list = ["arduino:samd:mkrzero", "arduino:avr:mega",
+ "arduino:avr:nano", "arduino:avr:uno",
+ "esp32:esp32:esp32", "esp8266:esp8266:generic"]
+ for board in test_list:
+ if board in (b['fqbn'] for b in board_list['boards']):
+ log.info('Test compilation for board {}'.format(board))
+ command = 'arduino-cli compile --libraries="." --warnings all'\
+ ' --fqbn {board} {sketch}'.format(board=board,
+ sketch=sketch)
+ process = subprocess.run(command.split(), stdout=subprocess.PIPE)
+ if process.returncode:
+ log.error(process.stdout.decode('utf-8'))
+ sys.exit(process.returncode)
+ else:
+ log.error('Board not installed: {}'.format(board))
+ sys.exit(-1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/lib_i2c/Sensirion_Core/tests/run_cppcheck.sh b/lib/lib_i2c/Sensirion_Core/tests/run_cppcheck.sh
new file mode 100644
index 000000000..60d620b1e
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/tests/run_cppcheck.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+set -euxo pipefail
+cppcheck --std=c++11 --language=c++ --error-exitcode=1 --enable=warning,style,performance,portability src/*
diff --git a/lib/lib_i2c/Sensirion_Core/tests/syntax_check.sh b/lib/lib_i2c/Sensirion_Core/tests/syntax_check.sh
new file mode 100644
index 000000000..53aeaba2c
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_Core/tests/syntax_check.sh
@@ -0,0 +1,5 @@
+#! /bin/bash
+set -euxo pipefail
+editorconfig-checker
+flake8
+find . -type f -iregex ".*\.\(c\|h\|cpp\|ino\)" -exec clang-format-6.0 -i -style=file {} \; && git diff --exit-code
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/CHANGELOG.md b/lib/lib_i2c/Sensirion_I2C_SEN5X/CHANGELOG.md
new file mode 100644
index 000000000..a1d8044bc
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/CHANGELOG.md
@@ -0,0 +1,20 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+
+## [0.2.0] - 2022-03-30
+
+Add support for SEN50
+
+## [0.1.0] - 2022-01-05
+
+Initial release
+
+[0.2.0]: https://github.com/Sensirion/embedded-i2c-sen5x/compare/0.1.0...0.2.0
+[0.1.0]: https://github.com/Sensirion/arduino-i2c-sen5x/releases/tag/0.1.0
+
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/LICENSE b/lib/lib_i2c/Sensirion_I2C_SEN5X/LICENSE
new file mode 100644
index 000000000..ff20c41df
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2021, Sensirion AG
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+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/lib/lib_i2c/Sensirion_I2C_SEN5X/README.md b/lib/lib_i2c/Sensirion_I2C_SEN5X/README.md
new file mode 100644
index 000000000..5165ad7c3
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/README.md
@@ -0,0 +1,97 @@
+
+# Sensirion I2C SEN5X Arduino Library
+
+This is the Sensirion SEN5X library for Arduino using the
+modules I2C interface.
+
+
+
+## Supported sensors
+
+- SEN50 (only particulate matter signals available)
+- SEN54 (no NOx signal available)
+- SEN55 (full feature set)
+
+# Installation
+
+To install, download the latest release as .zip file and add it to your
+[Arduino IDE](http://www.arduino.cc/en/main/software) via
+
+ Sketch => Include Library => Add .ZIP Library...
+
+Don't forget to **install the dependencies** listed below the same way via `Add
+.ZIP Library`
+
+Note: Installation via the Arduino Library Manager is coming soon.
+
+# Dependencies
+
+* [Sensirion Core](https://github.com/Sensirion/arduino-core)
+
+
+# Quick Start
+
+1. Connect the SEN5X Sensor to your Arduino board's standard
+ I2C bus. Check the pinout of your Arduino board to find the correct pins.
+ The pinout of the SEN5X Sensor board can be found in the
+ data sheet.
+
+ | *SEN5X* | *Arduino* | *Jumper Wire* |
+ | ------- | ----------- | ------------- |
+ | VCC | 5V | Red |
+ | GND | GND | Black |
+ | SDA | SDA | Green |
+ | SCL | SCL | Yellow |
+ | SEL | GND for I2C | Blue |
+
+
+
+ | *Pin* | *Name* | *Description* | *Comments* |
+ | ----- | ------ | ------------------------------- | -------------------------------- |
+ | 1 | VCC | Supply Voltage | 5V ±10% |
+ | 2 | GND | Ground |
+ | 3 | SDA | I2C: Serial data input / output | TTL 5V and LVTTL 3.3V compatible |
+ | 4 | SCL | I2C: Serial clock input | TTL 5V and LVTTL 3.3V compatible |
+ | 5 | SEL | Interface select | Pull to GND to select I2C |
+ | 6 | NC | Do not connect |
+
+2. Open the `exampleUsage` sample project within the Arduino IDE
+
+ File => Examples => Sensirion I2C SEN5X => exampleUsage
+
+3. Click the `Upload` button in the Arduino IDE or
+
+ Sketch => Upload
+
+4. When the upload process has finished, open the `Serial Monitor` or `Serial
+ Plotter` via the `Tools` menu to observe the measurement values. Note that
+ the `Baud Rate` in the corresponding window has to be set to `115200 baud`.
+
+# Contributing
+
+**Contributions are welcome!**
+
+We develop and test this driver using our company internal tools (version
+control, continuous integration, code review etc.) and automatically
+synchronize the master branch with GitHub. But this doesn't mean that we don't
+respond to issues or don't accept pull requests on GitHub. In fact, you're very
+welcome to open issues or create pull requests :)
+
+This Sensirion library uses
+[`clang-format`](https://releases.llvm.org/download.html) to standardize the
+formatting of all our `.cpp` 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 src/*.cpp src/*.h
+```
+
+Note that differences from this formatting will result in a failed build until
+they are fixed.
+
+# License
+
+See [LICENSE](LICENSE).
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/examples/exampleUsage/exampleUsage.ino b/lib/lib_i2c/Sensirion_I2C_SEN5X/examples/exampleUsage/exampleUsage.ino
new file mode 100644
index 000000000..c92a10374
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/examples/exampleUsage/exampleUsage.ino
@@ -0,0 +1,249 @@
+
+/*
+ * I2C-Generator: 0.3.0
+ * Yaml Version: 2.1.3
+ * Template Version: 0.7.0-112-g190ecaa
+ */
+/*
+ * Copyright (c) 2021, Sensirion AG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Sensirion AG nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#include
+#include
+
+// The used commands use up to 48 bytes. On some Arduino's the default buffer
+// space is not large enough
+#define MAXBUF_REQUIREMENT 48
+
+#if (defined(I2C_BUFFER_LENGTH) && \
+ (I2C_BUFFER_LENGTH >= MAXBUF_REQUIREMENT)) || \
+ (defined(BUFFER_LENGTH) && BUFFER_LENGTH >= MAXBUF_REQUIREMENT)
+#define USE_PRODUCT_INFO
+#endif
+
+SensirionI2CSen5x sen5x;
+
+void printModuleVersions() {
+ uint16_t error;
+ char errorMessage[256];
+
+ unsigned char productName[32];
+ uint8_t productNameSize = 32;
+
+ error = sen5x.getProductName(productName, productNameSize);
+
+ if (error) {
+ Serial.print("Error trying to execute getProductName(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ } else {
+ Serial.print("ProductName:");
+ Serial.println((char*)productName);
+ }
+
+ uint8_t firmwareMajor;
+ uint8_t firmwareMinor;
+ bool firmwareDebug;
+ uint8_t hardwareMajor;
+ uint8_t hardwareMinor;
+ uint8_t protocolMajor;
+ uint8_t protocolMinor;
+
+ error = sen5x.getVersion(firmwareMajor, firmwareMinor, firmwareDebug,
+ hardwareMajor, hardwareMinor, protocolMajor,
+ protocolMinor);
+ if (error) {
+ Serial.print("Error trying to execute getVersion(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ } else {
+ Serial.print("Firmware: ");
+ Serial.print(firmwareMajor);
+ Serial.print(".");
+ Serial.print(firmwareMinor);
+ Serial.print(", ");
+
+ Serial.print("Hardware: ");
+ Serial.print(hardwareMajor);
+ Serial.print(".");
+ Serial.println(hardwareMinor);
+ }
+}
+
+void printSerialNumber() {
+ uint16_t error;
+ char errorMessage[256];
+ unsigned char serialNumber[32];
+ uint8_t serialNumberSize = 32;
+
+ error = sen5x.getSerialNumber(serialNumber, serialNumberSize);
+ if (error) {
+ Serial.print("Error trying to execute getSerialNumber(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ } else {
+ Serial.print("SerialNumber:");
+ Serial.println((char*)serialNumber);
+ }
+}
+
+void setup() {
+
+ Serial.begin(115200);
+ while (!Serial) {
+ delay(100);
+ }
+
+ Wire.begin();
+
+ sen5x.begin(Wire);
+
+ uint16_t error;
+ char errorMessage[256];
+ error = sen5x.deviceReset();
+ if (error) {
+ Serial.print("Error trying to execute deviceReset(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ }
+
+// Print SEN55 module information if i2c buffers are large enough
+#ifdef USE_PRODUCT_INFO
+ printSerialNumber();
+ printModuleVersions();
+#endif
+
+ // set a temperature offset in degrees celsius
+ // Note: supported by SEN54 and SEN55 sensors
+ // By default, the temperature and humidity outputs from the sensor
+ // are compensated for the modules self-heating. If the module is
+ // designed into a device, the temperature compensation might need
+ // to be adapted to incorporate the change in thermal coupling and
+ // self-heating of other device components.
+ //
+ // A guide to achieve optimal performance, including references
+ // to mechanical design-in examples can be found in the app note
+ // “SEN5x – Temperature Compensation Instruction” at www.sensirion.com.
+ // Please refer to those application notes for further information
+ // on the advanced compensation settings used
+ // in `setTemperatureOffsetParameters`, `setWarmStartParameter` and
+ // `setRhtAccelerationMode`.
+ //
+ // Adjust tempOffset to account for additional temperature offsets
+ // exceeding the SEN module's self heating.
+ float tempOffset = 0.0;
+ error = sen5x.setTemperatureOffsetSimple(tempOffset);
+ if (error) {
+ Serial.print("Error trying to execute setTemperatureOffsetSimple(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ } else {
+ Serial.print("Temperature Offset set to ");
+ Serial.print(tempOffset);
+ Serial.println(" deg. Celsius (SEN54/SEN55 only");
+ }
+
+ // Start Measurement
+ error = sen5x.startMeasurement();
+ if (error) {
+ Serial.print("Error trying to execute startMeasurement(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ }
+}
+
+void loop() {
+ uint16_t error;
+ char errorMessage[256];
+
+ delay(1000);
+
+ // Read Measurement
+ float massConcentrationPm1p0;
+ float massConcentrationPm2p5;
+ float massConcentrationPm4p0;
+ float massConcentrationPm10p0;
+ float ambientHumidity;
+ float ambientTemperature;
+ float vocIndex;
+ float noxIndex;
+
+ error = sen5x.readMeasuredValues(
+ massConcentrationPm1p0, massConcentrationPm2p5, massConcentrationPm4p0,
+ massConcentrationPm10p0, ambientHumidity, ambientTemperature, vocIndex,
+ noxIndex);
+
+ if (error) {
+ Serial.print("Error trying to execute readMeasuredValues(): ");
+ errorToString(error, errorMessage, 256);
+ Serial.println(errorMessage);
+ } else {
+ Serial.print("MassConcentrationPm1p0:");
+ Serial.print(massConcentrationPm1p0);
+ Serial.print("\t");
+ Serial.print("MassConcentrationPm2p5:");
+ Serial.print(massConcentrationPm2p5);
+ Serial.print("\t");
+ Serial.print("MassConcentrationPm4p0:");
+ Serial.print(massConcentrationPm4p0);
+ Serial.print("\t");
+ Serial.print("MassConcentrationPm10p0:");
+ Serial.print(massConcentrationPm10p0);
+ Serial.print("\t");
+ Serial.print("AmbientHumidity:");
+ if (isnan(ambientHumidity)) {
+ Serial.print("n/a");
+ } else {
+ Serial.print(ambientHumidity);
+ }
+ Serial.print("\t");
+ Serial.print("AmbientTemperature:");
+ if (isnan(ambientTemperature)) {
+ Serial.print("n/a");
+ } else {
+ Serial.print(ambientTemperature);
+ }
+ Serial.print("\t");
+ Serial.print("VocIndex:");
+ if (isnan(vocIndex)) {
+ Serial.print("n/a");
+ } else {
+ Serial.print(vocIndex);
+ }
+ Serial.print("\t");
+ Serial.print("NoxIndex:");
+ if (isnan(noxIndex)) {
+ Serial.println("n/a");
+ } else {
+ Serial.println(noxIndex);
+ }
+ }
+}
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/images/SEN5X_pinout.png b/lib/lib_i2c/Sensirion_I2C_SEN5X/images/SEN5X_pinout.png
new file mode 100644
index 000000000..c42395c2f
Binary files /dev/null and b/lib/lib_i2c/Sensirion_I2C_SEN5X/images/SEN5X_pinout.png differ
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/images/SEN5x.png b/lib/lib_i2c/Sensirion_I2C_SEN5X/images/SEN5x.png
new file mode 100644
index 000000000..32bb95b01
Binary files /dev/null and b/lib/lib_i2c/Sensirion_I2C_SEN5X/images/SEN5x.png differ
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/keywords.txt b/lib/lib_i2c/Sensirion_I2C_SEN5X/keywords.txt
new file mode 100644
index 000000000..295031081
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/keywords.txt
@@ -0,0 +1,55 @@
+#######################################
+# Syntax Coloring Map
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+SensirionI2CSen5x KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+startMeasurement KEYWORD2
+startMeasurementWithoutPm KEYWORD2
+stopMeasurement KEYWORD2
+readDataReady KEYWORD2
+readMeasuredValues KEYWORD2
+readMeasuredValuesAsIntegers KEYWORD2
+readMeasuredValuesSen50 KEYWORD2
+readMeasuredRawValues KEYWORD2
+readMeasuredPmValues KEYWORD2
+readMeasuredPmValuesAsIntegers KEYWORD2
+startFanCleaning KEYWORD2
+setTemperatureOffsetSimple KEYWORD2
+getTemperatureOffsetSimple KEYWORD2
+setTemperatureOffsetParameters KEYWORD2
+getTemperatureOffsetParameters KEYWORD2
+setWarmStartParameter KEYWORD2
+getWarmStartParameter KEYWORD2
+setVocAlgorithmTuningParameters KEYWORD2
+getVocAlgorithmTuningParameters KEYWORD2
+setNoxAlgorithmTuningParameters KEYWORD2
+getNoxAlgorithmTuningParameters KEYWORD2
+setRhtAccelerationMode KEYWORD2
+getRhtAccelerationMode KEYWORD2
+setVocAlgorithmState KEYWORD2
+getVocAlgorithmState KEYWORD2
+setFanAutoCleaningInterval KEYWORD2
+getFanAutoCleaningInterval KEYWORD2
+getProductName KEYWORD2
+getSerialNumber KEYWORD2
+getVersion KEYWORD2
+readDeviceStatus KEYWORD2
+readAndClearDeviceStatus KEYWORD2
+deviceReset KEYWORD2
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+sen5x KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/library.properties b/lib/lib_i2c/Sensirion_I2C_SEN5X/library.properties
new file mode 100644
index 000000000..ea4a9000b
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/library.properties
@@ -0,0 +1,10 @@
+name=Sensirion I2C SEN5X
+version=0.2.0
+author=Sensirion
+maintainer=Sensirion
+sentence=Library for the SEN5X sensor family by Sensirion
+paragraph=Enables you to use the SEN50, SEN54 and SEN55 via I2C.
+url=https://github.com/Sensirion/arduino-i2c-sen5x
+category=Sensors
+depends=Sensirion Core
+includes=SensirionI2CSen5x.h
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/src/SensirionI2CSen5x.cpp b/lib/lib_i2c/Sensirion_I2C_SEN5X/src/SensirionI2CSen5x.cpp
new file mode 100644
index 000000000..3008e5226
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/src/SensirionI2CSen5x.cpp
@@ -0,0 +1,880 @@
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED
+ *
+ * I2C-Generator: 0.3.0
+ * Yaml Version: 2.1.3
+ * Template Version: 0.7.0-112-g190ecaa
+ */
+/*
+ * Copyright (c) 2021, Sensirion AG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Sensirion AG nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "SensirionI2CSen5x.h"
+#include "Arduino.h"
+#include "SensirionCore.h"
+#include
+#include
+
+#define SEN5X_I2C_ADDRESS 0x69
+#define UINT_INVALID 0xFFFF
+#define INT_INVALID 0x7FFF
+
+SensirionI2CSen5x::SensirionI2CSen5x() {
+}
+
+void SensirionI2CSen5x::begin(TwoWire& i2cBus) {
+ _i2cBus = &i2cBus;
+}
+
+uint16_t SensirionI2CSen5x::startMeasurement() {
+ uint16_t error = 0;
+ uint8_t buffer[2];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x21, buffer, 2);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(50);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::startMeasurementWithoutPm() {
+ uint16_t error = 0;
+ uint8_t buffer[2];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x37, buffer, 2);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(50);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::stopMeasurement() {
+ uint16_t error = 0;
+ uint8_t buffer[2];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x104, buffer, 2);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(200);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readDataReady(bool& dataReady) {
+ uint16_t error = 0;
+ uint8_t buffer[3];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x202, buffer, 3);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 3);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 3,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ uint8_t padding;
+ error |= rxFrame.getUInt8(padding); // remove padding
+ error |= rxFrame.getBool(dataReady);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readMeasuredValues(
+ float& massConcentrationPm1p0, float& massConcentrationPm2p5,
+ float& massConcentrationPm4p0, float& massConcentrationPm10p0,
+ float& ambientHumidity, float& ambientTemperature, float& vocIndex,
+ float& noxIndex) {
+
+ uint16_t error = 0;
+ uint16_t massConcentrationPm1p0Int;
+ uint16_t massConcentrationPm2p5Int;
+ uint16_t massConcentrationPm4p0Int;
+ uint16_t massConcentrationPm10p0Int;
+ int16_t ambientHumidityInt;
+ int16_t ambientTemperatureInt;
+ int16_t vocIndexInt;
+ int16_t noxIndexInt;
+
+ error = readMeasuredValuesAsIntegers(
+ massConcentrationPm1p0Int, massConcentrationPm2p5Int,
+ massConcentrationPm4p0Int, massConcentrationPm10p0Int,
+ ambientHumidityInt, ambientTemperatureInt, vocIndexInt, noxIndexInt);
+
+ if (error) {
+ return error;
+ }
+
+ massConcentrationPm1p0 = massConcentrationPm1p0Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm1p0Int / 10.0f;
+ massConcentrationPm2p5 = massConcentrationPm2p5Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm2p5Int / 10.0f;
+ massConcentrationPm4p0 = massConcentrationPm4p0Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm4p0Int / 10.0f;
+ massConcentrationPm10p0 = massConcentrationPm10p0Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm10p0Int / 10.0f;
+ ambientHumidity =
+ ambientHumidityInt == INT_INVALID ? NAN : ambientHumidityInt / 100.0f;
+ ambientTemperature = ambientTemperatureInt == INT_INVALID
+ ? NAN
+ : ambientTemperatureInt / 200.0f;
+ vocIndex = vocIndexInt == INT_INVALID ? NAN : vocIndexInt / 10.0f;
+ noxIndex = noxIndexInt == INT_INVALID ? NAN : noxIndexInt / 10.0f;
+
+ return NoError;
+}
+
+uint16_t SensirionI2CSen5x::readMeasuredValuesAsIntegers(
+ uint16_t& massConcentrationPm1p0, uint16_t& massConcentrationPm2p5,
+ uint16_t& massConcentrationPm4p0, uint16_t& massConcentrationPm10p0,
+ int16_t& ambientHumidity, int16_t& ambientTemperature, int16_t& vocIndex,
+ int16_t& noxIndex) {
+ uint16_t error = 0;
+ uint8_t buffer[24];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x3C4, buffer, 24);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 24);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 24,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt16(massConcentrationPm1p0);
+ error |= rxFrame.getUInt16(massConcentrationPm2p5);
+ error |= rxFrame.getUInt16(massConcentrationPm4p0);
+ error |= rxFrame.getUInt16(massConcentrationPm10p0);
+ error |= rxFrame.getInt16(ambientHumidity);
+ error |= rxFrame.getInt16(ambientTemperature);
+ error |= rxFrame.getInt16(vocIndex);
+ error |= rxFrame.getInt16(noxIndex);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readMeasuredRawValues(int16_t& rawHumidity,
+ int16_t& rawTemperature,
+ uint16_t& rawVoc,
+ uint16_t& rawNox) {
+ uint16_t error = 0;
+ uint8_t buffer[12];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x3D2, buffer, 12);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 12);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 12,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getInt16(rawHumidity);
+ error |= rxFrame.getInt16(rawTemperature);
+ error |= rxFrame.getUInt16(rawVoc);
+ error |= rxFrame.getUInt16(rawNox);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readMeasuredValuesSen50(
+ float& massConcentrationPm1p0, float& massConcentrationPm2p5,
+ float& massConcentrationPm4p0, float& massConcentrationPm10p0) {
+
+ uint16_t error = 0;
+ float ambientHumidityDummy;
+ float ambientTemperatureDummy;
+ float vocIndexDummy;
+ float noxIndexDummy;
+ error = readMeasuredValues(massConcentrationPm1p0, massConcentrationPm2p5,
+ massConcentrationPm4p0, massConcentrationPm10p0,
+ ambientHumidityDummy, ambientTemperatureDummy,
+ vocIndexDummy, noxIndexDummy);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readMeasuredPmValues(
+ float& massConcentrationPm1p0, float& massConcentrationPm2p5,
+ float& massConcentrationPm4p0, float& massConcentrationPm10p0,
+ float& numberConcentrationPm0p5, float& numberConcentrationPm1p0,
+ float& numberConcentrationPm2p5, float& numberConcentrationPm4p0,
+ float& numberConcentrationPm10p0, float& typicalParticleSize) {
+
+ uint16_t error = 0;
+ uint16_t massConcentrationPm1p0Int;
+ uint16_t massConcentrationPm2p5Int;
+ uint16_t massConcentrationPm4p0Int;
+ uint16_t massConcentrationPm10p0Int;
+ uint16_t numberConcentrationPm0p5Int;
+ uint16_t numberConcentrationPm1p0Int;
+ uint16_t numberConcentrationPm2p5Int;
+ uint16_t numberConcentrationPm4p0Int;
+ uint16_t numberConcentrationPm10p0Int;
+ uint16_t typicalParticleSizeInt;
+
+ error = readMeasuredPmValuesAsIntegers(
+ massConcentrationPm1p0Int, massConcentrationPm2p5Int,
+ massConcentrationPm4p0Int, massConcentrationPm10p0Int,
+ numberConcentrationPm0p5Int, numberConcentrationPm1p0Int,
+ numberConcentrationPm2p5Int, numberConcentrationPm4p0Int,
+ numberConcentrationPm10p0Int, typicalParticleSizeInt);
+
+ if (error) {
+ return error;
+ }
+
+ massConcentrationPm1p0 = massConcentrationPm1p0Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm1p0Int / 10.0f;
+ massConcentrationPm2p5 = massConcentrationPm2p5Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm2p5Int / 10.0f;
+ massConcentrationPm4p0 = massConcentrationPm4p0Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm4p0Int / 10.0f;
+ massConcentrationPm10p0 = massConcentrationPm10p0Int == UINT_INVALID
+ ? NAN
+ : massConcentrationPm10p0Int / 10.0f;
+ numberConcentrationPm0p5 = numberConcentrationPm0p5Int == UINT_INVALID
+ ? NAN
+ : numberConcentrationPm0p5Int / 10.0f;
+ numberConcentrationPm1p0 = numberConcentrationPm1p0Int == UINT_INVALID
+ ? NAN
+ : numberConcentrationPm1p0Int / 10.0f;
+ numberConcentrationPm2p5 = numberConcentrationPm2p5Int == UINT_INVALID
+ ? NAN
+ : numberConcentrationPm2p5Int / 10.0f;
+ numberConcentrationPm4p0 = numberConcentrationPm4p0Int == UINT_INVALID
+ ? NAN
+ : numberConcentrationPm4p0Int / 10.0f;
+ numberConcentrationPm10p0 = numberConcentrationPm10p0Int == UINT_INVALID
+ ? NAN
+ : numberConcentrationPm10p0Int / 10.0f;
+ typicalParticleSize = typicalParticleSizeInt == UINT_INVALID
+ ? NAN
+ : typicalParticleSizeInt / 1000.0f;
+
+ return NoError;
+}
+
+uint16_t SensirionI2CSen5x::readMeasuredPmValuesAsIntegers(
+ uint16_t& massConcentrationPm1p0, uint16_t& massConcentrationPm2p5,
+ uint16_t& massConcentrationPm4p0, uint16_t& massConcentrationPm10p0,
+ uint16_t& numberConcentrationPm0p5, uint16_t& numberConcentrationPm1p0,
+ uint16_t& numberConcentrationPm2p5, uint16_t& numberConcentrationPm4p0,
+ uint16_t& numberConcentrationPm10p0, uint16_t& typicalParticleSize) {
+ uint16_t error = 0;
+ uint8_t buffer[30];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x413, buffer, 30);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 30);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 30,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt16(massConcentrationPm1p0);
+ error |= rxFrame.getUInt16(massConcentrationPm2p5);
+ error |= rxFrame.getUInt16(massConcentrationPm4p0);
+ error |= rxFrame.getUInt16(massConcentrationPm10p0);
+ error |= rxFrame.getUInt16(numberConcentrationPm0p5);
+ error |= rxFrame.getUInt16(numberConcentrationPm1p0);
+ error |= rxFrame.getUInt16(numberConcentrationPm2p5);
+ error |= rxFrame.getUInt16(numberConcentrationPm4p0);
+ error |= rxFrame.getUInt16(numberConcentrationPm10p0);
+ error |= rxFrame.getUInt16(typicalParticleSize);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::startFanCleaning() {
+ uint16_t error = 0;
+ uint8_t buffer[2];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x5607, buffer, 2);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setTemperatureOffsetSimple(float tempOffset) {
+ int16_t defaultSlope = 0;
+ uint16_t defaultTimeConstant = 0;
+ int16_t tempOffsetTicks = static_cast(tempOffset * 200);
+ return setTemperatureOffsetParameters(tempOffsetTicks, defaultSlope,
+ defaultTimeConstant);
+}
+
+uint16_t SensirionI2CSen5x::getTemperatureOffsetSimple(float& tempOffset) {
+ int16_t tempOffsetTicks;
+ int16_t slope;
+ uint16_t timeConstant;
+ uint16_t error = 0;
+
+ error =
+ getTemperatureOffsetParameters(tempOffsetTicks, slope, timeConstant);
+ if (error) {
+ return error;
+ }
+
+ tempOffset = static_cast(tempOffsetTicks) / 200.0f;
+
+ return NoError;
+}
+
+uint16_t SensirionI2CSen5x::setTemperatureOffsetParameters(
+ int16_t tempOffset, int16_t slope, uint16_t timeConstant) {
+ uint16_t error = 0;
+ uint8_t buffer[11];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60B2, buffer, 11);
+
+ error |= txFrame.addInt16(tempOffset);
+ error |= txFrame.addInt16(slope);
+ error |= txFrame.addUInt16(timeConstant);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getTemperatureOffsetParameters(
+ int16_t& tempOffset, int16_t& slope, uint16_t& timeConstant) {
+ uint16_t error = 0;
+ uint8_t buffer[9];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60B2, buffer, 9);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 9);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 9,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getInt16(tempOffset);
+ error |= rxFrame.getInt16(slope);
+ error |= rxFrame.getUInt16(timeConstant);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setWarmStartParameter(uint16_t warmStart) {
+ uint16_t error = 0;
+ uint8_t buffer[5];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60C6, buffer, 5);
+
+ error |= txFrame.addUInt16(warmStart);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getWarmStartParameter(uint16_t& warmStart) {
+ uint16_t error = 0;
+ uint8_t buffer[3];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60C6, buffer, 3);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 3);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 3,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt16(warmStart);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setVocAlgorithmTuningParameters(
+ int16_t indexOffset, int16_t learningTimeOffsetHours,
+ int16_t learningTimeGainHours, int16_t gatingMaxDurationMinutes,
+ int16_t stdInitial, int16_t gainFactor) {
+ uint16_t error = 0;
+ uint8_t buffer[20];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60D0, buffer, 20);
+
+ error |= txFrame.addInt16(indexOffset);
+ error |= txFrame.addInt16(learningTimeOffsetHours);
+ error |= txFrame.addInt16(learningTimeGainHours);
+ error |= txFrame.addInt16(gatingMaxDurationMinutes);
+ error |= txFrame.addInt16(stdInitial);
+ error |= txFrame.addInt16(gainFactor);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getVocAlgorithmTuningParameters(
+ int16_t& indexOffset, int16_t& learningTimeOffsetHours,
+ int16_t& learningTimeGainHours, int16_t& gatingMaxDurationMinutes,
+ int16_t& stdInitial, int16_t& gainFactor) {
+ uint16_t error = 0;
+ uint8_t buffer[18];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60D0, buffer, 18);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 18);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 18,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getInt16(indexOffset);
+ error |= rxFrame.getInt16(learningTimeOffsetHours);
+ error |= rxFrame.getInt16(learningTimeGainHours);
+ error |= rxFrame.getInt16(gatingMaxDurationMinutes);
+ error |= rxFrame.getInt16(stdInitial);
+ error |= rxFrame.getInt16(gainFactor);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setNoxAlgorithmTuningParameters(
+ int16_t indexOffset, int16_t learningTimeOffsetHours,
+ int16_t learningTimeGainHours, int16_t gatingMaxDurationMinutes,
+ int16_t stdInitial, int16_t gainFactor) {
+ uint16_t error = 0;
+ uint8_t buffer[20];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60E1, buffer, 20);
+
+ error |= txFrame.addInt16(indexOffset);
+ error |= txFrame.addInt16(learningTimeOffsetHours);
+ error |= txFrame.addInt16(learningTimeGainHours);
+ error |= txFrame.addInt16(gatingMaxDurationMinutes);
+ error |= txFrame.addInt16(stdInitial);
+ error |= txFrame.addInt16(gainFactor);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getNoxAlgorithmTuningParameters(
+ int16_t& indexOffset, int16_t& learningTimeOffsetHours,
+ int16_t& learningTimeGainHours, int16_t& gatingMaxDurationMinutes,
+ int16_t& stdInitial, int16_t& gainFactor) {
+ uint16_t error = 0;
+ uint8_t buffer[18];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60E1, buffer, 18);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 18);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 18,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getInt16(indexOffset);
+ error |= rxFrame.getInt16(learningTimeOffsetHours);
+ error |= rxFrame.getInt16(learningTimeGainHours);
+ error |= rxFrame.getInt16(gatingMaxDurationMinutes);
+ error |= rxFrame.getInt16(stdInitial);
+ error |= rxFrame.getInt16(gainFactor);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setRhtAccelerationMode(uint16_t mode) {
+ uint16_t error = 0;
+ uint8_t buffer[5];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60F7, buffer, 5);
+
+ error |= txFrame.addUInt16(mode);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getRhtAccelerationMode(uint16_t& mode) {
+ uint16_t error = 0;
+ uint8_t buffer[3];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x60F7, buffer, 3);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 3);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 3,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt16(mode);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setVocAlgorithmState(const uint8_t state[],
+ uint8_t stateSize) {
+ uint16_t error = 0;
+ uint8_t buffer[14];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x6181, buffer, 14);
+
+ error |= txFrame.addBytes(state, stateSize);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getVocAlgorithmState(uint8_t state[],
+ uint8_t stateSize) {
+ uint16_t error = 0;
+ uint8_t buffer[12];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x6181, buffer, 12);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 12);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 12,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getBytes(state, stateSize);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::setFanAutoCleaningInterval(uint32_t interval) {
+ uint16_t error = 0;
+ uint8_t buffer[8];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x8004, buffer, 8);
+
+ error |= txFrame.addUInt32(interval);
+
+ if (error) {
+ return error;
+ }
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(20);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getFanAutoCleaningInterval(uint32_t& interval) {
+ uint16_t error = 0;
+ uint8_t buffer[6];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0x8004, buffer, 6);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 6);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 6,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt32(interval);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getProductName(unsigned char productName[],
+ uint8_t productNameSize) {
+ uint16_t error = 0;
+ uint8_t buffer[48];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0xD014, buffer, 48);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(50);
+
+ SensirionI2CRxFrame rxFrame(buffer, 48);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 48,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getBytes(productName, productNameSize);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::getSerialNumber(unsigned char serialNumber[],
+ uint8_t serialNumberSize) {
+ uint16_t error = 0;
+ uint8_t buffer[48];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0xD033, buffer, 48);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(50);
+
+ SensirionI2CRxFrame rxFrame(buffer, 48);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 48,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getBytes(serialNumber, serialNumberSize);
+ return error;
+}
+
+uint16_t
+SensirionI2CSen5x::getVersion(uint8_t& firmwareMajor, uint8_t& firmwareMinor,
+ bool& firmwareDebug, uint8_t& hardwareMajor,
+ uint8_t& hardwareMinor, uint8_t& protocolMajor,
+ uint8_t& protocolMinor) {
+ uint16_t error = 0;
+ uint8_t buffer[12];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0xD100, buffer, 12);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 12);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 12,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt8(firmwareMajor);
+ error |= rxFrame.getUInt8(firmwareMinor);
+ error |= rxFrame.getBool(firmwareDebug);
+ error |= rxFrame.getUInt8(hardwareMajor);
+ error |= rxFrame.getUInt8(hardwareMinor);
+ error |= rxFrame.getUInt8(protocolMajor);
+ error |= rxFrame.getUInt8(protocolMinor);
+ uint8_t padding;
+ error |= rxFrame.getUInt8(padding); // remove padding
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readDeviceStatus(uint32_t& deviceStatus) {
+ uint16_t error = 0;
+ uint8_t buffer[6];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0xD206, buffer, 6);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 6);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 6,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt32(deviceStatus);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::readAndClearDeviceStatus(uint32_t& deviceStatus) {
+ uint16_t error = 0;
+ uint8_t buffer[6];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0xD210, buffer, 6);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ delay(20);
+
+ SensirionI2CRxFrame rxFrame(buffer, 6);
+ error = SensirionI2CCommunication::receiveFrame(SEN5X_I2C_ADDRESS, 6,
+ rxFrame, *_i2cBus);
+ if (error) {
+ return error;
+ }
+
+ error |= rxFrame.getUInt32(deviceStatus);
+ return error;
+}
+
+uint16_t SensirionI2CSen5x::deviceReset() {
+ uint16_t error = 0;
+ uint8_t buffer[2];
+ SensirionI2CTxFrame txFrame =
+ SensirionI2CTxFrame::createWithUInt16Command(0xD304, buffer, 2);
+
+ error = SensirionI2CCommunication::sendFrame(SEN5X_I2C_ADDRESS, txFrame,
+ *_i2cBus);
+ delay(200);
+ return error;
+}
diff --git a/lib/lib_i2c/Sensirion_I2C_SEN5X/src/SensirionI2CSen5x.h b/lib/lib_i2c/Sensirion_I2C_SEN5X/src/SensirionI2CSen5x.h
new file mode 100644
index 000000000..a572ff3fe
--- /dev/null
+++ b/lib/lib_i2c/Sensirion_I2C_SEN5X/src/SensirionI2CSen5x.h
@@ -0,0 +1,857 @@
+/*
+ * THIS FILE IS AUTOMATICALLY GENERATED
+ *
+ * I2C-Generator: 0.3.0
+ * Yaml Version: 2.1.3
+ * Template Version: 0.7.0-112-g190ecaa
+ */
+/*
+ * Copyright (c) 2021, Sensirion AG
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Sensirion AG nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SENSIRIONI2CSEN5X_H
+#define SENSIRIONI2CSEN5X_H
+
+#include
+
+#include
+
+class SensirionI2CSen5x {
+
+ public:
+ SensirionI2CSen5x();
+ /**
+ * begin() - Initializes the SensirionI2CSen5x class.
+ *
+ * @param serial Arduino stream object to be communicated with.
+ *
+ */
+ void begin(TwoWire& i2cBus);
+
+ /**
+ * startMeasurement() - Starts a continuous measurement.
+
+ * After starting the measurement, it takes some time (~1s) until the first
+ * measurement results are available. You could poll with the command
+ * 0x0202 \"Read Data Ready\" to check when the results are ready to read.
+ *
+ * This command is only available in idle mode. If the device is already
+ * in any measure mode, this command has no effect.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t startMeasurement(void);
+
+ /**
+ * startMeasurementWithoutPm() - Starts a continuous measurement without PM.
+ * Only humidity, temperature, VOC and NOx are available in this mode. Laser
+ * and fan are switched off to keep power consumption low.
+ *
+ * After starting the measurement, it takes some time (~1s) until the first
+ * measurement results are available. You could poll with the command
+ * 0x0202 \"Read Data Ready\" to check when the results are ready to read.
+ *
+ * This command is only available in idle mode. If the device is already
+ * in any measure mode, this command has no effect.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t startMeasurementWithoutPm(void);
+
+ /**
+ * stopMeasurement() - Stops the measurement and returns to idle mode.
+ *
+ * If the device is already in idle mode, this command has no effect.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t stopMeasurement(void);
+
+ /**
+ * readDataReady() - This command can be used to check if new measurement
+ * results are ready to read. The data ready flag is automatically reset
+ * after reading the measurement values with the 0x03.. \"Read Measured
+ * Values\" commands.
+ *
+ * @note During fan (auto-)cleaning, no measurement data is available for
+ * several seconds and thus this flag will not be set until cleaning has
+ * finished. So please expect gaps of several seconds at any time if fan
+ * auto-cleaning is enabled.
+ *
+ * @param padding Padding byte, always 0x00.
+ *
+ * @param dataReady True (0x01) if data is ready, False (0x00) if not. When
+ * no measurement is running, False will be returned.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readDataReady(bool& dataReady);
+
+ /**
+ * readMeasuredValues() - Returns the measured values.
+ *
+ * The command 0x0202 \"Read Data Ready\" can be used to check if new
+ * data is available since the last read operation. If no new data is
+ * available, the previous values will be returned again. If no data is
+ * available at all (e.g. measurement not running for at least one
+ * second), all values will be NAN.
+ *
+ * @param massConcentrationPm1p0 PM1.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm2p5 PM2.5 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm4p0 PM4.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm10p0 PM10.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param ambientHumidity RH [%]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param ambientTemperature T [°C]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param vocIndex VOC Index
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param noxIndex NOx Index
+ * Note: If this value is unknown, which is true for SEN54,
+ * NAN is returned. During the first 10..11 seconds after
+ * power-on or device reset, this value will be NAN as well.*
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readMeasuredValues(float& massConcentrationPm1p0,
+ float& massConcentrationPm2p5,
+ float& massConcentrationPm4p0,
+ float& massConcentrationPm10p0,
+ float& ambientHumidity,
+ float& ambientTemperature, float& vocIndex,
+ float& noxIndex);
+
+ /**
+ * readMeasuredValuesAsIntegers() - Returns the measured values
+ * without scaling factors applied.
+ *
+ * The command 0x0202 \"Read Data Ready\" can be used to check if new
+ * data is available since the last read operation. If no new data is
+ * available, the previous values will be returned again. If no data is
+ * available at all (e.g. measurement not running for at least one
+ * second), all values will be at their upper limit (0xFFFF for `uint16`,
+ * 0x7FFF for `int16`).
+ *
+ * @param massConcentrationPm1p0 Value is scaled with factor 10:
+ * PM1.0 [µg/m³] = value / 10
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param massConcentrationPm2p5 Value is scaled with factor 10:
+ * PM2.5 [µg/m³] = value / 10
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param massConcentrationPm4p0 Value is scaled with factor 10:
+ * PM4.0 [µg/m³] = value / 10
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param massConcentrationPm10p0 Value is scaled with factor 10:
+ * PM10.0 [µg/m³] = value / 10
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param ambientHumidity Value is scaled with factor 100:
+ * RH [%] = value /100
+ * Note: If this value is unknown, 0x7FFF is returned.*
+ *
+ * @param ambientTemperature Value is scaled with factor 200:
+ * T [°C] = value / 200
+ * Note: If this value is unknown, 0x7FFF is returned.*
+ *
+ * @param vocIndex Value is scaled with factor 10: VOC Index = value / 10
+ * Note: If this value is unknown, 0x7FFF is returned.*
+ *
+ * @param noxIndex Value is scaled with factor 10: NOx Index = value / 10
+ * Note: If this value is unknown, which is the case for SEN54,
+ * 0x7FFF is returned. During the first 10..11 seconds after power-on
+ * or device reset, this value will be 0x7FFF as well.*
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readMeasuredValuesAsIntegers(uint16_t& massConcentrationPm1p0,
+ uint16_t& massConcentrationPm2p5,
+ uint16_t& massConcentrationPm4p0,
+ uint16_t& massConcentrationPm10p0,
+ int16_t& ambientHumidity,
+ int16_t& ambientTemperature,
+ int16_t& vocIndex, int16_t& noxIndex);
+
+ /**
+ * readMeasuredRawValues() - Returns the measured raw values.
+ *
+ * The command 0x0202 \"Read Data Ready\" can be used to check if new
+ * data is available since the last read operation. If no new data is
+ * available, the previous values will be returned again. If no data
+ * is available at all (e.g. measurement not running for at least one
+ * second), all values will be at their upper limit (0xFFFF for `uint16`,
+ * 0x7FFF for `int16`).
+ *
+ * Supported sensors: SEN54 (no NOx), SEN55
+ *
+ * @param rawHumidity Value is scaled with factor 100: RH [%] = value / 100
+ * Note: If this value is unknown, 0x7FFF is returned.*
+ *
+ * @param rawTemperature Value is scaled with factor 200:
+ * T [°C] = value / 200
+ * Note: If this value is unknown, 0x7FFF is returned.*
+ *
+ * @param rawVoc Raw measured VOC ticks without scale factor.
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param rawNox Raw measured NOx ticks without scale factor.
+ * Note: If this value is unknown, which is the case for SEN54,
+ * 0x7FFF is returned. During the first 10..11 seconds after power-on
+ * or device reset, this value will be 0x7FFF as well.*
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readMeasuredRawValues(int16_t& rawHumidity,
+ int16_t& rawTemperature, uint16_t& rawVoc,
+ uint16_t& rawNox);
+
+ /**
+ * readMeasuredValuesSen50() - Returns the measured values for SEN50.
+ *
+ * The command 0x0202 \"Read Data Ready\" can be used to check if new
+ * data is available since the last read operation. If no new data is
+ * available, the previous values will be returned again. If no data is
+ * available at all (e.g. measurement not running for at least one
+ * second), all values will be NAN.
+ *
+ * @param massConcentrationPm1p0 PM1.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm2p5 PM2.5 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm4p0 PM4.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm10p0 PM10.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readMeasuredValuesSen50(float& massConcentrationPm1p0,
+ float& massConcentrationPm2p5,
+ float& massConcentrationPm4p0,
+ float& massConcentrationPm10p0);
+
+ /**
+ * readMeasuredPmValues() - Returns the measured particulate matter values.
+ *
+ * The command 0x0202 \"Read Data Ready\" can be used to check if new
+ * data is available since the last read operation. If no new data is
+ * available, the previous values will be returned again. If no data
+ * is available at all (e.g. measurement not running for at least one
+ * second), all values will be NAN.
+ *
+ * @param massConcentrationPm1p0 PM1.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm2p5 PM2.5 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm4p0 PM4.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param massConcentrationPm10p0 PM10.0 [µg/m³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param numberConcentrationPm0p5 PM0.5 [#/cm³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param numberConcentrationPm1p0 PM1.0 [#/cm³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param numberConcentrationPm2p5 PM2.5 [#/cm³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param numberConcentrationPm4p0 PM4.0 [#/cm³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param numberConcentrationPm10p0 PM10.0 [#/cm³]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @param typicalParticleSize Size [µm]
+ * Note: If this value is unknown, NAN is returned.*
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readMeasuredPmValues(
+ float& massConcentrationPm1p0, float& massConcentrationPm2p5,
+ float& massConcentrationPm4p0, float& massConcentrationPm10p0,
+ float& numberConcentrationPm0p5, float& numberConcentrationPm1p0,
+ float& numberConcentrationPm2p5, float& numberConcentrationPm4p0,
+ float& numberConcentrationPm10p0, float& typicalParticleSize);
+
+ /**
+ * readMeasuredPmValuesAsIntegers() - Returns the measured particulate
+ * matter values.
+ *
+ * The command 0x0202 \"Read Data Ready\" can be used to check if new
+ * data is available since the last read operation. If no new data is
+ * available, the previous values will be returned again. If no data
+ * is available at all (e.g. measurement not running for at least one
+ * second), all values will be 0xFFFF.
+ *
+ * @param massConcentrationPm1p0 Value is scaled with factor 10:
+ * PM1.0 [µg/m³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param massConcentrationPm2p5 Value is scaled with factor 10:
+ * PM2.5 [µg/m³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param massConcentrationPm4p0 Value is scaled with factor 10:
+ * PM4.0 [µg/m³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param massConcentrationPm10p0 Value is scaled with factor 10:
+ * PM10.0 [µg/m³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param numberConcentrationPm0p5 Value is scaled with factor 10:
+ * PM0.5 [#/cm³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param numberConcentrationPm1p0 Value is scaled with factor 10:
+ * PM1.0 [#/cm³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param numberConcentrationPm2p5 Value is scaled with factor 10:
+ * PM2.5 [#/cm³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param numberConcentrationPm4p0 Value is scaled with factor 10:
+ * PM4.0 [#/cm³] = value / 1
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param numberConcentrationPm10p0 Value is scaled with factor 10:
+ * PM10.0 [#/cm³] = value / 10
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @param typicalParticleSize Value is scaled with factor 1000:
+ * Size [µm] = value / 1000
+ * Note: If this value is unknown, 0xFFFF is returned.*
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readMeasuredPmValuesAsIntegers(
+ uint16_t& massConcentrationPm1p0, uint16_t& massConcentrationPm2p5,
+ uint16_t& massConcentrationPm4p0, uint16_t& massConcentrationPm10p0,
+ uint16_t& numberConcentrationPm0p5, uint16_t& numberConcentrationPm1p0,
+ uint16_t& numberConcentrationPm2p5, uint16_t& numberConcentrationPm4p0,
+ uint16_t& numberConcentrationPm10p0, uint16_t& typicalParticleSize);
+
+ /**
+ * startFanCleaning() - Starts the fan cleaning manually. The \"data
+ * ready\"-flag will be cleared immediately and during the next few seconds,
+ * no new measurement results will be available (old values will be
+ * returned). Once the cleaning is finished, the \"data ready\"-flag will be
+ * set and new measurement results will be available.
+ *
+ * When executing this command while cleaning is already active, the
+ * command does nothing.
+ *
+ * If you stop the measurement while fan cleaning is active, the cleaning
+ * will be aborted immediately.
+ *
+ * @note This command is only available in measure mode with PM measurement
+ * enabled, i.e. only if the fan is already running. In any other state,
+ * this command does nothing.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t startFanCleaning(void);
+
+ /**
+ * setTemperatureOffsetSimple() - Sets the temperature offset parameter
+ * in degrees celsius for the device, while leaving the other parameters at
+ * their default setting.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param tempOffset Constant temperature offset in degrees celsius.
+ * The default value is 0.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setTemperatureOffsetSimple(float tempOffset);
+
+ /**
+ * getTemperatureOffsetSimple() - Gets the temperature offset parameter
+ * in degrees celsius from the device.
+ * @note The other parameters, such as slope and time constant may differ
+ * from the default values, if they were previously set using
+ * `setTemperatureOffsetParameters`.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param tempOffset Constant temperature offset in degrees celsius.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getTemperatureOffsetSimple(float& tempOffset);
+
+ /**
+ * setTemperatureOffsetParameters() - Sets the temperature offset parameters
+ * for the device.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param tempOffset Constant temperature offset scaled with factor 200 (T
+ * [°C] = value / 200). The default value is 0.
+ *
+ * @param slope Normalized temperature offset slope scaled with factor 10000
+ * (applied factor = value / 10000). The default value is 0.
+ *
+ * @param timeConstant Time constant [s] how fast the new slope and offset
+ * will be applied. After the specified value in seconds, 63% of the new
+ * slope and offset are applied. A time constant of zero means the new
+ * values will be applied immediately (within the next measure interval of 1
+ * second).
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setTemperatureOffsetParameters(int16_t tempOffset, int16_t slope,
+ uint16_t timeConstant);
+
+ /**
+ * getTemperatureOffsetParameters() - Gets the temperature offset parameters
+ * from the device.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param tempOffset Constant temperature offset scaled with factor 200 (T
+ * [°C] = value / 200).
+ *
+ * @param slope Normalized temperature offset slope scaled with factor 10000
+ * (applied factor = value / 10000).
+ *
+ * @param timeConstant Time constant [s] how fast the slope and offset are
+ * applied. After the specified value in seconds, 63% of the new slope and
+ * offset are applied.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getTemperatureOffsetParameters(int16_t& tempOffset, int16_t& slope,
+ uint16_t& timeConstant);
+
+ /**
+ * setWarmStartParameter() - Sets the warm start parameter for the device.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @note This parameter can be changed in any state of the device (and the
+ * getter immediately returns the new value), but it is applied only the
+ * next time starting a measurement, i.e. when sending a \"Start
+ * Measurement\" command! So the parameter needs to be set *before* a
+ * warm-start measurement is started.
+ *
+ * @param warmStart Warm start behavior as a value in the range from 0 (cold
+ * start) to 65535 (warm start). The default value is 0.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setWarmStartParameter(uint16_t warmStart);
+
+ /**
+ * getWarmStartParameter() - Gets the warm start parameter from the device.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param warmStart Warm start behavior as a value in the range from 0 (cold
+ * start) to 65535 (warm start).
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getWarmStartParameter(uint16_t& warmStart);
+
+ /**
+ * setVocAlgorithmTuningParameters() - Sets the tuning parameters of the VOC
+ * algorithm.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @note This command is available only in idle mode. In measure mode, this
+ * command has no effect. In addition, it has no effect if at least one
+ * parameter is outside the specified range.
+ *
+ * @param indexOffset VOC index representing typical (average) conditions.
+ * Allowed values are in range 1..250. The default value is 100.
+ *
+ * @param learningTimeOffsetHours Time constant to estimate the VOC
+ * algorithm offset from the history in hours. Past events will be forgotten
+ * after about twice the learning time. Allowed values are in range 1..1000.
+ * The default value is 12 hours.
+ *
+ * @param learningTimeGainHours Time constant to estimate the VOC algorithm
+ * gain from the history in hours. Past events will be forgotten after about
+ * twice the learning time. Allowed values are in range 1..1000. The default
+ * value is 12 hours.
+ *
+ * @param gatingMaxDurationMinutes Maximum duration of gating in minutes
+ * (freeze of estimator during high VOC index signal). Set to zero to
+ * disable the gating. Allowed values are in range 0..3000. The default
+ * value is 180 minutes.
+ *
+ * @param stdInitial Initial estimate for standard deviation. Lower value
+ * boosts events during initial learning period, but may result in larger
+ * device-to-device variations. Allowed values are in range 10..5000. The
+ * default value is 50.
+ *
+ * @param gainFactor Gain factor to amplify or to attenuate the VOC index
+ * output. Allowed values are in range 1..1000. The default value is 230.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setVocAlgorithmTuningParameters(int16_t indexOffset,
+ int16_t learningTimeOffsetHours,
+ int16_t learningTimeGainHours,
+ int16_t gatingMaxDurationMinutes,
+ int16_t stdInitial,
+ int16_t gainFactor);
+
+ /**
+ * getVocAlgorithmTuningParameters() - Gets the currently set tuning
+ * parameters of the VOC algorithm.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param indexOffset VOC index representing typical (average) conditions.
+ *
+ * @param learningTimeOffsetHours Time constant to estimate the VOC
+ * algorithm offset from the history in hours. Past events will be forgotten
+ * after about twice the learning time.
+ *
+ * @param learningTimeGainHours Time constant to estimate the VOC algorithm
+ * gain from the history in hours. Past events will be forgotten after about
+ * twice the learning time.
+ *
+ * @param gatingMaxDurationMinutes Maximum duration of gating in minutes
+ * (freeze of estimator during high VOC index signal). Zero disables the
+ * gating.
+ *
+ * @param stdInitial Initial estimate for standard deviation. Lower value
+ * boosts events during initial learning period, but may result in larger
+ * device-to-device variations.
+ *
+ * @param gainFactor Gain factor to amplify or to attenuate the VOC index
+ * output.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getVocAlgorithmTuningParameters(int16_t& indexOffset,
+ int16_t& learningTimeOffsetHours,
+ int16_t& learningTimeGainHours,
+ int16_t& gatingMaxDurationMinutes,
+ int16_t& stdInitial,
+ int16_t& gainFactor);
+
+ /**
+ * setNoxAlgorithmTuningParameters() - Sets the tuning parameters of the NOx
+ * algorithm.
+ *
+ * Supported sensors: SEN55
+ *
+ * @note This command is available only in idle mode. In measure mode, this
+ * command has no effect. In addition, it has no effect if at least one
+ * parameter is outside the specified range.
+ *
+ * @param indexOffset NOx index representing typical (average) conditions.
+ * Allowed values are in range 1..250. The default value is 1.
+ *
+ * @param learningTimeOffsetHours Time constant to estimate the NOx
+ * algorithm offset from the history in hours. Past events will be forgotten
+ * after about twice the learning time. Allowed values are in range 1..1000.
+ * The default value is 12 hours.
+ *
+ * @param learningTimeGainHours The time constant to estimate the NOx
+ * algorithm gain from the history has no impact for NOx. This parameter is
+ * still in place for consistency reasons with the VOC tuning parameters
+ * command. This parameter must always be set to 12 hours.
+ *
+ * @param gatingMaxDurationMinutes Maximum duration of gating in minutes
+ * (freeze of estimator during high NOx index signal). Set to zero to
+ * disable the gating. Allowed values are in range 0..3000. The default
+ * value is 720 minutes.
+ *
+ * @param stdInitial The initial estimate for standard deviation parameter
+ * has no impact for NOx. This parameter is still in place for consistency
+ * reasons with the VOC tuning parameters command. This parameter must
+ * always be set to 50.
+ *
+ * @param gainFactor Gain factor to amplify or to attenuate the NOx index
+ * output. Allowed values are in range 1..1000. The default value is 230.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setNoxAlgorithmTuningParameters(int16_t indexOffset,
+ int16_t learningTimeOffsetHours,
+ int16_t learningTimeGainHours,
+ int16_t gatingMaxDurationMinutes,
+ int16_t stdInitial,
+ int16_t gainFactor);
+
+ /**
+ * getNoxAlgorithmTuningParameters() - Gets the currently set tuning
+ * parameters of the NOx algorithm.
+ *
+ * Supported sensors: SEN55
+ *
+ * @param indexOffset NOx index representing typical (average) conditions.
+ *
+ * @param learningTimeOffsetHours Time constant to estimate the NOx
+ * algorithm offset from the history in hours. Past events will be forgotten
+ * after about twice the learning time.
+ *
+ * @param learningTimeGainHours The time constant to estimate the NOx
+ * algorithm gain from the history has no impact for NOx. This parameter is
+ * still in place for consistency reasons with the VOC tuning parameters
+ * command.
+ *
+ * @param gatingMaxDurationMinutes Maximum duration of gating in minutes
+ * (freeze of estimator during high NOx index signal). Zero disables the
+ * gating.
+ *
+ * @param stdInitial The initial estimate for standard deviation has no
+ * impact for NOx. This parameter is still in place for consistency reasons
+ * with the VOC tuning parameters command.
+ *
+ * @param gainFactor Gain factor to amplify or to attenuate the NOx index
+ * output.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getNoxAlgorithmTuningParameters(int16_t& indexOffset,
+ int16_t& learningTimeOffsetHours,
+ int16_t& learningTimeGainHours,
+ int16_t& gatingMaxDurationMinutes,
+ int16_t& stdInitial,
+ int16_t& gainFactor);
+
+ /**
+ * setRhtAccelerationMode() - Sets the RH/T acceleration mode.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @note This parameter can be changed in any state of the device (and the
+ * getter immediately returns the new value), but it is applied only the
+ * next time starting a measurement, i.e. when sending a \"Start
+ * Measurement\" command. So the parameter needs to be set *before* a new
+ * measurement is started.
+ *
+ * @param mode The new RH/T acceleration mode.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setRhtAccelerationMode(uint16_t mode);
+
+ /**
+ * getRhtAccelerationMode() - Gets the RH/T acceleration mode.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param mode The current RH/T acceleration mode.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getRhtAccelerationMode(uint16_t& mode);
+
+ /**
+ * setVocAlgorithmState() - Sets the VOC algorithm state previously received
+ * with the \"Get VOC Algorithm State\" command.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @note This command is only available in idle mode and the state will be
+ * applied only once when starting the next measurement. Any further
+ * measurements (i.e. when stopping and restarting the measure mode) will
+ * reset the state to initial values. In measure mode, this command has no
+ * effect.
+ *
+ * @param state VOC algorithm state to restore.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setVocAlgorithmState(const uint8_t state[], uint8_t stateSize);
+
+ /**
+ * getVocAlgorithmState() - Gets the current VOC algorithm state. This data
+ * can be used to restore the state with the \"Set VOC Algorithm State\"
+ * command after a short power cycle or device reset.
+ *
+ * This command can be used either in measure mode or in idle mode
+ * (which will then return the state at the time when the measurement
+ * was stopped). In measure mode, the state can be read each measure
+ * interval to always have the latest state available, even in case of
+ * a sudden power loss.
+ *
+ * Supported sensors: SEN54, SEN55
+ *
+ * @param state Current VOC algorithm state.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getVocAlgorithmState(uint8_t state[], uint8_t stateSize);
+
+ /**
+ * setFanAutoCleaningInterval() - Sets the fan auto cleaning interval for
+ * the device.
+ *
+ * @param interval Fan auto cleaning interval [s]. Set to zero to disable
+ * auto cleaning.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t setFanAutoCleaningInterval(uint32_t interval);
+
+ /**
+ * getFanAutoCleaningInterval() - Gets the fan auto cleaning interval from
+ * the device.
+ *
+ * @param interval Fan auto cleaning interval [s]. Zero means auto cleaning
+ * is disabled.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getFanAutoCleaningInterval(uint32_t& interval);
+
+ /**
+ * getProductName() - Gets the product name from the device.
+ *
+ * @param productName Null-terminated ASCII string containing the product
+ * name. Up to 32 characters can be read from the device.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getProductName(unsigned char productName[],
+ uint8_t productNameSize);
+
+ /**
+ * getSerialNumber() - Gets the serial number from the device.
+ *
+ * @param serialNumber Null-terminated ASCII string containing the serial
+ * number. Up to 32 characters can be read from the device.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getSerialNumber(unsigned char serialNumber[],
+ uint8_t serialNumberSize);
+
+ /**
+ * getVersion() - Gets the version information for the hardware, firmware
+ * and communication protocol.
+ *
+ * @param firmwareMajor Firmware major version number.
+ *
+ * @param firmwareMinor Firmware minor version number.
+ *
+ * @param firmwareDebug Firmware debug state. If the debug state is set, the
+ * firmware is in development.
+ *
+ * @param hardwareMajor Hardware major version number.
+ *
+ * @param hardwareMinor Hardware minor version number.
+ *
+ * @param protocolMajor Protocol major version number.
+ *
+ * @param protocolMinor Protocol minor version number.
+ *
+ * @param padding Padding byte, ignore this.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t getVersion(uint8_t& firmwareMajor, uint8_t& firmwareMinor,
+ bool& firmwareDebug, uint8_t& hardwareMajor,
+ uint8_t& hardwareMinor, uint8_t& protocolMajor,
+ uint8_t& protocolMinor);
+
+ /**
+ * readDeviceStatus() - Reads the current device status.
+ *
+ * Use this command to get detailed information about the device status.
+ * The device status is encoded in flags. Each device status flag
+ * represents a single bit in a 32-bit integer value. If more than one
+ * error is present, the device status register value is the sum of the
+ * corresponding flag values. For details about the available flags,
+ * refer to the device status flags documentation.
+ *
+ * @note The status flags of type \"Error\" are sticky, i.e. they are not
+ * cleared automatically even if the error condition no longer exists. So
+ * they can only be cleared manually with the command 0xD210 \"Read And
+ * Clear Device Status\" or with a device reset. All other flags are not
+ * sticky, i.e. they are cleared automatically if the trigger condition
+ * disappears.
+ *
+ * @param deviceStatus Device status (32 flags as an integer value). For
+ * details, please refer to the device status flags documentation.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readDeviceStatus(uint32_t& deviceStatus);
+
+ /**
+ * readAndClearDeviceStatus() - Reads the current device status (like
+ * command 0xD206 \"Read Device Status\") and afterwards clears all flags.
+ *
+ * @param deviceStatus Device status (32 flags as an integer value)
+ * **before** clearing it. For details, please refer to the device status
+ * flags documentation.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t readAndClearDeviceStatus(uint32_t& deviceStatus);
+
+ /**
+ * deviceReset() - Executes a reset on the device. This has the same effect
+ * as a power cycle.
+ *
+ * @return 0 on success, an error code otherwise
+ */
+ uint16_t deviceReset(void);
+
+ private:
+ TwoWire* _i2cBus = nullptr;
+};
+
+#endif /* SENSIRIONI2CSEN5X_H */
diff --git a/tasmota/include/tasmota_configurations.h b/tasmota/include/tasmota_configurations.h
index 1ed060c54..bc572e890 100644
--- a/tasmota/include/tasmota_configurations.h
+++ b/tasmota/include/tasmota_configurations.h
@@ -98,6 +98,7 @@
#define USE_MGS // [I2cDriver17] Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code)
#define USE_SGP30 // [I2cDriver18] Enable SGP30 sensor (I2C address 0x58) (+1k1 code)
#define USE_SGP40 // [I2cDriver69] Enable SGP40 sensor (I2C address 0x59) (+1k4 code)
+#define USE_SEN5X // [I2cDriver76] Enable SEN5X sensor (I2C address 0x69) (+3k code)
//#define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
#define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
//#define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
diff --git a/tasmota/include/tasmota_configurations_ESP32.h b/tasmota/include/tasmota_configurations_ESP32.h
index 9360a109c..673c70d49 100644
--- a/tasmota/include/tasmota_configurations_ESP32.h
+++ b/tasmota/include/tasmota_configurations_ESP32.h
@@ -369,6 +369,7 @@
//#define USE_MGS // [I2cDriver17] Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code)
//#define USE_SGP30 // [I2cDriver18] Enable SGP30 sensor (I2C address 0x58) (+1k1 code)
//#define USE_SGP40 // [I2cDriver69] Enable SGP40 sensor (I2C address 0x59) (+1k4 code)
+//#define USE_SEN5X // [I2cDriver76] Enable SEN5X sensor (I2C address 0x69) (+3k code)
//#define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
//#define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
//#define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
@@ -586,6 +587,7 @@
#define USE_MGS // [I2cDriver17] Enable Xadow and Grove Mutichannel Gas sensor using library Multichannel_Gas_Sensor (+10k code)
#define USE_SGP30 // [I2cDriver18] Enable SGP30 sensor (I2C address 0x58) (+1k1 code)
#define USE_SGP40 // [I2cDriver69] Enable SGP40 sensor (I2C address 0x59) (+1k4 code)
+#define USE_SEN5X // [I2cDriver76] Enable SEN5X sensor (I2C address 0x69) (+3k code)
//#define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
#define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
//#define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 6eee06d05..64b040afc 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -619,6 +619,7 @@
#define MGS_SENSOR_ADDR 0x04 // Default Mutichannel Gas sensor i2c address
// #define USE_SGP30 // [I2cDriver18] Enable SGP30 sensor (I2C address 0x58) (+1k1 code)
// #define USE_SGP40 // [I2cDriver69] Enable SGP40 sensor (I2C address 0x59) (+1k4 code)
+// #define USE_SEN5X // [I2cDriver76] Enable SEN5X sensor (I2C address 0x69) (+3k code)
// #define USE_SI1145 // [I2cDriver19] Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
// #define USE_LM75AD // [I2cDriver20] Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
// #define USE_APDS9960 // [I2cDriver21] Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
diff --git a/tasmota/tasmota_support/support_features.ino b/tasmota/tasmota_support/support_features.ino
index 8f52b111e..c1e816293 100644
--- a/tasmota/tasmota_support/support_features.ino
+++ b/tasmota/tasmota_support/support_features.ino
@@ -870,8 +870,9 @@ void ResponseAppendFeatures(void)
#ifdef USE_TUYAMCUBR
feature9 |= 0x00004000; // xdrv_65_tuyamcubr.ino
#endif
-
-// feature9 |= 0x00008000;
+#if defined(USE_I2C) && defined(USE_SEN5X)
+ feature9 |= 0x00008000; // xsns_103_sen5x.ino
+#endif
// feature9 |= 0x00010000;
// feature9 |= 0x00020000;
diff --git a/tasmota/tasmota_xsns_sensor/xsns_103_sen5x.ino b/tasmota/tasmota_xsns_sensor/xsns_103_sen5x.ino
new file mode 100644
index 000000000..720550137
--- /dev/null
+++ b/tasmota/tasmota_xsns_sensor/xsns_103_sen5x.ino
@@ -0,0 +1,312 @@
+/*
+ xsns_103_sen5x.ino - SEN5X gas and air quality sensor support for Tasmota
+
+ Copyright (C) 2022 Tyeth Gundry
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#ifdef USE_I2C
+#ifdef USE_SEN5X
+/*********************************************************************************************\
+ * SEN5X - Gas (VOC - Volatile Organic Compounds / NOx - Nitrous Oxides) and Particulates (PPM)
+ *
+ * Source: Sensirion SEN5X Driver + Example, and Tasmota Driver 98 by Jean-Pierre Deschamps
+ * Adaption for TASMOTA: Tyeth Gundry
+ *
+ * I2C Address: 0x59
+\*********************************************************************************************/
+
+#define XSNS_103 103
+#define XI2C_76 76 // See I2CDEVICES.md
+
+#define SEN5X_ADDRESS 0x69
+
+#include
+#include
+SensirionI2CSen5x *sen5x = nullptr;
+
+struct SEN5XDATA_s {
+ bool sen5x_ready;
+ float abshum;
+ float massConcentrationPm1p0;
+ float massConcentrationPm2p5;
+ float massConcentrationPm4p0;
+ float massConcentrationPm10p0;
+ float ambientHumidity;
+ float ambientTemperature;
+ float vocIndex;
+ float noxIndex;
+} *SEN5XDATA = nullptr;
+/********************************************************************************************/
+
+void sen5x_Init(void)
+{
+ if(!TasmotaGlobal.i2c_enabled){
+ DEBUG_SENSOR_LOG(PSTR("I2C Not enabled, so not loading SEN5X driver."));
+ return;
+ }
+ int usingI2cBus = 0;
+#ifdef ESP32
+ if (!I2cSetDevice(SEN5X_ADDRESS, 0))
+ {
+ DEBUG_SENSOR_LOG(PSTR("Sensirion SEN5X not found, i2c bus 0"));
+ if (TasmotaGlobal.i2c_enabled_2 ){
+
+ if(!I2cSetDevice(SEN5X_ADDRESS, 1)){
+ DEBUG_SENSOR_LOG(PSTR("Sensirion SEN5X not found, i2c bus 1"));
+ return;
+ }
+ usingI2cBus = 1;
+ }
+ else {
+ return;
+ }
+ }
+#else
+ if (!I2cSetDevice(SEN5X_ADDRESS))
+ {
+ DEBUG_SENSOR_LOG(PSTR("Sensirion SEN5X not found, i2c bus 0"));
+ return;
+ }
+#endif
+ if (SEN5XDATA == nullptr)
+ SEN5XDATA = (SEN5XDATA_s *)calloc(1, sizeof(struct SEN5XDATA_s));
+ SEN5XDATA->sen5x_ready = false;
+ if(sen5x == nullptr) sen5x = new SensirionI2CSen5x();
+ if(usingI2cBus==1){
+#ifdef ESP32
+ sen5x->begin(Wire1);
+#else
+ sen5x->begin(Wire);
+#endif
+ }
+ else {
+ sen5x->begin(Wire);
+ }
+ int error_stop = sen5x->deviceReset();
+ if (error_stop != 0)
+ {
+ DEBUG_SENSOR_LOG(PSTR("Sensirion SEN5X failed to reset device (I2C Bus %d)"), usingI2cBus);
+ return;
+ }
+ // Wait 1 second for sensors to start recording + 100ms for reset command
+ delay(1100);
+ int error_start = sen5x->startMeasurement();
+ if (error_start != 0)
+ {
+ DEBUG_SENSOR_LOG(PSTR("Sensirion SEN5X failed to start measurement (I2C Bus %d)"), usingI2cBus);
+ return;
+ }
+ SEN5XDATA->sen5x_ready = true;
+ I2cSetActiveFound(SEN5X_ADDRESS, "SEN5X", usingI2cBus);
+ DEBUG_SENSOR_LOG(PSTR("Sensirion SEN5X found, i2c bus %d"), usingI2cBus);
+}
+
+// #define POW_FUNC pow
+#define POW_FUNC FastPrecisePow
+
+float sen5x_AbsoluteHumidity(float temperature, float humidity)
+{
+ // taken from https://carnotcycle.wordpress.com/2012/08/04/how-to-convert-relative-humidity-to-absolute-humidity/
+ // precision is about 0.1°C in range -30 to 35°C
+ // August-Roche-Magnus 6.1094 exp(17.625 x T)/(T + 243.04)
+ // Buck (1981) 6.1121 exp(17.502 x T)/(T + 240.97)
+ // reference https://www.eas.ualberta.ca/jdwilson/EAS372_13/Vomel_CIRES_satvpformulae.html
+ float temp = NAN;
+ const float mw = 18.01534f; // molar mass of water g/mol
+ const float r = 8.31447215f; // Universal gas constant J/mol/K
+
+ if (isnan(temperature) || isnan(humidity))
+ {
+ return NAN;
+ }
+
+ temp = POW_FUNC(2.718281828f, (17.67f * temperature) / (temperature + 243.5f));
+
+ // return (6.112 * temp * humidity * 2.1674) / (273.15 + temperature); //simplified version
+ return (6.112f * temp * humidity * mw) / ((273.15f + temperature) * r); // long version
+}
+
+#define SAVE_PERIOD 30
+
+void SEN5XUpdate(void) // Perform every second to ensure proper operation of the baseline compensation algorithm
+{
+ uint16_t error;
+ char errorMessage[256];
+ DEBUG_SENSOR_LOG(PSTR("Running readMeasuredValues for SEN5X..."));
+
+ error = sen5x->readMeasuredValues(
+ SEN5XDATA->massConcentrationPm1p0, SEN5XDATA->massConcentrationPm2p5, SEN5XDATA->massConcentrationPm4p0,
+ SEN5XDATA->massConcentrationPm10p0, SEN5XDATA->ambientHumidity, SEN5XDATA->ambientTemperature, SEN5XDATA->vocIndex,
+ SEN5XDATA->noxIndex);
+
+ if (error)
+ {
+ AddLog(LOG_LEVEL_DEBUG, PSTR("Failed to retrieve SEN5X readings."));
+ #ifdef DEBUG_TASMOTA_SENSOR
+ DEBUG_SENSOR_LOG(PSTR("Error trying to execute readMeasuredValues(): \n"));
+ errorToString(error, errorMessage, 256);
+ DEBUG_SENSOR_LOG(errorMessage);
+ #endif
+ }
+ else
+ {
+#ifdef DEBUG_TASMOTA_SENSOR
+ DEBUG_SENSOR_LOG(PSTR("SEN5x readings:-"));
+ DEBUG_SENSOR_LOG(PSTR("MassConcentrationPm1p0: %f\n"), SEN5XDATA->massConcentrationPm1p0);
+ DEBUG_SENSOR_LOG(PSTR("MassConcentrationPm2p5: %f\n"), SEN5XDATA->massConcentrationPm2p5);
+ DEBUG_SENSOR_LOG(PSTR("MassConcentrationPm4p0: %f\n"), SEN5XDATA->massConcentrationPm4p0);
+ DEBUG_SENSOR_LOG(PSTR("MassConcentrationPm10p0: %f\n"), SEN5XDATA->massConcentrationPm10p0);
+ if (isnan(SEN5XDATA->ambientHumidity))
+ {
+ DEBUG_SENSOR_LOG(PSTR("AmbientHumidity: n/a\n"));
+ }
+ else
+ {
+ DEBUG_SENSOR_LOG(PSTR("AmbientHumidity: %f\n"), SEN5XDATA->ambientHumidity);
+ }
+
+ if (isnan(SEN5XDATA->ambientTemperature))
+ {
+ DEBUG_SENSOR_LOG(PSTR("AmbientTemperature: n/a\n"));
+ }
+ else
+ {
+ DEBUG_SENSOR_LOG(PSTR("AmbientTemperature: %f\n"), SEN5XDATA->ambientTemperature);
+ }
+
+ if (isnan(SEN5XDATA->vocIndex))
+ {
+ DEBUG_SENSOR_LOG(PSTR("VocIndex: n/a\n"));
+ }
+ else
+ {
+ DEBUG_SENSOR_LOG(PSTR("VocIndex: %f\n"), SEN5XDATA->vocIndex);
+ }
+
+ if (isnan(SEN5XDATA->noxIndex))
+ {
+ DEBUG_SENSOR_LOG(PSTR("NoxIndex: n/a\n"));
+ }
+ else
+ {
+ DEBUG_SENSOR_LOG(PSTR("NoxIndex: %f\n"), SEN5XDATA->noxIndex);
+ }
+#endif
+ }
+ if (!isnan(SEN5XDATA->ambientTemperature) && SEN5XDATA->ambientHumidity > 0) {
+ SEN5XDATA->abshum = sen5x_AbsoluteHumidity(SEN5XDATA->ambientTemperature, SEN5XDATA->ambientHumidity);
+ DEBUG_SENSOR_LOG(PSTR("AbsoluteHumidity: %f\n"), SEN5XDATA->abshum);
+ }
+}
+
+#ifdef USE_WEBSERVER
+const char HTTP_SNS_SEN5X_UNITS[] PROGMEM = "{s}SEN5X %s{m}%.*f %s{e}";
+const char HTTP_SNS_SEN5X_UNITLESS[] PROGMEM = "{s}SEN5X %s{m}%.*f{e}";
+// {s} = , {m} = | , {e} = |
+const char HTTP_SNS_AHUMSEN5X[] PROGMEM = "{s}SEN5X Abs Humidity{m}%s g/m³{e}";
+#endif
+
+#define D_JSON_AHUM "aHumidity"
+
+void SEN5XShow(bool json)
+{
+ if (SEN5XDATA->sen5x_ready)
+ {
+ char sen5x_abs_hum[33];
+ bool ahum_available = !isnan(SEN5XDATA->ambientTemperature) && (SEN5XDATA->ambientHumidity > 0);
+ if (ahum_available)
+ {
+ // has humidity + temperature
+ dtostrfd(SEN5XDATA->abshum, 4, sen5x_abs_hum);
+ }
+ if (json)
+ {
+ ResponseAppend_P(PSTR(",\"SEN5X\":{"));
+ ResponseAppend_P(PSTR("\"PM1\":%.1f,"), SEN5XDATA->massConcentrationPm1p0);
+ ResponseAppend_P(PSTR("\"PM2.5\":%.1f,"), SEN5XDATA->massConcentrationPm2p5);
+ ResponseAppend_P(PSTR("\"PM4\":%.1f,"), SEN5XDATA->massConcentrationPm4p0);
+ ResponseAppend_P(PSTR("\"PM10\":%.1f,"), SEN5XDATA->massConcentrationPm10p0);
+ if (!isnan(SEN5XDATA->noxIndex))
+ ResponseAppend_P(PSTR("\"NOx\":%.0f,"), SEN5XDATA->noxIndex);
+ if (!isnan(SEN5XDATA->vocIndex))
+ ResponseAppend_P(PSTR("\"VOC\":%.0f,"), SEN5XDATA->vocIndex);
+ if (!isnan(SEN5XDATA->ambientTemperature))
+ ResponseAppendTHD(SEN5XDATA->ambientTemperature, SEN5XDATA->ambientHumidity);
+ if (ahum_available)
+ ResponseAppend_P(PSTR(",\"" D_JSON_AHUM "\":%s"), sen5x_abs_hum);
+ ResponseJsonEnd();
+ }
+
+#ifdef USE_WEBSERVER
+
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITS, "PM1", 1, SEN5XDATA->massConcentrationPm1p0, "μg/m³");
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITS, "PM2.5", 1, SEN5XDATA->massConcentrationPm2p5, "μg/m³");
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITS, "PM4", 1, SEN5XDATA->massConcentrationPm4p0, "μg/m³");
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITS, "PM10", 1, SEN5XDATA->massConcentrationPm10p0, "μg/m³");
+ if (!isnan(SEN5XDATA->noxIndex))
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITLESS, "NOx", 0, SEN5XDATA->noxIndex);
+ if (!isnan(SEN5XDATA->vocIndex))
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITLESS, "VOC", 0, SEN5XDATA->vocIndex);
+ if (!isnan(SEN5XDATA->ambientTemperature))
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITS, "Temperature", 2, SEN5XDATA->ambientTemperature, "°C");
+ if (!isnan(SEN5XDATA->ambientHumidity))
+ WSContentSend_PD(HTTP_SNS_SEN5X_UNITS, "Humidity", 2, SEN5XDATA->ambientHumidity, "%RH");
+ if (ahum_available)
+ WSContentSend_PD(HTTP_SNS_AHUMSEN5X, sen5x_abs_hum);
+
+#endif
+ }
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xsns103(uint32_t function)
+{
+ if (!I2cEnabled(XI2C_76))
+ {
+ return false;
+ }
+
+ bool result = false;
+
+ if (FUNC_INIT == function)
+ {
+ sen5x_Init();
+ }
+ else if (SEN5XDATA != nullptr)
+ {
+ switch (function)
+ {
+ case FUNC_EVERY_SECOND:
+ SEN5XUpdate();
+ break;
+ case FUNC_JSON_APPEND:
+ SEN5XShow(1);
+ break;
+#ifdef USE_WEBSERVER
+ case FUNC_WEB_SENSOR:
+ SEN5XShow(0);
+ break;
+#endif // USE_WEBSERVER
+ }
+ }
+ return result;
+}
+
+#endif // USE_SEN5X
+#endif // USE_I2C
diff --git a/tools/decode-status.py b/tools/decode-status.py
index 843a2d0e8..567d54d90 100755
--- a/tools/decode-status.py
+++ b/tools/decode-status.py
@@ -290,7 +290,7 @@ a_features = [[
"USE_SGP40","USE_LUXV30B","USE_CANSNIFFER","USE_QMC5883L",
"USE_MODBUS_ENERGY","USE_SHELLY_PRO","USE_DALI","USE_BP1658CJ",
"USE_DINGTIAN_RELAY","USE_HMC5883L","USE_LD2410","USE_ME007",
- "USE_DISPLAY_TM1650","USE_PCA9632","USE_TUYAMCUBR","",
+ "USE_DISPLAY_TM1650","USE_PCA9632","USE_TUYAMCUBR","USE_SEN5X",
"","","","",
"","","","",
"","","","",