Added C8-CO2-5K CO2 Sensor (#22905)

* Added the C8-CO2-5K CO2 sensor.

* Small improvements

* Renamed one var with incorrect name.
This commit is contained in:
Jeroen Vermeulen 2025-01-31 09:34:55 +01:00 committed by GitHub
parent 9d963ce599
commit 34b173e953
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 228 additions and 4 deletions

View File

@ -234,6 +234,7 @@ Note: the `minimal` variant is not listed as it shouldn't be used outside of the
| USE_HC8 | - | - / - | - | - | - | - |
| USE_PIPSOLAR | - | - / - | - | - | - | - |
| USE_WOOLIIS | - | - / - | - | - | - | - |
| USE_C8_CO2_5K | - | - / - | - | - | - | - |
| | | | | | | |
| USE_NRF24 | - | - / - | - | - | - | - |
| USE_MIBLE | - | - / - | - | - | - | - |

View File

@ -230,6 +230,7 @@ In addition to @arendst the following code is mainly owned by:
| xsns_113_hc8 | Daniel Maier
| xsns_114_amsx915 | Bastian Urschel
| xsns_115_wooliis | Luca Melette
| xsns_117_c8_co2_5k | @jeroenvermeulen
| |
| xsns_127_esp32_sensors | @arendst
| |

View File

@ -50,6 +50,8 @@ const be_const_member_t lv_gpio_constants[] = {
{ "BS814_DAT", (int32_t) GPIO_BS814_DAT },
{ "BUZZER", (int32_t) GPIO_BUZZER },
{ "BUZZER_INV", (int32_t) GPIO_BUZZER_INV },
{ "C8_CO2_5K_TX", (int32_t) GPIO_C8_CO2_5K_TX },
{ "C8_CO2_5K_RX", (int32_t) GPIO_C8_CO2_5K_RX },
{ "CC1101_GDO0", (int32_t) GPIO_CC1101_GDO0 },
{ "CC1101_GDO2", (int32_t) GPIO_CC1101_GDO2 },
{ "CHANGE", CHANGE },

View File

@ -229,6 +229,7 @@ enum UserSelectablePins {
GPIO_I2C_SER_TX, GPIO_I2C_SER_RX, // I2C via Serial using SC18IM704 protocol (xdrv74)
GPIO_TM1640CLK, GPIO_TM1640DIN, // TM1640 (16 x seven-segment LED controler)
GPIO_TWAI_TX, GPIO_TWAI_RX, GPIO_TWAI_BO, GPIO_TWAI_CLK, // ESP32 TWAI serial interface
GPIO_C8_CO2_5K_TX, GPIO_C8_CO2_5K_RX, // C8-CO2-5K CO2 Sensor
GPIO_SENSOR_END };
// Error as warning to rethink GPIO usage with max 2045
@ -504,7 +505,8 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_LD2410S_TX "|" D_SENSOR_LD2410S_RX "|"
D_SENSOR_I2C_SER_TX "|" D_SENSOR_I2C_SER_RX "|"
D_SENSOR_TM1640_CLK "|" D_SENSOR_TM1640_DIN "|"
D_SENSOR_TWAI_TX "|" D_SENSOR_TWAI_RX "|" D_SENSOR_TWAI_BO "|" D_SENSOR_TWAI_CLK
D_SENSOR_TWAI_TX "|" D_SENSOR_TWAI_RX "|" D_SENSOR_TWAI_BO "|" D_SENSOR_TWAI_CLK "|"
D_SENSOR_C8_CO2_5K_TX "|" D_SENSOR_C8_CO2_5K_RX
;
const char kSensorNamesFixed[] PROGMEM =
@ -1141,6 +1143,11 @@ const uint16_t kGpioNiceList[] PROGMEM = {
#ifdef USE_WOOLIIS
AGPIO(GPIO_WOOLIIS_RX), // Wooliis Battery capacity monitor Serial interface
#endif
#ifdef USE_C8_CO2_5K
AGPIO(GPIO_C8_CO2_5K_TX), // SC8-CO2-5K Serial interface
AGPIO(GPIO_C8_CO2_5K_RX), // SC8-CO2-5K Serial interface
#endif
#ifdef ESP32
#ifdef USE_ESP32_TWAI

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 Rx"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1028,6 +1028,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 - RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis - RX"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1022,6 +1022,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "А"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "A"

View File

@ -1021,6 +1021,8 @@
#define D_SENSOR_LOX_O2_RX "LoxO2 RX"
#define D_GPIO_MAGIC_SWITCH "MagicSwitch"
#define D_SENSOR_WOOLIIS_RX "Wooliis Rx"
#define D_SENSOR_C8_CO2_5K_RX "C8-CO2-5K Rx"
#define D_SENSOR_C8_CO2_5K_TX "C8-CO2-5K Tx"
// Units
#define D_UNIT_AMPERE "安培"

View File

@ -902,6 +902,7 @@
// #define GM861_DECODE_AIM // Decode AIM-id (+0k3 code)
// #define GM861_HEARTBEAT // Enable heartbeat (+0k2 code)
//#define USE_WOOLIIS // Add support for Wooliis Hall Effect Coulometer or Battery capacity monitor (+1k6 code)
//#define USE_C8_CO2_5K // Add support for C8-CO2-5K CO2 Sensor (+4k3 code)
//#define USE_DALI // Add support for DALI gateway (+5k code)
// -- Power monitoring sensors --------------------

View File

@ -937,7 +937,9 @@ constexpr uint32_t feature[] = {
#ifdef USE_ESP32_TWAI
0x00000800 | // xdrv_91_esp32_twai.ino
#endif
// 0x00001000 | //
#ifdef USE_C8_CO2_5K
0x00001000 | // xsns_117_c8_co2_5k.ino
#endif
// 0x00002000 | //
// 0x00004000 | //
// 0x00008000 | //

View File

@ -0,0 +1,153 @@
/*
xsns_117_c8_co2_5k.ino - SC8-CO2-5K CO2 sensor support for Tasmota
Copyright (C) 2025 Jeroen Vermeulen
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_C8_CO2_5K
/*********************************************************************************************\
* SC8-CO2-5K - CO2 sensor
*
* Inspired by:
* https://community.openenergymonitor.org/uploads/short-url/tDiJ3EWtv7OlcHZnNgX3dFv8Cpv.pdf
* https://spezifisch.codeberg.page/posts/2022-08-23/co2-sensor-reverse-engineering/
* https://github.com/Jaron0211/C8_master
\********************************************************************************************/
#define XSNS_117 117
#include <TasmotaSerial.h>
#ifndef CO2_LOW
#define CO2_LOW 800 // Below this CO2 value show green light
#endif
#ifndef CO2_HIGH
#define CO2_HIGH 1200 // Above this CO2 value show red light
#endif
#define C8CO2_READ_TIMEOUT 400
const char c8cp2_type[] = "C8-CO2-5K";
static uint8_t c8co2_buffer[16] = {0};
TasmotaSerial *c8co2_serial = nullptr;
uint16_t c8co2_ppm = 0;
void C8CO2_Init()
{
if (PinUsed(GPIO_C8_CO2_5K_RX))
{
int txPin = PinUsed(GPIO_C8_CO2_5K_TX) ? Pin(GPIO_C8_CO2_5K_TX) : -1;
c8co2_serial = new TasmotaSerial(Pin(GPIO_C8_CO2_5K_RX), txPin, 1);
if (c8co2_serial->begin(9600))
{
if (c8co2_serial->hardwareSerial())
ClaimSerial();
#ifdef ESP32
AddLog(LOG_LEVEL_DEBUG, PSTR("C8-CO2-5K: Serial UART%d"), c8co2_serial->getUart());
#endif
}
AddLog(LOG_LEVEL_DEBUG, PSTR("C8-CO2-5K: Sensor serial ready"));
}
}
void C8CO2_EverySecond()
{
unsigned long start = millis();
while ((millis() - start) < C8CO2_READ_TIMEOUT && c8co2_serial->available() > 0)
{
c8co2_buffer[0] = c8co2_buffer[1];
c8co2_buffer[1] = c8co2_serial->read();
if (c8co2_buffer[0] != 0x42 || c8co2_buffer[1] != 0x4D) // Check for header bytes
continue;
if (c8co2_serial->readBytes(&c8co2_buffer[2], 14) != 14)
{
AddLog(LOG_LEVEL_INFO, PSTR("C8-CO2-5K: ERROR Incomplete data received"));
return;
}
uint8_t sum = 0;
for (int i = 0; i < 15; i++)
{
sum += c8co2_buffer[i];
}
if (sum != c8co2_buffer[15])
{
AddLog(LOG_LEVEL_INFO, PSTR("C8-CO2-5K: ERROR Checksum mismatch"));
return;
}
c8co2_ppm = (c8co2_buffer[6] << 8) | c8co2_buffer[7];
AddLog(LOG_LEVEL_DEBUG, PSTR("C8-CO2-5K Sensor: %d ppm"), c8co2_ppm);
#ifdef USE_LIGHT
LightSetSignal(CO2_LOW, CO2_HIGH, c8co2_ppm);
#endif // USE_LIGHT
return;
}
}
void C8CO2_Show(bool json)
{
if (json)
{
ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_CO2 "\":%d}"), c8cp2_type, c8co2_ppm);
#ifdef USE_DOMOTICZ
if (0 == TasmotaGlobal.tele_period)
{
DomoticzSensor(DZ_AIRQUALITY, c8co2_ppm);
}
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
}
else
{
WSContentSend_PD(HTTP_SNS_CO2, c8cp2_type, c8co2_ppm);
#endif // USE_WEBSERVER
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns117(uint32_t function)
{
if (FUNC_INIT == function)
{
C8CO2_Init();
}
else if (c8co2_serial)
{
switch (function)
{
case FUNC_EVERY_SECOND:
C8CO2_EverySecond();
break;
case FUNC_JSON_APPEND:
C8CO2_Show(1);
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
C8CO2_Show(0);
break;
#endif // USE_WEBSERVER
}
}
return false;
}
#endif // USE_C8_CO2_5K

View File

@ -310,7 +310,7 @@ a_features = [[
"USE_MAGIC_SWITCH","USE_PIPSOLAR","USE_GPIO_VIEWER","USE_AMSX915",
"USE_SPI_LORA","USE_SPL06_007","USE_QMP6988","USE_WOOLIIS",
"USE_HX711_M5SCALES","USE_RX8010","USE_PCF85063","USE_ESP32_TWAI",
"","","","",
"USE_C8_CO2_5K","","","",
"","","","",
"","","","",
"","","","",

View File

@ -367,4 +367,7 @@ RN2XX3_RST = GPIO_RN2XX3_RST
WOOLIIS_RX = GPIO_WOOLIIS_RX
C8_CO2_5K_TX = GPIO_C8_CO2_5K_TX
C8_CO2_5K_RX = GPIO_C8_CO2_5K_RX
SENSOR_END = GPIO_SENSOR_END