Add SEN5X to I2C devices (#17736)

This commit is contained in:
Tyeth Gundry 2023-01-29 07:06:25 +00:00 committed by GitHub
parent 9e522e8fa3
commit 2ed602057c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 5133 additions and 3 deletions

View File

@ -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 | - | - / - | - | - | - | - |

View File

@ -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 |
| |

View File

@ -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)

View File

@ -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']
...

View File

@ -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 <https://keepachangelog.com/en/1.0.0/>`_
and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0.html>`_.
`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

View File

@ -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.

View File

@ -0,0 +1,139 @@
<!-- Downloaded from https://github.com/Sensirion/arduino-core
on 10/01/2023 at commit bd2d3ce9355a3a1997beeb820e59072ef9430d5e -->
# 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 Sensirions 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);
```

View File

@ -0,0 +1,62 @@
#include <SensirionCore.h>
#include <Wire.h>
#include <stdint.h>
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);
}

View File

@ -0,0 +1,73 @@
#include <SensirionCore.h>
#include <stdint.h>
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.
}
}

View File

@ -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)
#######################################

View File

@ -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

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
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_ */

View File

@ -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 <stdint.h>
#include <stdio.h>
#include <string.h>
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;
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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<uint8_t>(I2C_BUFFER_LENGTH) / static_cast<uint8_t>(3)) * 3;
#elif defined(BUFFER_LENGTH)
const uint8_t sizeBuffer =
(static_cast<uint8_t>(BUFFER_LENGTH) / static_cast<uint8_t>(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<uint8_t>(numBytes),
static_cast<uint8_t>(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;
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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<uint8_t>((command & 0xFF00) >> 8);
instance._buffer[1] = static_cast<uint8_t>((command & 0x00FF) >> 0);
return instance;
}
uint16_t SensirionI2CTxFrame::addCommand(uint16_t command) {
if (_bufferSize < 2) {
return TxFrameError | BufferSizeError;
}
_buffer[0] = static_cast<uint8_t>((command & 0xFF00) >> 8);
_buffer[1] = static_cast<uint8_t>((command & 0x00FF) >> 0);
return NoError;
}
uint16_t SensirionI2CTxFrame::addUInt32(uint32_t data) {
uint16_t error = _addByte(static_cast<uint8_t>((data & 0xFF000000) >> 24));
error |= _addByte(static_cast<uint8_t>((data & 0x00FF0000) >> 16));
error |= _addByte(static_cast<uint8_t>((data & 0x0000FF00) >> 8));
error |= _addByte(static_cast<uint8_t>((data & 0x000000FF) >> 0));
return error;
}
uint16_t SensirionI2CTxFrame::addInt32(int32_t data) {
return addUInt32(static_cast<uint32_t>(data));
}
uint16_t SensirionI2CTxFrame::addUInt16(uint16_t data) {
uint16_t error = _addByte(static_cast<uint8_t>((data & 0xFF00) >> 8));
error |= _addByte(static_cast<uint8_t>((data & 0x00FF) >> 0));
return error;
}
uint16_t SensirionI2CTxFrame::addInt16(int16_t data) {
return addUInt16(static_cast<uint16_t>(data));
}
uint16_t SensirionI2CTxFrame::addUInt8(uint8_t data) {
return _addByte(data);
}
uint16_t SensirionI2CTxFrame::addInt8(int8_t data) {
return _addByte(static_cast<uint8_t>(data));
}
uint16_t SensirionI2CTxFrame::addBool(bool data) {
return _addByte(static_cast<uint8_t>(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;
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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<uint32_t>(_buffer[_index++]) << 24;
data |= static_cast<uint32_t>(_buffer[_index++]) << 16;
data |= static_cast<uint32_t>(_buffer[_index++]) << 8;
data |= static_cast<uint32_t>(_buffer[_index++]);
_numBytes -= 4;
return NoError;
}
uint16_t SensirionRxFrame::getInt32(int32_t& data) {
uint32_t ret;
uint16_t error = getUInt32(ret);
data = static_cast<int32_t>(ret);
return error;
}
uint16_t SensirionRxFrame::getUInt16(uint16_t& data) {
if (_numBytes < 2) {
return RxFrameError | NoDataError;
}
data = static_cast<uint16_t>(_buffer[_index++]) << 8;
data |= static_cast<uint16_t>(_buffer[_index++]);
_numBytes -= 2;
return NoError;
}
uint16_t SensirionRxFrame::getInt16(int16_t& data) {
uint16_t ret;
uint16_t error = getUInt16(ret);
data = static_cast<int16_t>(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<int8_t>(_buffer[_index++]);
_numBytes -= 1;
return NoError;
}
uint16_t SensirionRxFrame::getBool(bool& data) {
if (_numBytes < 1) {
return RxFrameError | NoDataError;
}
data = static_cast<bool>(_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;
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
/**
* 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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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;
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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_ */

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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<uint8_t>((data & 0xFF000000) >> 24));
error |= addUInt8(static_cast<uint8_t>((data & 0x00FF0000) >> 16));
error |= addUInt8(static_cast<uint8_t>((data & 0x0000FF00) >> 8));
error |= addUInt8(static_cast<uint8_t>((data & 0x000000FF) >> 0));
return error;
}
uint16_t SensirionShdlcTxFrame::addInt32(int32_t data) {
return addUInt32(static_cast<uint32_t>(data));
}
uint16_t SensirionShdlcTxFrame::addUInt16(uint16_t data) {
uint16_t error = addUInt8(static_cast<uint8_t>((data & 0xFF00) >> 8));
error |= addUInt8(static_cast<uint8_t>((data & 0x00FF) >> 0));
return error;
}
uint16_t SensirionShdlcTxFrame::addInt16(int16_t data) {
return addUInt16(static_cast<uint16_t>(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<uint8_t>(data));
}
uint16_t SensirionShdlcTxFrame::addBool(bool data) {
return addUInt8(static_cast<uint8_t>(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;
}

View File

@ -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 <stdint.h>
#include <stdlib.h>
#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_ */

View File

@ -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()

View File

@ -0,0 +1,3 @@
#!/bin/bash
set -euxo pipefail
cppcheck --std=c++11 --language=c++ --error-exitcode=1 --enable=warning,style,performance,portability src/*

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -0,0 +1,97 @@
<!-- Downloaded from https://github.com/Sensirion/arduino-i2c-sen5x
on 10/01/2023 at commit d7a73c86073cca34b84cde83814f5c17b87aad6d -->
# Sensirion I2C SEN5X Arduino Library
This is the Sensirion SEN5X library for Arduino using the
modules I2C interface.
<center><img src="images/SEN5x.png" width="500px"></center>
## 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 |
<center><img src="images/SEN5X_pinout.png" width="300px"></center>
| *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).

View File

@ -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 <Arduino.h>
#include <SensirionI2CSen5x.h>
#include <Wire.h>
// 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);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

View File

@ -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)
#######################################

View File

@ -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

View File

@ -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 <Wire.h>
#include <math.h>
#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<int16_t>(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<float>(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;
}

View File

@ -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 <Wire.h>
#include <SensirionCore.h>
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 */

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <SensirionI2CSen5x.h>
#include <Wire.h>
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} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
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

View File

@ -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",
"","","","",
"","","","",
"","","","",