Add support for Sensirion STCC4 CO2 sensor
- Change SHT1x software reset I2C bus after initial (un)detection
@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
|
||||
### Added
|
||||
- Support for Sensirion SCD43 CO2 sensor
|
||||
- I2S full duplex, auto rx sample rate (#24469)
|
||||
- Support for Sensirion STCC4 CO2 sensor
|
||||
|
||||
### Breaking Changed
|
||||
|
||||
@@ -15,6 +16,7 @@ All notable changes to this project will be documented in this file.
|
||||
- Matter improved parameters handling (#24471)
|
||||
- Sensirion Core library from v0.6.0 to v0.7.2
|
||||
- Sen5x power on delay of 60ms (#24452)
|
||||
- SHT1x software reset I2C bus after initial (un)detection
|
||||
|
||||
### Fixed
|
||||
- Do not free BT memory when in use (#24480)
|
||||
|
||||
@@ -236,6 +236,7 @@ In addition to @arendst the following code is mainly owned by:
|
||||
| xsns_117_c8_co2_5k | @jeroenvermeulen
|
||||
| xsns_118_ags02ma | Akshaylal S
|
||||
| xsns_119_sen6x | @arendst
|
||||
| xsns_120_stcc4 | @arendst
|
||||
| |
|
||||
| xsns_127_esp32_sensors | @arendst
|
||||
| |
|
||||
|
||||
@@ -139,5 +139,6 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip
|
||||
96 | USE_RX8025 | xdrv_56 | RX8025 | 0x32 | Yes | RX8025 RTC
|
||||
97 | USE_SEN6X | xsns_119 | SEN6X | 0x6B | Yes | Gas (CO2/VOC/NOx index) and air quality (PPM <1,<2.5,<4,<10)
|
||||
98 | USE_FM24CXX | xdrv_93 | FM24CXX | 0x50 - 0x57| Yes | FM24CXX - External FRAM with console / berry R/W operations
|
||||
|
||||
99 | USE_STCC4 | xsns_120 | STCC4 | 0x64, 0x65 | Yes | CO2 and optional temperature and humidity sensor
|
||||
|
||||
NOTE: Bus2 supported on ESP32 only.
|
||||
|
||||
@@ -115,12 +115,14 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm
|
||||
## Changelog v15.3.0.1
|
||||
### Added
|
||||
- Support for Sensirion SCD42 and SCD43 CO2 sensor
|
||||
- Support for Sensirion STCC4 CO2 sensor
|
||||
- I2S full duplex, auto rx sample rate [#24469](https://github.com/arendst/Tasmota/issues/24469)
|
||||
- Sen5x power on delay of 60ms [#24452](https://github.com/arendst/Tasmota/issues/24452)
|
||||
|
||||
### Changed
|
||||
- Sensirion Core library from v0.6.0 to v0.7.2
|
||||
- LVGL library from v9.4.0 to v9.5.0 [#24470](https://github.com/arendst/Tasmota/issues/24470)
|
||||
- SHT1x software reset I2C bus after initial (un)detection
|
||||
- Matter improved parameters handling [#24471](https://github.com/arendst/Tasmota/issues/24471)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -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']
|
||||
...
|
||||
@@ -0,0 +1,15 @@
|
||||
# CHANGELOG
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.0] - 2025-6-20
|
||||
|
||||
### Added
|
||||
|
||||
- Initial support for STCC4.
|
||||
|
||||
[Unreleased]: https://github.com/Sensirion/arduino-i2c-stcc4/compare/1.0.0...HEAD
|
||||
[1.0.0]: https://github.com/Sensirion/arduino-i2c-stcc4/releases/tag/1.0.0
|
||||
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2025, Sensirion AG
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,197 @@
|
||||
# Sensirion I²C STCC4 Arduino Library
|
||||
|
||||
This is the Sensirion STCC4 library for Arduino allowing you to
|
||||
communicate with a STCC4 sensor
|
||||
over I²C.
|
||||
|
||||
<img src="images/STCC4.png" width="300px">
|
||||
|
||||
Click [here](https://sensirion.com/products/catalog/STCC4) to learn more about the Sensirion STCC4 sensor.
|
||||
|
||||
|
||||
|
||||
The default I²C address of [STCC4](https://sensirion.com/products/catalog/STCC4) is **0x64**.
|
||||
|
||||
> [!NOTE]
|
||||
> The SEK-STCC4 board from Sensirion includes a STCC4 and a SHT4x for temperature and humidity compensation, which is controlled by the STCC4 through the integrated I2C controller interface. The provided examples are designed considering this sensor configuration.
|
||||
|
||||
|
||||
## Installation of the library
|
||||
|
||||
This library can be installed using the Arduino Library manager:
|
||||
Start the [Arduino IDE](http://www.arduino.cc/en/main/software) and open
|
||||
the Library Manager via
|
||||
|
||||
`Sketch` ➔ `Include Library` ➔ `Manage Libraries...`
|
||||
|
||||
Search for the `Sensirion I2C STCC4` library in the `Filter
|
||||
your search...` field and install it by clicking the `install` button.
|
||||
|
||||
If you cannot find it in the library manager, 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 library
|
||||
manager or `Add .ZIP Library`
|
||||
|
||||
#### Dependencies
|
||||
* [Sensirion Core](https://github.com/Sensirion/arduino-core)
|
||||
|
||||
## Connect the sensor
|
||||
|
||||
Use the following pin description to connect your STCC4 to the standard I²C bus of your Arduino board:
|
||||
|
||||
<img src="images/STCC4_pinout.png" width="300px">
|
||||
|
||||
| *Pin* | *Cable Color* | *Name* | *Description* | *Comments* |
|
||||
|-------|---------------|:------:|----------------|------------|
|
||||
| 1 | black | GND | Ground |
|
||||
| 2 | red | VDD | Supply Voltage | 2.7V to 5.5V
|
||||
| 3 | green | SDA | I2C: Serial data input / output |
|
||||
| 4 | yellow | SCL | I2C: Serial clock input |
|
||||
|
||||
|
||||
|
||||
|
||||
The recommended voltage is 3.3V.
|
||||
|
||||
### Board specific wiring
|
||||
You will find pinout schematics for recommended board models below:
|
||||
|
||||
|
||||
|
||||
<details><summary>Arduino Uno</summary>
|
||||
<p>
|
||||
|
||||
| *STCC4* | *STCC4 Pin* | *Cable Color* | *Board Pin* |
|
||||
| :---: | --- | --- | --- |
|
||||
| GND | 1 | black | GND |
|
||||
| VDD | 2 | red | 3.3V |
|
||||
| SDA | 3 | green | D18/SDA |
|
||||
| SCL | 4 | yellow | D19/SCL |
|
||||
|
||||
|
||||
|
||||
<img src="images/Arduino-Uno-Rev3-i2c-pinout-3.3V.png" width="600px">
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
<details><summary>Arduino Nano</summary>
|
||||
<p>
|
||||
|
||||
| *STCC4* | *STCC4 Pin* | *Cable Color* | *Board Pin* |
|
||||
| :---: | --- | --- | --- |
|
||||
| GND | 1 | black | GND |
|
||||
| VDD | 2 | red | 3.3V |
|
||||
| SDA | 3 | green | A4 |
|
||||
| SCL | 4 | yellow | A5 |
|
||||
|
||||
|
||||
|
||||
<img src="images/Arduino-Nano-i2c-pinout-3.3V.png" width="600px">
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
<details><summary>Arduino Micro</summary>
|
||||
<p>
|
||||
|
||||
| *STCC4* | *STCC4 Pin* | *Cable Color* | *Board Pin* |
|
||||
| :---: | --- | --- | --- |
|
||||
| GND | 1 | black | GND |
|
||||
| VDD | 2 | red | 3.3V |
|
||||
| SDA | 3 | green | D2/SDA |
|
||||
| SCL | 4 | yellow | ~D3/SCL |
|
||||
|
||||
|
||||
|
||||
<img src="images/Arduino-Micro-i2c-pinout-3.3V.png" width="600px">
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
<details><summary>Arduino Mega 2560</summary>
|
||||
<p>
|
||||
|
||||
| *STCC4* | *STCC4 Pin* | *Cable Color* | *Board Pin* |
|
||||
| :---: | --- | --- | --- |
|
||||
| GND | 1 | black | GND |
|
||||
| VDD | 2 | red | 3.3V |
|
||||
| SDA | 3 | green | D20/SDA |
|
||||
| SCL | 4 | yellow | D21/SCL |
|
||||
|
||||
|
||||
|
||||
<img src="images/Arduino-Mega-2560-Rev3-i2c-pinout-3.3V.png" width="600px">
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
<details><summary>ESP32 DevKitC</summary>
|
||||
<p>
|
||||
|
||||
| *STCC4* | *STCC4 Pin* | *Cable Color* | *Board Pin* |
|
||||
| :---: | --- | --- | --- |
|
||||
| GND | 1 | black | GND |
|
||||
| VDD | 2 | red | 3V3 |
|
||||
| SDA | 3 | green | GPIO 21 |
|
||||
| SCL | 4 | yellow | GPIO 22 |
|
||||
|
||||
|
||||
|
||||
<img src="images/esp32-devkitc-i2c-pinout-3.3V.png" width="600px">
|
||||
</p>
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Install the libraries and dependencies according to [Installation of the library](#installation-of-the-library)
|
||||
|
||||
2. Connect the STCC4 sensor to your Arduino as explained in [Connect the sensor](#connect-the-sensor)
|
||||
|
||||
3. Open the `exampleUsage` sample project within the Arduino IDE:
|
||||
|
||||
`File` ➔ `Examples` ➔ `Sensirion I2C STCC4` ➔ `exampleUsage`
|
||||
|
||||
|
||||
|
||||
5. 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 used tool has to be set to `115200 baud`.
|
||||
|
||||
## Contributing
|
||||
|
||||
**Contributions are welcome!**
|
||||
|
||||
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).
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
||||
*
|
||||
* Generator: sensirion-driver-generator 1.2.0
|
||||
* Product: stcc4
|
||||
* Model-Version: 3.4.0
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2025, Sensirion AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Sensirion AG nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <SensirionI2cStcc4.h>
|
||||
#include <Wire.h>
|
||||
|
||||
// macro definitions
|
||||
// make sure that we use the proper definition of NO_ERROR
|
||||
#ifdef NO_ERROR
|
||||
#undef NO_ERROR
|
||||
#endif
|
||||
#define NO_ERROR 0
|
||||
|
||||
SensirionI2cStcc4 sensor;
|
||||
|
||||
static char errorMessage[64];
|
||||
static int16_t error;
|
||||
|
||||
void PrintUint64(uint64_t& value) {
|
||||
Serial.print("0x");
|
||||
Serial.print((uint32_t)(value >> 32), HEX);
|
||||
Serial.print((uint32_t)(value & 0xFFFFFFFF), HEX);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
while (!Serial) {
|
||||
delay(100);
|
||||
}
|
||||
Wire.begin();
|
||||
sensor.begin(Wire, STCC4_I2C_ADDR_64);
|
||||
|
||||
uint32_t productId = 0;
|
||||
uint64_t serialNumber = 0;
|
||||
error = sensor.stopContinuousMeasurement();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute stopContinuousMeasurement(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
error = sensor.getProductId(productId, serialNumber);
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute getProductId(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
Serial.print("productId: ");
|
||||
Serial.print(productId);
|
||||
Serial.print("\t");
|
||||
Serial.print("serialNumber: ");
|
||||
PrintUint64(serialNumber);
|
||||
Serial.println();
|
||||
error = sensor.startContinuousMeasurement();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute startContinuousMeasurement(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
int16_t co2Concentration = 0;
|
||||
float temperature = 0.0;
|
||||
float relativeHumidity = 0.0;
|
||||
uint16_t sensorStatus = 0;
|
||||
delay(1000);
|
||||
error = sensor.readMeasurement(co2Concentration, temperature,
|
||||
relativeHumidity, sensorStatus);
|
||||
if (error != NO_ERROR) {
|
||||
// A failed read can be caused by clock shifting. We advise to retry
|
||||
// after a delay of 150ms.
|
||||
Serial.print(
|
||||
"Error trying to execute readMeasurement() (retry in 150ms): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
delay(150);
|
||||
error = sensor.readMeasurement(co2Concentration, temperature,
|
||||
relativeHumidity, sensorStatus);
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute readMeasurement() after "
|
||||
"additional delay: ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Serial.print("co2Concentration: ");
|
||||
Serial.print(co2Concentration);
|
||||
Serial.print("\t");
|
||||
Serial.print("temperature: ");
|
||||
Serial.print(temperature);
|
||||
Serial.print("\t");
|
||||
Serial.print("relativeHumidity: ");
|
||||
Serial.print(relativeHumidity);
|
||||
Serial.print("\t");
|
||||
Serial.print("sensorStatus: ");
|
||||
Serial.print(sensorStatus);
|
||||
Serial.println();
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
||||
*
|
||||
* Generator: sensirion-driver-generator 1.2.0
|
||||
* Product: stcc4
|
||||
* Model-Version: 3.4.0
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2025, Sensirion AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Sensirion AG nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <SensirionI2cStcc4.h>
|
||||
#include <Wire.h>
|
||||
|
||||
// macro definitions
|
||||
// make sure that we use the proper definition of NO_ERROR
|
||||
#ifdef NO_ERROR
|
||||
#undef NO_ERROR
|
||||
#endif
|
||||
#define NO_ERROR 0
|
||||
|
||||
SensirionI2cStcc4 sensor;
|
||||
|
||||
static char errorMessage[64];
|
||||
static int16_t error;
|
||||
|
||||
void setup() {
|
||||
|
||||
Serial.begin(115200);
|
||||
while (!Serial) {
|
||||
delay(100);
|
||||
}
|
||||
Wire.begin();
|
||||
sensor.begin(Wire, STCC4_I2C_ADDR_64);
|
||||
|
||||
delay(6);
|
||||
// Ensure sensor is in idle state
|
||||
error = sensor.exitSleepMode();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute exitSleepMode(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
error = sensor.stopContinuousMeasurement();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute stopContinuousMeasurement(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
// Enter sleep mode
|
||||
error = sensor.enterSleepMode();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute enterSleepMode(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
int16_t co2Concentration = 0;
|
||||
float temperature = 0.0;
|
||||
float relativeHumidity = 0.0;
|
||||
uint16_t status = 0;
|
||||
//
|
||||
// Measure every 10 seconds.
|
||||
delay(10000);
|
||||
//
|
||||
// Exit sleep mode to put the sensor into idle mode
|
||||
// to be able to perform a single shot measurement
|
||||
error = sensor.exitSleepMode();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute exitSleepMode(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// If humidity/temperature and/or pressure compensation is
|
||||
// desired, you should call the respective compensation
|
||||
// functions here. Check-out the header file for the definition
|
||||
// of the compensation functions.
|
||||
//
|
||||
// Perform a single shot measurement and read the sensor data
|
||||
error = sensor.measureSingleShot();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute measureSingleShot(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
error = sensor.readMeasurement(co2Concentration, temperature,
|
||||
relativeHumidity, status);
|
||||
if (error != NO_ERROR) {
|
||||
// A failed read can be caused by clock shifting. We advise to retry
|
||||
// after a delay of 150ms.
|
||||
Serial.print(
|
||||
"Error trying to execute readMeasurement() (retry in 150ms): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
delay(150);
|
||||
error = sensor.readMeasurement(co2Concentration, temperature,
|
||||
relativeHumidity, status);
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute readMeasurement() after "
|
||||
"additional delay: ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Power down the sensor to reduce power consumption.
|
||||
error = sensor.enterSleepMode();
|
||||
if (error != NO_ERROR) {
|
||||
Serial.print("Error trying to execute enterSleepMode(): ");
|
||||
errorToString(error, errorMessage, sizeof errorMessage);
|
||||
Serial.println(errorMessage);
|
||||
return;
|
||||
}
|
||||
//
|
||||
// Print results as physical unit.
|
||||
Serial.print("CO2 concentration [ppm] = ");
|
||||
Serial.print(co2Concentration);
|
||||
Serial.println();
|
||||
Serial.print("Temperature [°C] = ");
|
||||
Serial.print(temperature);
|
||||
Serial.println();
|
||||
Serial.print("Humidity [RH] = ");
|
||||
Serial.print(relativeHumidity);
|
||||
Serial.println();
|
||||
Serial.print("Status = ");
|
||||
Serial.print(status);
|
||||
Serial.println();
|
||||
}
|
||||
|
After Width: | Height: | Size: 405 KiB |
|
After Width: | Height: | Size: 322 KiB |
|
After Width: | Height: | Size: 296 KiB |
|
After Width: | Height: | Size: 315 KiB |
|
After Width: | Height: | Size: 267 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 524 KiB |
@@ -0,0 +1,43 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
SensirionI2cStcc4 KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
readMeasurement KEYWORD2
|
||||
setPressureCompensation KEYWORD2
|
||||
startContinuousMeasurement KEYWORD2
|
||||
readMeasurementRaw KEYWORD2
|
||||
stopContinuousMeasurement KEYWORD2
|
||||
measureSingleShot KEYWORD2
|
||||
performForcedRecalibration KEYWORD2
|
||||
getProductId KEYWORD2
|
||||
setRhtCompensation KEYWORD2
|
||||
setPressureCompensationRaw KEYWORD2
|
||||
performSelfTest KEYWORD2
|
||||
performConditioning KEYWORD2
|
||||
enterSleepMode KEYWORD2
|
||||
exitSleepMode KEYWORD2
|
||||
enableTestingMode KEYWORD2
|
||||
disableTestingMode KEYWORD2
|
||||
performFactoryReset KEYWORD2
|
||||
signalTemperature KEYWORD2
|
||||
signalRelativeHumidity KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Instances (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
sensor KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -0,0 +1,11 @@
|
||||
name=Sensirion I2C STCC4
|
||||
version=1.0.0
|
||||
author=Sensirion
|
||||
maintainer=Sensirion
|
||||
sentence=Library for the STCC4 sensor by Sensirion
|
||||
paragraph=Enables you to use the STCC4 sensor via I2C.
|
||||
url=https://github.com/Sensirion/arduino-i2c-stcc4
|
||||
category=Sensors
|
||||
architectures=*
|
||||
depends=Sensirion Core
|
||||
includes=SensirionI2cStcc4.h
|
||||
@@ -0,0 +1,7 @@
|
||||
# driver generation metadata
|
||||
generator_version: 1.2.0
|
||||
model_version: 3.4.0
|
||||
dg_status: released
|
||||
is_manually_modified: true
|
||||
first_generated: '2025-02-24 11:47'
|
||||
last_generated: '2025-06-20 08:02'
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
||||
*
|
||||
* Generator: sensirion-driver-generator 1.2.0
|
||||
* Product: stcc4
|
||||
* Model-Version: 3.4.0
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2025, Sensirion AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Sensirion AG nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "SensirionI2cStcc4.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
// make sure that we use the proper definition of NO_ERROR
|
||||
#ifdef NO_ERROR
|
||||
#undef NO_ERROR
|
||||
#endif
|
||||
#define NO_ERROR 0
|
||||
|
||||
static uint8_t communication_buffer[18] = {0};
|
||||
|
||||
SensirionI2cStcc4::SensirionI2cStcc4() {
|
||||
}
|
||||
|
||||
float SensirionI2cStcc4::signalTemperature(uint16_t rawTemperature) {
|
||||
float temperature = 0.0;
|
||||
temperature = -45.0 + ((175.0 * rawTemperature) / 65535.0);
|
||||
return temperature;
|
||||
}
|
||||
|
||||
float SensirionI2cStcc4::signalRelativeHumidity(uint16_t rawRelativeHumidity) {
|
||||
float relativeHumidity = 0.0;
|
||||
relativeHumidity = -6.0 + ((125.0 * rawRelativeHumidity) / 65535.0);
|
||||
return relativeHumidity;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::readMeasurement(int16_t& co2Concentration,
|
||||
float& temperature,
|
||||
float& relativeHumidity,
|
||||
uint16_t& sensorStatus) {
|
||||
int16_t rawCo2Concentration = 0;
|
||||
uint16_t rawTemperature = 0;
|
||||
uint16_t rawRelativeHumidity = 0;
|
||||
uint16_t sensorStatusRaw = 0;
|
||||
int16_t localError = 0;
|
||||
localError = readMeasurementRaw(rawCo2Concentration, rawTemperature,
|
||||
rawRelativeHumidity, sensorStatusRaw);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
co2Concentration = rawCo2Concentration;
|
||||
temperature = SensirionI2cStcc4::signalTemperature(rawTemperature);
|
||||
relativeHumidity =
|
||||
SensirionI2cStcc4::signalRelativeHumidity(rawRelativeHumidity);
|
||||
sensorStatus = sensorStatusRaw;
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::setPressureCompensation(uint16_t pressure) {
|
||||
int16_t localError = 0;
|
||||
localError = setPressureCompensationRaw((uint16_t)(pressure / 2));
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::startContinuousMeasurement() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x218b, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::readMeasurementRaw(int16_t& co2ConcentrationRaw,
|
||||
uint16_t& temperatureRaw,
|
||||
uint16_t& relativeHumidityRaw,
|
||||
uint16_t& sensorStatusRaw) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0xec05, buffer_ptr, 12);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(1);
|
||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 12);
|
||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 12,
|
||||
rxFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError |= rxFrame.getInt16(co2ConcentrationRaw);
|
||||
localError |= rxFrame.getUInt16(temperatureRaw);
|
||||
localError |= rxFrame.getUInt16(relativeHumidityRaw);
|
||||
localError |= rxFrame.getUInt16(sensorStatusRaw);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::stopContinuousMeasurement() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x3f86, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(1200);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::measureSingleShot() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x219d, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(500);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t
|
||||
SensirionI2cStcc4::performForcedRecalibration(int16_t targetCO2Concentration,
|
||||
int16_t& frcCorrection) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x362f, buffer_ptr, 5);
|
||||
localError |= txFrame.addInt16(targetCO2Concentration);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(90);
|
||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 5);
|
||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
|
||||
rxFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError |= rxFrame.getInt16(frcCorrection);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::getProductId(uint32_t& productId,
|
||||
uint64_t& serialNumber) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x365b, buffer_ptr, 18);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(1);
|
||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 18);
|
||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 18,
|
||||
rxFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError |= rxFrame.getUInt32(productId);
|
||||
localError |= rxFrame.getInteger((uint8_t*)&serialNumber, LongInteger, 8);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::setRhtCompensation(uint16_t rawTemperature,
|
||||
uint16_t rawHumidity) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0xe000, buffer_ptr, 8);
|
||||
localError |= txFrame.addUInt16(rawTemperature);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError |= txFrame.addUInt16(rawHumidity);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(1);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::setPressureCompensationRaw(uint16_t pressure) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0xe016, buffer_ptr, 5);
|
||||
localError |= txFrame.addUInt16(pressure);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(1);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::performSelfTest(uint16_t& testResult) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x278c, buffer_ptr, 3);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(360);
|
||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
|
||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
|
||||
rxFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError |= rxFrame.getUInt16(testResult);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::performConditioning() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x29bc, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(22000);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::enterSleepMode() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x3650, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(1);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::exitSleepMode() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt8Command(0x0, buffer_ptr, 2);
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
delay(5);
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::enableTestingMode() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x3fbc, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::disableTestingMode() {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x3f3d, buffer_ptr, 2);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
return localError;
|
||||
}
|
||||
|
||||
int16_t SensirionI2cStcc4::performFactoryReset(uint16_t& factoryResetResult) {
|
||||
int16_t localError = NO_ERROR;
|
||||
uint8_t* buffer_ptr = communication_buffer;
|
||||
SensirionI2CTxFrame txFrame =
|
||||
SensirionI2CTxFrame::createWithUInt16Command(0x3632, buffer_ptr, 3);
|
||||
localError =
|
||||
SensirionI2CCommunication::sendFrame(_i2cAddress, txFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
delay(90);
|
||||
SensirionI2CRxFrame rxFrame(buffer_ptr, 3);
|
||||
localError = SensirionI2CCommunication::receiveFrame(_i2cAddress, 3,
|
||||
rxFrame, *_i2cBus);
|
||||
if (localError != NO_ERROR) {
|
||||
return localError;
|
||||
}
|
||||
localError |= rxFrame.getUInt16(factoryResetResult);
|
||||
return localError;
|
||||
}
|
||||
|
||||
void SensirionI2cStcc4::begin(TwoWire& i2cBus, uint8_t i2cAddress) {
|
||||
_i2cBus = &i2cBus;
|
||||
_i2cAddress = i2cAddress;
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* THIS FILE IS AUTOMATICALLY GENERATED
|
||||
*
|
||||
* Generator: sensirion-driver-generator 1.2.0
|
||||
* Product: stcc4
|
||||
* Model-Version: 3.4.0
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2025, Sensirion AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of Sensirion AG nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef SENSIRIONI2CSTCC4_H
|
||||
#define SENSIRIONI2CSTCC4_H
|
||||
|
||||
#include <SensirionCore.h>
|
||||
#include <Wire.h>
|
||||
|
||||
#define STCC4_I2C_ADDR_64 0x64
|
||||
|
||||
typedef enum {
|
||||
STCC4_START_CONTINUOUS_MEASUREMENT_CMD_ID = 0x218b,
|
||||
STCC4_READ_MEASUREMENT_RAW_CMD_ID = 0xec05,
|
||||
STCC4_STOP_CONTINUOUS_MEASUREMENT_CMD_ID = 0x3f86,
|
||||
STCC4_MEASURE_SINGLE_SHOT_CMD_ID = 0x219d,
|
||||
STCC4_PERFORM_FORCED_RECALIBRATION_CMD_ID = 0x362f,
|
||||
STCC4_GET_PRODUCT_ID_CMD_ID = 0x365b,
|
||||
STCC4_SET_RHT_COMPENSATION_CMD_ID = 0xe000,
|
||||
STCC4_SET_PRESSURE_COMPENSATION_RAW_CMD_ID = 0xe016,
|
||||
STCC4_PERFORM_SELF_TEST_CMD_ID = 0x278c,
|
||||
STCC4_PERFORM_CONDITIONING_CMD_ID = 0x29bc,
|
||||
STCC4_ENTER_SLEEP_MODE_CMD_ID = 0x3650,
|
||||
STCC4_EXIT_SLEEP_MODE_CMD_ID = 0x0,
|
||||
STCC4_ENABLE_TESTING_MODE_CMD_ID = 0x3fbc,
|
||||
STCC4_DISABLE_TESTING_MODE_CMD_ID = 0x3f3d,
|
||||
STCC4_PERFORM_FACTORY_RESET_CMD_ID = 0x3632,
|
||||
} STCC4CmdId;
|
||||
|
||||
class SensirionI2cStcc4 {
|
||||
public:
|
||||
SensirionI2cStcc4();
|
||||
/**
|
||||
* @brief Initializes the STCC4 class.
|
||||
*
|
||||
* @param i2cBus Arduino stream object to be used for communication.
|
||||
*/
|
||||
void begin(TwoWire& i2cBus, uint8_t i2cAddress);
|
||||
|
||||
/**
|
||||
* @brief readMeasurement
|
||||
*
|
||||
* reads measurement data
|
||||
*
|
||||
* @param[out] co2Concentration
|
||||
* @param[out] temperature
|
||||
* @param[out] relativeHumidity
|
||||
* @param[out] sensorStatus
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t readMeasurement(int16_t& co2Concentration, float& temperature,
|
||||
float& relativeHumidity, uint16_t& sensorStatus);
|
||||
|
||||
/**
|
||||
* @brief Set the pressure compensation in Pa
|
||||
*
|
||||
* External pressure values can be set through the set_pressure_compensation
|
||||
* command. The written pressure value is applied for pressure compensation
|
||||
* after a maximum of one measurement interval. Power cycling resets the
|
||||
* sensor to the default value of 101'300 Pa.
|
||||
*
|
||||
* @param[in] pressure
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t setPressureCompensation(uint16_t pressure);
|
||||
|
||||
/**
|
||||
* @brief Start a continuous measurement (interval 1 s).
|
||||
*
|
||||
* After sending the start_continuous_measurement command, the sensor will
|
||||
* begin measuring the CO2 gas concentration continuously with a sampling
|
||||
* interval of 1 second. The recommended communication sequence for
|
||||
* continuous measurement is as follows: 1. The sensor is powered up into
|
||||
* the idle state. 2. The I2C controller sends a
|
||||
* start_continuous_measurement command. 3. The I2C controller periodically
|
||||
* reads out data with the read_measurement command. 4. If desired, stop
|
||||
* taking measurements using the stop_continuous_measurement command.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t startContinuousMeasurement();
|
||||
|
||||
/**
|
||||
* @brief Read CO₂, temperature, and humidity measurements.
|
||||
*
|
||||
* After the command *start_continuous_measurement* has been sent, the chip
|
||||
* continuously measures and updates the measurement results. New results
|
||||
* (co2_concentration, temperature, and relative_humidity) can be read
|
||||
* continuously with this command.
|
||||
*
|
||||
* @param[out] co2ConcentrationRaw CO2 concentration in ppm. No conversion
|
||||
* needed.
|
||||
* @param[out] temperatureRaw Raw temperature signal. The signal s can be
|
||||
* converted into a temperature in °C (t) by applying the formula t = -45 +
|
||||
* 175
|
||||
* * s / 65535.
|
||||
* @param[out] relativeHumidityRaw Raw relative humidity signal. The signal
|
||||
* s can be converted into relative humidity in % (rh) by applying the
|
||||
* formula rh = -6 + 125 * s / 65535.
|
||||
* @param[out] sensorStatusRaw The sensor status. If the sensor is in
|
||||
* testing mode, byte 10 is equal to 4.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t readMeasurementRaw(int16_t& co2ConcentrationRaw,
|
||||
uint16_t& temperatureRaw,
|
||||
uint16_t& relativeHumidityRaw,
|
||||
uint16_t& sensorStatusRaw);
|
||||
|
||||
/**
|
||||
* @brief The command stops the continuous measurement and puts the sensor
|
||||
* into idle mode.
|
||||
*
|
||||
* After receiving the stop_continuous_measurement command, the sensor will
|
||||
* finish the currently running measurement before returning to idle mode.
|
||||
* Therefore, a wait time of one measurement interval plus a 200 ms clock
|
||||
* tolerance is required before a new command is acknowledged.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t stopContinuousMeasurement();
|
||||
|
||||
/**
|
||||
* @brief Performs a single shot measurement.
|
||||
*
|
||||
* The measure_single_shot command conducts an on-demand measurement of CO2
|
||||
* gas concentration. The typical communication sequence is as follows: 1.
|
||||
* The sensor is powered up with the exit_sleep_mode command if previously
|
||||
* powered down using the enter_sleep_mode command. 2. The I2C controller
|
||||
* sends a measure_single_shot command and waits for the execution time. 3.
|
||||
* The I2C controller reads out data with the read_measurement command. 4.
|
||||
* Repeat steps 2-3 as required by the application. 5. If desired, set the
|
||||
* sensor to sleep with the enter_sleep_mode command.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t measureSingleShot();
|
||||
|
||||
/**
|
||||
* @brief Perform a forced recalibration (FRC) of the CO₂ concentration.
|
||||
*
|
||||
* The forced recalibration (FRC) can be applied to correct the sensor's
|
||||
* output by means of an externally obtained CO 2 reference value. The
|
||||
* recommended communication sequence for a successful FRC in continuous
|
||||
* mode is as follows:
|
||||
* 1. Operate the STCC4 for at least 3 minutes. Ensure the sensor reading
|
||||
* and environmental conditions, including CO2 concentration, are stable. 2.
|
||||
* The I2C controller sends the stop_continuous_measurement command
|
||||
* (Section 5.3.2) 3. Wait for the specified execution time of
|
||||
* stop_continuous_measurement command.
|
||||
* 4. Issue the perform_forced_recalibration command with the target CO2
|
||||
* concentration and read out the applied FRC correction.
|
||||
*
|
||||
* The recommended communication sequence for a successful FRC in single
|
||||
* shot mode is as follows: 1. Operate the STCC4 for at least 3 minutes. For
|
||||
* sampling intervals significantly larger than 10 seconds, increase the
|
||||
* operation time accordingly. Ensure the sensor reading and environmental
|
||||
* conditions, including CO2 concentration, are stable. 2. Issue the
|
||||
* perform_forced_recalibration command with the reference CO2 concentration
|
||||
* and read out the applied FRC correction. The sensor must remain in idle
|
||||
* mode after operation before command execution.
|
||||
*
|
||||
* @param[in] targetCO2Concentration Target CO₂ concentration in ppm.
|
||||
* @param[out] frcCorrection Returns the FRC correction value if FRC has
|
||||
* been successful. 0xFFFF on failure.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t performForcedRecalibration(int16_t targetCO2Concentration,
|
||||
int16_t& frcCorrection);
|
||||
|
||||
/**
|
||||
* @brief Read the sensor's 32-bit product id and 64-bit serial number.
|
||||
*
|
||||
* The get_product_ID command retrieves the product identifier and serial
|
||||
* number. The command can be used to check communication with the sensor.
|
||||
*
|
||||
* @param[out] productId 32-bit
|
||||
* @param[out] serialNumber 64-bit unique serial number of the sensor.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t getProductId(uint32_t& productId, uint64_t& serialNumber);
|
||||
|
||||
/**
|
||||
* @brief Write the raw SHT4x temperature and humidity values to the STCC4.
|
||||
*
|
||||
* When the SHT4x is not directly connected to the STCC4 for humidity and
|
||||
* temperature compensation, the set_rht_compensation command allows
|
||||
* external input of relative humidity (RH) and temperature (T) values. The
|
||||
* temperature value must be provided from the RHT sensor providing the RH
|
||||
* value for correct absolute humidity calculation. The written RHT values
|
||||
* are applied for compensation after a maximum of one measurement interval.
|
||||
* The default or last written values are used for RHT compensation until
|
||||
* overwritten. Power cycling resets the sensor to the default values of
|
||||
* 25°C and 50 %RH.
|
||||
*
|
||||
* @param[in] rawTemperature raw temperature ticks as provided by SHT4x
|
||||
* sensor
|
||||
* @param[in] rawHumidity raw humidity ticks as provided by SHT4x sensor
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*
|
||||
* Example:
|
||||
* --------
|
||||
*
|
||||
* @code{.cpp}
|
||||
*
|
||||
* int16_t localError = 0;
|
||||
* localError = sensor.setRhtCompensation(26214, 29359);
|
||||
* if (localError != NO_ERROR) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
int16_t setRhtCompensation(uint16_t rawTemperature, uint16_t rawHumidity);
|
||||
|
||||
/**
|
||||
* @brief Write the pressure value to the STCC4
|
||||
*
|
||||
* External pressure values can be set through the set_pressure_compensation
|
||||
* command. The written pressure value is applied for pressure compensation
|
||||
* after a maximum of one measurement interval. Power cycling resets the
|
||||
* sensor to the default value of 101'300 Pa. The default or the last
|
||||
* written value is used in pressure compensation until overwritten.
|
||||
*
|
||||
* @param[in] pressure The pressure value as Pa divided by 2. E.g. for
|
||||
* 101300 Pa you have to send 50650
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*
|
||||
* Example:
|
||||
* --------
|
||||
*
|
||||
* @code{.cpp}
|
||||
*
|
||||
* int16_t localError = 0;
|
||||
* localError = sensor.setPressureCompensationRaw(50650);
|
||||
* if (localError != NO_ERROR) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
int16_t setPressureCompensationRaw(uint16_t pressure);
|
||||
|
||||
/**
|
||||
* @brief Performs an internal check to determine if the sensor has a
|
||||
* malfunction.
|
||||
*
|
||||
* The perform_self_test command can be used as an end-of-line test to check
|
||||
* the sensor functionality. Word[0] = 0 → pass Word[0] ≠ 0 → malfunction
|
||||
* detected
|
||||
*
|
||||
* @param[out] testResult Test result of the self-test. If the result is ≠
|
||||
* 0, the self-test failed.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t performSelfTest(uint16_t& testResult);
|
||||
|
||||
/**
|
||||
* @brief Conditions the sensor with a set operation profile.
|
||||
*
|
||||
* The perform_conditioning command is recommended to improve sensor
|
||||
* performance when the sensor has not completed measurements for more than
|
||||
* 3 hours. It is recommended to allow a settling time of 2 seconds after
|
||||
* the execution time is complete. The recommended communication sequence is
|
||||
* as follows: 1. The sensor is woken up with the exit_sleep_mode command if
|
||||
* previously put to sleep using the enter_sleep_mode command. 2. The I2C
|
||||
* controller sends a perform_conditioning command. 3. Wait for the
|
||||
* specified execution time of perform_conditioning command plus a 2 second
|
||||
* settling time. 4. The I2C controller sends a measurement command (e.g.
|
||||
* start_continuous_measurement command or measure_single_shot command.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t performConditioning();
|
||||
|
||||
/**
|
||||
* @brief Sets the sensor from idle mode into sleep mode.
|
||||
*
|
||||
* The enter_sleep_mode command sets the sensor to sleep mode through the
|
||||
* I2C interface. The written relative humidity, temperature, and pressure
|
||||
* compensation values as well as the ASC state will be retained while in
|
||||
* sleep mode.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t enterSleepMode();
|
||||
|
||||
/**
|
||||
* @brief The sensor is set from sleep mode into idle mode when it receives
|
||||
* the valid I²C address and a write bit (‘0’).
|
||||
*
|
||||
* The exit_sleep_mode command sets the sensor to idle mode through the I2C
|
||||
* interface. The sensor's idle state can be verified by reading out the
|
||||
* product ID.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t exitSleepMode();
|
||||
|
||||
/**
|
||||
* @brief Enable the sensor testing mode.
|
||||
*
|
||||
* The enable_testing_mode command is used to test the sensor with the ASC
|
||||
* algorithm disabled. The sensor state can be verified by reading out the
|
||||
* sensor status word in the read_measurement command.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t enableTestingMode();
|
||||
|
||||
/**
|
||||
* @brief Disable the sensor testing mode.
|
||||
*
|
||||
* The disable_testing_mode command is used to exit the testing mode. The
|
||||
* sensor state can be verified by reading out the sensor status word in the
|
||||
* read_measurement command.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t disableTestingMode();
|
||||
|
||||
/**
|
||||
* @brief Reset the FRC and ASC algorithm history.
|
||||
*
|
||||
* The perform_factory_reset command can be used to reset the FRC and ASC
|
||||
* algorithm history.
|
||||
*
|
||||
* @param[out] factoryResetResult The result of the factory reset. If the
|
||||
* result is ≠ 0, the factory reset failed.
|
||||
*
|
||||
* @return error_code 0 on success, an error code otherwise.
|
||||
*/
|
||||
int16_t performFactoryReset(uint16_t& factoryResetResult);
|
||||
|
||||
/**
|
||||
* @brief signalTemperature
|
||||
*
|
||||
* @param[in] rawTemperature
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static float signalTemperature(uint16_t rawTemperature);
|
||||
|
||||
/**
|
||||
* @brief signalRelativeHumidity
|
||||
*
|
||||
* @param[in] rawRelativeHumidity
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static float signalRelativeHumidity(uint16_t rawRelativeHumidity);
|
||||
|
||||
private:
|
||||
TwoWire* _i2cBus = nullptr;
|
||||
uint8_t _i2cAddress = 0;
|
||||
};
|
||||
|
||||
#endif // SENSIRIONI2CSTCC4_H
|
||||
@@ -663,8 +663,9 @@
|
||||
#define MGS_SENSOR_ADDR 0x04 // Default Mutichannel Gas sensor i2c address
|
||||
// #define USE_SCD30 // [I2cDriver29] Enable Sensiron SCd30 CO2 sensor (I2C address 0x61) (+3k3 code)
|
||||
// #define USE_SCD40 // [I2cDriver62] Enable Sensiron SCd40/Scd41 CO2 sensor (I2C address 0x62) (+3k5 code)
|
||||
// #define USE_STCC4 // [I2cDriver99] Enable Sensiron STCC4 CO2 sensor (I2C address 0x64 or 0x65) (+3k8 code)
|
||||
// #define USE_SEN5X // [I2cDriver76] Enable Sensiron SEN5X sensor (I2C address 0x69) (+3k code)
|
||||
// #define USE_SEN6X // [I2cDriver97] Enable Sensiron SEN6X sensor (I2C address 0x6B) (+7k8 code)
|
||||
// #define USE_SEN6X // [I2cDriver97] Enable Sensiron SEN6X sensor (I2C address 0x6B) (+8k8 code)
|
||||
// #define USE_SHT3X // [I2cDriver15] Enable Sensiron SHT3x (I2C address 0x44 or 0x45) or SHTC3 (I2C address 0x70) sensor (+0k7 code)
|
||||
// #define USE_SGP30 // [I2cDriver18] Enable Sensiron SGP30 sensor (I2C address 0x58) (+1k1 code)
|
||||
// #define USE_SGP40 // [I2cDriver69] Enable Sensiron SGP40 sensor (I2C address 0x59) (+1k4 code)
|
||||
|
||||
@@ -337,6 +337,21 @@ bool I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len,
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*/
|
||||
|
||||
bool I2cReset(uint32_t bus = 0) {
|
||||
/*
|
||||
NXP UM10204 I2C-bus specification and user manual - Software Reset
|
||||
Following a General Call, (0000 0000), sending 0000 0110 (06h) as the second byte
|
||||
causes a software reset. This feature is optional and not all devices respond to this
|
||||
command. On receiving this 2-byte sequence, all devices designed to respond to
|
||||
the general call address reset and take in the programmable part of their address.
|
||||
Precautions must be taken to ensure that a device is not pulling down the SDA or SCL
|
||||
line after applying the supply voltage, since these low levels would block the bus.
|
||||
*/
|
||||
return I2cWrite0(0, 6, bus);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*/
|
||||
|
||||
void I2cScan(uint8_t bus = 0) {
|
||||
// Return error codes defined in twi.h and core_esp8266_si2c.c
|
||||
// I2C_OK 0
|
||||
@@ -389,6 +404,8 @@ void I2cScan(uint8_t bus = 0) {
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------------------------*/
|
||||
|
||||
void I2cResetActive(uint32_t addr, uint8_t bus = 0) {
|
||||
addr &= 0x7F; // Max I2C address is 127
|
||||
I2C.active[bus][addr / 32] &= ~(1 << (addr % 32));
|
||||
|
||||
@@ -970,7 +970,9 @@ constexpr uint32_t feature[] = {
|
||||
#if defined(USE_I2C) && defined(USE_FM24CXX)
|
||||
0x00400000 | // xdrv_93_fm24cxx.ino
|
||||
#endif
|
||||
// 0x00800000 | //
|
||||
#if defined(USE_I2C) && defined(USE_STCC4)
|
||||
0x00800000 | // xsns_120_stcc4.ino
|
||||
#endif
|
||||
// 0x01000000 | //
|
||||
// 0x02000000 | //
|
||||
// 0x04000000 | //
|
||||
|
||||
@@ -162,6 +162,7 @@ void ShtDetect(void) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_I2C D_SHT1X_FOUND));
|
||||
}
|
||||
I2cBegin(Sht1x.sda_pin, Sht1x.scl_pin); // Reinit I2C bus
|
||||
I2cReset();
|
||||
}
|
||||
|
||||
void ShtEverySecond(void) {
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
#define XSNS_119 119
|
||||
#define XI2C_97 97 // See I2CDEVICES.md
|
||||
|
||||
//#define SENSIRION_DEBUG // Adds 1k2 to code size
|
||||
|
||||
#include <SensirionI2cSen6x.h>
|
||||
|
||||
#define SEN6X_STATE_READ_MEASUREMENT 0
|
||||
@@ -60,7 +62,7 @@
|
||||
|
||||
enum eSen6xFeatures { SEN6X_VOCNOX = 1, SEN6X_CO2 = 2, SEN6X_HCHO = 4 };
|
||||
|
||||
SensirionI2cSen6x *sen6x = nullptr;
|
||||
SensirionI2cSen6x sen6x;
|
||||
|
||||
const char mSenNames[] PROGMEM = "SEN62|SEN63C||SEN65|SEN66||SEN68|SEN69C";
|
||||
|
||||
@@ -123,14 +125,20 @@ uint32_t ParseIntParameters(uint32_t count, int *params) {
|
||||
|
||||
void Sen6xStopStartMeasurement(void) {
|
||||
// Stop measurement and restart after 1 second
|
||||
sen6x->stopMeasurement(); // Performs internal delay(1000)
|
||||
sen6x.stopMeasurement(); // Performs internal delay(1000)
|
||||
SEN6XDATA->state = SEN6X_STATE_START_MEASUREMENT;
|
||||
}
|
||||
|
||||
bool Sen6xError(const char* func, int error) {
|
||||
bool result = (error != 0);
|
||||
if (result) {
|
||||
#ifdef SENSIRION_DEBUG
|
||||
char error_msg[64];
|
||||
errorToString(error, error_msg, sizeof(error_msg));
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("S6X: %s error %d %s"), func, error, error_msg);
|
||||
#else
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("S6X: %s error %d"), func, error);
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -139,7 +147,13 @@ bool CmndSen6xError(int error) {
|
||||
bool result = (error != 0);
|
||||
if (result) {
|
||||
ResponseCmnd();
|
||||
#ifdef SENSIRION_DEBUG
|
||||
char error_msg[64];
|
||||
errorToString(error, error_msg, sizeof(error_msg));
|
||||
ResponseAppend_P(PSTR("{\"Error\":\"%d %s\"}"), error, error_msg);
|
||||
#else
|
||||
ResponseAppend_P(PSTR("{\"Error\":%d}"), error);
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -153,26 +167,25 @@ void Sen6xInit(void) {
|
||||
// Sen6xError("Scan", bus +1);
|
||||
continue;
|
||||
}
|
||||
sen6x = new SensirionI2cSen6x();
|
||||
sen6x->begin(I2cGetWire(bus), SEN6X_I2C_ADDR_6B);
|
||||
sen6x.begin(I2cGetWire(bus), SEN6X_I2C_ADDR_6B);
|
||||
|
||||
if (Sen6xError("Reset", sen6x->deviceReset())) { // Performs delay(1200) if no error
|
||||
if (Sen6xError("Reset", sen6x.deviceReset())) { // Performs delay(1200) if no error
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
if (Sen6xError("Version", sen6x->getVersion(major, minor))) {
|
||||
if (Sen6xError("Version", sen6x.getVersion(major, minor))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int8_t serial_number[32] = { 0 };
|
||||
if (Sen6xError("Serialnumber", sen6x->getSerialNumber(serial_number, sizeof(serial_number)))) {
|
||||
if (Sen6xError("Serialnumber", sen6x.getSerialNumber(serial_number, sizeof(serial_number)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int8_t product_name[32] = { 0 };
|
||||
if (Sen6xError("Productname", sen6x->getProductName(product_name, sizeof(product_name)))) {
|
||||
if (Sen6xError("Productname", sen6x.getProductName(product_name, sizeof(product_name)))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -206,7 +219,7 @@ void Sen6xUpdate(void) {
|
||||
int16_t temperature;
|
||||
int16_t vocIndex;
|
||||
int16_t noxIndex;
|
||||
if (!Sen6xError("Measurement", sen6x->readMeasuredValuesAsIntegers(
|
||||
if (!Sen6xError("Measurement", sen6x.readMeasuredValuesAsIntegers(
|
||||
SEN6XDATA->model,
|
||||
massConcentrationPm1p0, massConcentrationPm2p5,
|
||||
massConcentrationPm4p0, massConcentrationPm10p0,
|
||||
@@ -227,7 +240,7 @@ void Sen6xUpdate(void) {
|
||||
}
|
||||
break;
|
||||
case SEN6X_STATE_START_MEASUREMENT -1:
|
||||
if (Sen6xError("StartContinuous", sen6x->startContinuousMeasurement())) {
|
||||
if (Sen6xError("StartContinuous", sen6x.startContinuousMeasurement())) {
|
||||
SEN6XDATA->state = SEN6X_STATE_START_MEASUREMENT +2;
|
||||
}
|
||||
break;
|
||||
@@ -253,33 +266,33 @@ void (* const Sen6xCommand[])(void) PROGMEM = {
|
||||
|
||||
void CmndSen6xState(void) {
|
||||
int8_t productname[32] = { 0 };
|
||||
sen6x->getProductName(productname, sizeof(productname)); // No need to stop
|
||||
sen6x.getProductName(productname, sizeof(productname)); // No need to stop
|
||||
int8_t serial_number[32] = { 0 };
|
||||
sen6x->getSerialNumber(serial_number, sizeof(serial_number)); // No need to stop
|
||||
sen6x.getSerialNumber(serial_number, sizeof(serial_number)); // No need to stop
|
||||
SEN6XDeviceStatus status;
|
||||
sen6x->readDeviceStatus(status); // No need to stop
|
||||
sen6x.readDeviceStatus(status); // No need to stop
|
||||
Response_P(PSTR("{\"SEN6x\":{\"Name\":\"%s\",\"Serial\":\"%s\",\"Version\":\"%d.%d\",\"Status\":\"%04X\""),
|
||||
productname, serial_number, SEN6XDATA->major, SEN6XDATA->minor, status);
|
||||
|
||||
bool stop_measurement = false;
|
||||
|
||||
if (SEN6XDATA->features & SEN6X_CO2) {
|
||||
Sen6xStopStartMeasurement(); // Stop measurement and restart after 1 second
|
||||
Sen6xStopStartMeasurement(); // Stop measurement and restart after 1 second
|
||||
stop_measurement = true;
|
||||
uint16_t pressure;
|
||||
sen6x->getAmbientPressure(pressure); // No need to stop
|
||||
sen6x.getAmbientPressure(pressure); // No need to stop
|
||||
uint16_t altitude;
|
||||
sen6x->getSensorAltitude(altitude); // Only in idle mode (stopped measurement)
|
||||
sen6x.getSensorAltitude(altitude); // Only in idle mode (stopped measurement)
|
||||
uint8_t padding;
|
||||
bool calstatus;
|
||||
sen6x->getCo2SensorAutomaticSelfCalibration(padding, calstatus); // Only in idle mode (stopped measurement)
|
||||
sen6x.getCo2SensorAutomaticSelfCalibration(padding, calstatus); // Only in idle mode (stopped measurement)
|
||||
ResponseAppend_P(PSTR(",\"CO2\":{\"Altitude\":%d,\"Pressure\":%d,\"AutoCal\":%d}"),
|
||||
altitude, pressure, calstatus);
|
||||
}
|
||||
|
||||
if (SEN6XDATA->features & SEN6X_VOCNOX) {
|
||||
if (!stop_measurement) {
|
||||
Sen6xStopStartMeasurement(); // Stop measurement and restart after 1 second
|
||||
Sen6xStopStartMeasurement(); // Stop measurement and restart after 1 second
|
||||
stop_measurement = true;
|
||||
}
|
||||
int16_t voc_io;
|
||||
@@ -288,7 +301,7 @@ void CmndSen6xState(void) {
|
||||
int16_t voc_gmdm;
|
||||
int16_t voc_si;
|
||||
int16_t voc_gf;
|
||||
sen6x->getVocAlgorithmTuningParameters(voc_io, voc_ltoh, voc_ltgh, voc_gmdm, voc_si, voc_gf); // Only in idle mode (stopped measurement)
|
||||
sen6x.getVocAlgorithmTuningParameters(voc_io, voc_ltoh, voc_ltgh, voc_gmdm, voc_si, voc_gf); // Only in idle mode (stopped measurement)
|
||||
ResponseAppend_P(PSTR(",\"VOC\":{\"IdxOffset\":%d,\"LearningTime\":{\"Offset\":%d,\"Gain\":%d},\"GatingMaxDur\":%d,\"StdInit\":%d,\"GainFctr\":%d}"),
|
||||
voc_io, voc_ltoh, voc_ltgh, voc_gmdm, voc_si, voc_gf);
|
||||
int16_t nox_io;
|
||||
@@ -297,7 +310,7 @@ void CmndSen6xState(void) {
|
||||
int16_t nox_gmdm;
|
||||
int16_t nox_si;
|
||||
int16_t nox_gf;
|
||||
sen6x->getNoxAlgorithmTuningParameters(nox_io, nox_ltoh, nox_ltgh, nox_gmdm, nox_si, nox_gf); // Only in idle mode (stopped measurement)
|
||||
sen6x.getNoxAlgorithmTuningParameters(nox_io, nox_ltoh, nox_ltgh, nox_gmdm, nox_si, nox_gf); // Only in idle mode (stopped measurement)
|
||||
ResponseAppend_P(PSTR(",\"NOx\":{\"IdxOffset\":%d,\"LearningTimeOffset\":%d,\"GatingMaxDur\":%d,\"GainFctr\":%d}"),
|
||||
nox_io, nox_ltoh, nox_gmdm, nox_gf);
|
||||
}
|
||||
@@ -310,7 +323,7 @@ void CmndSen6xAltitude(void) {
|
||||
if (SEN6XDATA->features & SEN6X_CO2) {
|
||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload <= 3000)) {
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen6x->setSensorAltitude(XdrvMailbox.payload))) {
|
||||
if (CmndSen6xError(sen6x.setSensorAltitude(XdrvMailbox.payload))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -324,7 +337,7 @@ void CmndSen6xPressure(void) {
|
||||
// Sen6xPres <700..1200> - hPa
|
||||
if (SEN6XDATA->features & SEN6X_CO2) {
|
||||
if ((XdrvMailbox.payload >= 700) && (XdrvMailbox.payload <= 1200)) {
|
||||
if (CmndSen6xError(sen6x->setAmbientPressure(XdrvMailbox.payload))) {
|
||||
if (CmndSen6xError(sen6x.setAmbientPressure(XdrvMailbox.payload))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -340,7 +353,7 @@ void CmndSen6xTemperatureOffset(void) {
|
||||
int value[4] = { 0 };
|
||||
if (4 == ParseIntParameters(4, value)) {
|
||||
Sen6xStopStartMeasurement();
|
||||
if (!CmndSen6xError(sen6x->setTemperatureOffsetParameters(value[0], value[1], value[2], value[3]))) {
|
||||
if (!CmndSen6xError(sen6x.setTemperatureOffsetParameters(value[0], value[1], value[2], value[3]))) {
|
||||
ResponseCmndDone();
|
||||
}
|
||||
}
|
||||
@@ -353,7 +366,7 @@ void CmndSen6xTemperatureAcceleration(void) {
|
||||
uint32_t value[4] = { 0 };
|
||||
if (4 == ParseParameters(4, value)) {
|
||||
Sen6xStopStartMeasurement();
|
||||
if (!CmndSen6xError(sen6x->setTemperatureAccelerationParameters(value[0], value[1], value[2], value[3]))) {
|
||||
if (!CmndSen6xError(sen6x.setTemperatureAccelerationParameters(value[0], value[1], value[2], value[3]))) {
|
||||
ResponseCmndDone();
|
||||
}
|
||||
}
|
||||
@@ -370,21 +383,21 @@ void CmndSen6xCo2SelfCalibration(void) {
|
||||
delay(400); // Wait at least 1400ms
|
||||
uint16_t reference = XdrvMailbox.payload;
|
||||
uint16_t correction;
|
||||
if (!CmndSen6xError(sen6x->performForcedCo2Recalibration(reference, correction))) {
|
||||
if (!CmndSen6xError(sen6x.performForcedCo2Recalibration(reference, correction))) {
|
||||
ResponseCmndNumber(correction); // FRC = return_value - 0x8000. If the recalibration has failed this returned value is 0xFFFF.
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((0 == XdrvMailbox.payload) || (1 == XdrvMailbox.payload)) {
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen6x->setCo2SensorAutomaticSelfCalibration(XdrvMailbox.payload))) {
|
||||
if (CmndSen6xError(sen6x.setCo2SensorAutomaticSelfCalibration(XdrvMailbox.payload))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
else if (2 == XdrvMailbox.payload) {
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen63->sensorFactoryReset())) { // Implemented for STCC2 in SEN63C
|
||||
if (CmndSen6xError(sen63->sensorFactoryReset())) { // Implemented for STCC4 in SEN63C
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -402,7 +415,7 @@ void CmndSen6xVocAlgorithmTuningParams(void) {
|
||||
int value[6] = { 0 };
|
||||
if (6 == ParseIntParameters(6, value)) {
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen6x->setVocAlgorithmTuningParameters(value[0], value[1], value[2], value[3], value[4], value[5]))) {
|
||||
if (CmndSen6xError(sen6x.setVocAlgorithmTuningParameters(value[0], value[1], value[2], value[3], value[4], value[5]))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -421,7 +434,7 @@ void CmndSen6xNoxAlgorithmTuningParams(void) {
|
||||
Sen6xStopStartMeasurement();
|
||||
uint16_t ltgh = 12; // This parameter has no impact for NOx and must always be set to 12 hours
|
||||
uint16_t si = 50; // This parameter has no impact for NOx and must always be set to 50
|
||||
if (CmndSen6xError(sen6x->setNoxAlgorithmTuningParameters(value[0], value[1], ltgh, value[2], si, value[3]))) {
|
||||
if (CmndSen6xError(sen6x.setNoxAlgorithmTuningParameters(value[0], value[1], ltgh, value[2], si, value[3]))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -433,7 +446,7 @@ void CmndSen6xFanClean(void) {
|
||||
// Sen6xClean - This command triggers fan cleaning. The fan is set to the maximum speed
|
||||
// for 10 seconds and then automatically stopped.
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen6x->startFanCleaning())) {
|
||||
if (CmndSen6xError(sen6x.startFanCleaning())) {
|
||||
return;
|
||||
}
|
||||
SEN6XDATA->state = SEN6X_STATE_CLEAN_FAN_WAIT;
|
||||
@@ -444,7 +457,7 @@ void CmndSen6xSHTHeater(void) {
|
||||
// Sen6xHeat - This command activates the SHT sensor heater with 200mW for 1s.
|
||||
// The heater is then automatically deactivated again.
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen6x->activateShtHeater())) {
|
||||
if (CmndSen6xError(sen6x.activateShtHeater())) {
|
||||
return;
|
||||
}
|
||||
SEN6XDATA->state = SEN6X_STATE_SHT_HEATER_WAIT;
|
||||
@@ -458,12 +471,12 @@ void CmndSen6xVocState(void) {
|
||||
// TBD - Restore voc_state after a restart/power cycle - needs filesystem
|
||||
/*
|
||||
Sen6xStopStartMeasurement();
|
||||
if (CmndSen6xError(sen6x->setVocAlgorithmState(voc_state, sizeof(voc_state)))) {
|
||||
if (CmndSen6xError(sen6x.setVocAlgorithmState(voc_state, sizeof(voc_state)))) {
|
||||
return;
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
if (CmndSen6xError(sen6x->getVocAlgorithmState(voc_state, sizeof(voc_state)))) {
|
||||
if (CmndSen6xError(sen6x.getVocAlgorithmState(voc_state, sizeof(voc_state)))) {
|
||||
return;
|
||||
}
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("S6X: State %12_H"), voc_state);
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
xsns_120_stcc4.ino - Sensirion STCC4 CO₂ sensor support for Tasmota
|
||||
|
||||
SPDX-FileCopyrightText: 2026 Theo Arends
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-only
|
||||
*/
|
||||
|
||||
#ifdef USE_I2C
|
||||
#ifdef USE_STCC4
|
||||
/*********************************************************************************************\
|
||||
* Sensirion STCC4 CO₂ (and optional embedded SHT4x RH&T sensor)
|
||||
*
|
||||
* Commands:
|
||||
* Stcc - State information
|
||||
* StccPres - Set the pressure compensation in hPa (default 1013)
|
||||
* StccPres <400..1100>
|
||||
* StccRHT - Write temperature and humidity values (default 25°C and 50 %RH) when SHT4x is not present
|
||||
* StccRHT <temperature>,<humidity>
|
||||
* StccRHT <0..200>,<0..100>
|
||||
* StccCal - Re-calibrate CO₂ sensor (FRC)
|
||||
* StccCal <351..32000>
|
||||
* StccReset 1 - Reset the Forced recalibration (FRC) and ASC algorithm history
|
||||
*
|
||||
* I2C Address: 0x64 or 0x65
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define XSNS_120 120
|
||||
#define XI2C_99 99 // See I2CDEVICES.md
|
||||
|
||||
//#define SENSIRION_DEBUG // Adds 1k2 to code size
|
||||
|
||||
#include <SensirionI2cStcc4.h>
|
||||
|
||||
#define STCC4_STATE_READ_MEASUREMENT 0
|
||||
#define STCC4_STATE_START_MEASUREMENT 2 // Wait at least 2 seconds as otherwise see sporadic error 527 - Not enough data received
|
||||
|
||||
SensirionI2cStcc4 stcc4;
|
||||
|
||||
struct StccData_s {
|
||||
int16_t co2;
|
||||
uint16_t humidity;
|
||||
uint16_t temperature;
|
||||
uint16_t sensor_status;
|
||||
uint8_t state;
|
||||
} *StccData = nullptr;
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void StccStopStartMeasurement(void) {
|
||||
// Stop measurement and restart after 2 seconds
|
||||
stcc4.stopContinuousMeasurement(); // Performs internal delay(1200)
|
||||
StccData->state = STCC4_STATE_START_MEASUREMENT;
|
||||
}
|
||||
|
||||
bool StccError(const char* func, int error) {
|
||||
bool result = (error != 0);
|
||||
if (result) {
|
||||
#ifdef SENSIRION_DEBUG
|
||||
char error_msg[64];
|
||||
errorToString(error, error_msg, sizeof(error_msg));
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("STC: %s error %d %s"), func, error, error_msg);
|
||||
#else
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("STC: %s error %d"), func, error);
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CmndStccError(int error) {
|
||||
bool result = (error != 0);
|
||||
if (result) {
|
||||
ResponseCmnd();
|
||||
#ifdef SENSIRION_DEBUG
|
||||
char error_msg[64];
|
||||
errorToString(error, error_msg, sizeof(error_msg));
|
||||
ResponseAppend_P(PSTR("{\"Error\":\"%d %s\"}"), error, error_msg);
|
||||
#else
|
||||
ResponseAppend_P(PSTR("{\"Error\":%d}"), error);
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/********************************************************************************************/
|
||||
|
||||
void StccInit(void) {
|
||||
PowerOnDelay(20); // Sensor startup time (Time after power-on until I2C communication can be started)
|
||||
for (uint32_t address = STCC4_I2C_ADDR_64; address < STCC4_I2C_ADDR_64 +2; address++) {
|
||||
for (uint32_t bus = 0; bus < 2; bus++) {
|
||||
if (!I2cSetDevice(address, bus)) {
|
||||
// StccError("Scan", address + bus +1);
|
||||
continue;
|
||||
}
|
||||
stcc4.begin(I2cGetWire(bus), address);
|
||||
/*
|
||||
if (StccError("ExitSleep", stcc4.exitSleepMode())) { // Performs delay(5)
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
if (StccError("ForceStop", stcc4.stopContinuousMeasurement())) { // Performs delay(1200) if no error
|
||||
continue;
|
||||
}
|
||||
uint32_t product_id;
|
||||
uint64_t serial_number;
|
||||
if (StccError("Serialnumber", stcc4.getProductId(product_id, serial_number))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
StccData = (StccData_s *)calloc(1, sizeof(struct StccData_s));
|
||||
StccData->state = STCC4_STATE_START_MEASUREMENT;
|
||||
I2cSetActiveFound(address, "STCC4", bus);
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("STC: STCC4 serialnumber %_U"), &serial_number);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StccUpdate(void) {
|
||||
switch (StccData->state) {
|
||||
case STCC4_STATE_READ_MEASUREMENT:
|
||||
{
|
||||
// Use temp vars in case the read fails; Do not store invalid values.
|
||||
int16_t co2;
|
||||
uint16_t humidity;
|
||||
uint16_t temperature;
|
||||
uint16_t sensor_status;
|
||||
bool error = false;
|
||||
if (stcc4.readMeasurementRaw(co2, temperature, humidity, sensor_status)) {
|
||||
delay(150); // A failed read can be caused by clock shifting. We advise to retry after a delay of 150ms.
|
||||
error = StccError("Measurement", stcc4.readMeasurementRaw(co2, temperature, humidity, sensor_status));
|
||||
}
|
||||
if (!error) {
|
||||
StccData->co2 = co2;
|
||||
StccData->temperature = temperature;
|
||||
StccData->humidity = humidity;
|
||||
StccData->sensor_status = sensor_status;
|
||||
#ifdef USE_LIGHT
|
||||
LightSetSignal(CO2_LOW, CO2_HIGH, StccData->co2); // SetOption18 - Pair light signal with CO₂ sensor
|
||||
#endif // USE_LIGHT
|
||||
StccData->state = STCC4_STATE_START_MEASUREMENT; // Take at least 2 seconds between read as otherwise see sporadic error 527 - Not enough data received
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STCC4_STATE_START_MEASUREMENT:
|
||||
if (StccError("StartContinuous", stcc4.startContinuousMeasurement())) {
|
||||
StccData->state = STCC4_STATE_START_MEASUREMENT +2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (StccData->state) {
|
||||
StccData->state--;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
const char kStccCommands[] PROGMEM = "Stcc|" // Prefix
|
||||
"|Pres|RHT|Cal|Reset";
|
||||
|
||||
void (* const StccCommand[])(void) PROGMEM = {
|
||||
&CmndStccState, &CmndStccPressure, &CmndStccRHT, &CmndStccReset };
|
||||
|
||||
void CmndStccState(void) {
|
||||
StccStopStartMeasurement();
|
||||
uint32_t product_id;
|
||||
uint64_t serial_number;
|
||||
stcc4.getProductId(product_id, serial_number);
|
||||
Response_P(PSTR("{\"STCC4\":{\"Product\":\"%08X\",\"Serial\":%_U,\"Status\":\"%04X\"}"),
|
||||
product_id, &serial_number, StccData->sensor_status);
|
||||
}
|
||||
|
||||
void CmndStccPressure(void) {
|
||||
// StccPres - Set the pressure compensation in hPa (default 1013)
|
||||
// StccPres <400..1100>
|
||||
if ((XdrvMailbox.payload >= 400) && (XdrvMailbox.payload <= 1100)) {
|
||||
if (!CmndStccError(stcc4.setPressureCompensationRaw(XdrvMailbox.payload * 50))) {
|
||||
ResponseCmndDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CmndStccRHT(void) {
|
||||
// StccRHT - Write temperature and humidity values (default 25°C and 50 %RH) when SHT4x is not present
|
||||
// StccRHT <temperature>,<humidity>
|
||||
// StccRHT <0..200>,<0..100>
|
||||
uint32_t value[2] = { 0 };
|
||||
if (2 == ParseParameters(2, value)) {
|
||||
float raw_temperature = (((float)value[0] + 45.0) * 65535.0) / 175.0;
|
||||
float raw_humidity = (((float)value[1] + 6.0) * 65535.0) / 125.0;
|
||||
if (!CmndStccError(stcc4.setRhtCompensation((uint16_t)raw_temperature, (uint16_t)raw_humidity))) {
|
||||
ResponseCmndDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CmndStccCalibration(void) {
|
||||
// StccCal - Re-calibrate CO₂ sensor with target_value
|
||||
// StccCal <351..32000>
|
||||
if (XdrvMailbox.payload > 350) {
|
||||
StccStopStartMeasurement();
|
||||
int16_t reference = XdrvMailbox.payload;
|
||||
int16_t correction;
|
||||
if (!CmndStccError(stcc4.performForcedRecalibration(reference, correction))) {
|
||||
ResponseCmndNumber(correction); // FRC = return_value - 0x8000. If the recalibration has failed this returned value is 0xFFFF.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CmndStccReset(void) {
|
||||
// StccReset 1 - Reset the Forced recalibration (FRC) and ASC algorithm history
|
||||
if (1 == XdrvMailbox.payload) {
|
||||
StccStopStartMeasurement();
|
||||
uint16_t result;
|
||||
if (!CmndStccError(stcc4.performFactoryReset(result))) {
|
||||
ResponseCmndNumber(result); // If the factory reset has failed the returned value is not 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Presentation
|
||||
\*********************************************************************************************/
|
||||
|
||||
#define STCC4_INT_INVALID 0x7FFF
|
||||
|
||||
float StccInt16(int16_t value) {
|
||||
return (value == STCC4_INT_INVALID) ? NAN : value;
|
||||
}
|
||||
|
||||
void StccShow(bool json) {
|
||||
float co2 = StccInt16(StccData->co2); // Change invalid to null
|
||||
float humidity = ConvertHumidity(stcc4.signalRelativeHumidity(StccData->humidity));
|
||||
float temperature = ConvertTemp(stcc4.signalTemperature(StccData->temperature));
|
||||
|
||||
if (json) {
|
||||
ResponseAppend_P(PSTR(",\"STCC4\":{\"" D_JSON_CO2 "\":%0_f,"), &co2);
|
||||
ResponseAppendTHD(temperature, humidity);
|
||||
ResponseAppend_P(PSTR("}"));
|
||||
#ifdef USE_WEBSERVER
|
||||
} else {
|
||||
WSContentSend_PD(HTTP_SNS_F_CO2, "STCC4", &co2);
|
||||
WSContentSend_THD("STCC4", temperature, humidity);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xsns120(uint32_t function) {
|
||||
if (!I2cEnabled(XI2C_99)) { return false; }
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (FUNC_INIT == function) {
|
||||
StccInit();
|
||||
}
|
||||
else if (StccData) {
|
||||
switch (function) {
|
||||
case FUNC_EVERY_SECOND:
|
||||
StccUpdate();
|
||||
break;
|
||||
case FUNC_JSON_APPEND:
|
||||
StccShow(1);
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_SENSOR:
|
||||
StccShow(0);
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kStccCommands, StccCommand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_STCC4
|
||||
#endif // USE_I2C
|
||||
@@ -313,7 +313,7 @@ a_features = [[
|
||||
"USE_HX711_M5SCALES","USE_RX8010","USE_PCF85063","USE_ESP32_TWAI",
|
||||
"USE_C8_CO2_5K","USE_WIZMOTE","USE_V9240","USE_TELNET",
|
||||
"USE_XYZMODEM","USE_WIREGUARD","USE_AP33772S","USE_VID6608",
|
||||
"USE_AGS02MA","USE_SEN6X","USE_FM24CXX","",
|
||||
"USE_AGS02MA","USE_SEN6X","USE_FM24CXX","USE_STCC4",
|
||||
"","","","",
|
||||
"","","",""
|
||||
]]
|
||||
@@ -343,7 +343,7 @@ else:
|
||||
obj = json.load(fp)
|
||||
|
||||
def StartDecode():
|
||||
print ("\n*** decode-status.py v15.2.0.6 by Theo Arends and Jacek Ziolkowski ***")
|
||||
print ("\n*** decode-status.py v15.3.0.1 by Theo Arends and Jacek Ziolkowski ***")
|
||||
|
||||
# print("Decoding\n{}".format(obj))
|
||||
|
||||
|
||||