8static const char *
const TAG =
"sen6x";
10static constexpr uint8_t POLL_RETRIES = 24;
11static constexpr uint32_t I2C_READ_DELAY = 20;
12static constexpr uint32_t CMD_EXEC_DELAY = 20;
13static constexpr uint32_t POLL_INTERVAL = 50;
15static constexpr uint32_t TIMEOUT_POLL = 1;
16static constexpr uint32_t TIMEOUT_SETUP_STEP = 2;
17static constexpr uint16_t SEN6X_CMD_GET_DATA_READY_STATUS = 0x0202;
18static constexpr uint16_t SEN6X_CMD_GET_FIRMWARE_VERSION = 0xD100;
19static constexpr uint16_t SEN6X_CMD_GET_PRODUCT_NAME = 0xD014;
20static constexpr uint16_t SEN6X_CMD_GET_SERIAL_NUMBER = 0xD033;
22static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT = 0x0300;
23static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN62 = 0x04A3;
24static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN63C = 0x0471;
25static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN65 = 0x0446;
26static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN68 = 0x0467;
27static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN69C = 0x04B5;
29static constexpr uint16_t SEN6X_CMD_START_MEASUREMENTS = 0x0021;
30static constexpr uint16_t SEN6X_CMD_RESET = 0xD304;
31static constexpr uint16_t SEN6X_CMD_VOC_ALGORITHM_TUNING = 0x60D0;
32static constexpr uint16_t SEN6X_CMD_NOX_ALGORITHM_TUNING = 0x60E1;
34static inline void set_read_command_and_words(SEN6XComponent::Sen6xType
type, uint16_t &read_cmd, uint8_t &read_words) {
35 read_cmd = SEN6X_CMD_READ_MEASUREMENT;
38 case SEN6XComponent::SEN62:
39 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN62;
42 case SEN6XComponent::SEN63C:
43 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN63C;
46 case SEN6XComponent::SEN65:
47 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN65;
50 case SEN6XComponent::SEN66:
51 read_cmd = SEN6X_CMD_READ_MEASUREMENT;
54 case SEN6XComponent::SEN68:
55 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN68;
58 case SEN6XComponent::SEN69C:
59 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN69C;
68 ESP_LOGCONFIG(TAG,
"Setting up sen6x...");
74 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
82 uint16_t raw_serial_number[16];
83 if (!this->
get_register(SEN6X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 16, 20)) {
84 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
93 uint16_t raw_product_name[16];
94 if (!this->
get_register(SEN6X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
95 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
105 if (inferred_type == UNKNOWN) {
106 ESP_LOGE(TAG,
"Unknown product '%s'", this->
product_name_.c_str());
110 ESP_LOGD(TAG,
"Type inferred from product: %s", this->
product_name_.c_str());
111 }
else if (this->
sen6x_type_ != inferred_type && inferred_type != UNKNOWN) {
112 ESP_LOGW(TAG,
"Configured type (used) mismatches product '%s'", this->
product_name_.c_str());
121 if (this->voc_sensor_ && !has_voc_nox) {
122 ESP_LOGE(TAG,
"VOC requires SEN65, SEN66, SEN68, or SEN69C");
123 this->voc_sensor_ =
nullptr;
125 if (this->nox_sensor_ && !has_voc_nox) {
126 ESP_LOGE(TAG,
"NOx requires SEN65, SEN66, SEN68, or SEN69C");
127 this->nox_sensor_ =
nullptr;
129 if (this->co2_sensor_ && !has_co2) {
130 ESP_LOGE(TAG,
"CO2 requires SEN63C, SEN66, or SEN69C");
131 this->co2_sensor_ =
nullptr;
133 if (this->hcho_sensor_ && !has_hcho) {
134 ESP_LOGE(TAG,
"Formaldehyde requires SEN68 or SEN69C");
135 this->hcho_sensor_ =
nullptr;
140 uint16_t raw_firmware_version = 0;
141 if (!this->
get_register(SEN6X_CMD_GET_FIRMWARE_VERSION, raw_firmware_version, 20)) {
142 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
189 ESP_LOGE(TAG,
"Write 0x%04X failed, error %d", SEN6X_CMD_START_MEASUREMENTS, this->
last_error_);
196 ESP_LOGD(TAG,
"Initialized");
203 ESP_LOGE(TAG,
"Write 0x%04X failed, error %d", i2c_command, this->
last_error_);
220void SEN6XComponent::dump_config() {
227 this->
product_name_.c_str(), this->serial_number_.c_str(), this->firmware_version_major_,
228 this->firmware_version_minor_, this->address_);
229 LOG_UPDATE_INTERVAL(
this);
230 LOG_SENSOR(
" ",
"PM 1.0", this->pm_1_0_sensor_);
231 LOG_SENSOR(
" ",
"PM 2.5", this->pm_2_5_sensor_);
232 LOG_SENSOR(
" ",
"PM 4.0", this->pm_4_0_sensor_);
233 LOG_SENSOR(
" ",
"PM 10.0", this->pm_10_0_sensor_);
234 LOG_SENSOR(
" ",
"Temperature", this->temperature_sensor_);
235 LOG_SENSOR(
" ",
"Humidity", this->humidity_sensor_);
236 LOG_SENSOR(
" ",
"VOC", this->voc_sensor_);
237 LOG_SENSOR(
" ",
"NOx", this->nox_sensor_);
238 LOG_SENSOR(
" ",
"HCHO", this->hcho_sensor_);
239 LOG_SENSOR(
" ",
"CO2", this->co2_sensor_);
242void SEN6XComponent::update() {
274 ESP_LOGD(TAG,
"Data not ready");
277 ESP_LOGV(TAG,
"Data ready polling attempt %u",
283 ESP_LOGD(TAG,
"write data ready status error (%d)", this->
last_error_);
287 this->
set_timeout(TIMEOUT_POLL, I2C_READ_DELAY, [
this]() {
288 uint16_t raw_read_status;
289 if (!this->
read_data(&raw_read_status, 1)) {
291 ESP_LOGD(TAG,
"read data ready status error (%d)", this->
last_error_);
295 if ((raw_read_status & 0x0001) == 0) {
308 ESP_LOGD(TAG,
"Read measurement failed (%d)", this->
last_error_);
316 uint16_t measurements[10];
320 ESP_LOGD(TAG,
"Read data failed (%d)", this->
last_error_);
323 int8_t voc_index = -1;
324 int8_t nox_index = -1;
325 int8_t hcho_index = -1;
326 int8_t co2_index = -1;
327 bool co2_uint16 =
false;
359 float pm_1_0 = measurements[0] / 10.0f;
360 if (measurements[0] == 0xFFFF)
362 float pm_2_5 = measurements[1] / 10.0f;
363 if (measurements[1] == 0xFFFF)
365 float pm_4_0 = measurements[2] / 10.0f;
366 if (measurements[2] == 0xFFFF)
368 float pm_10_0 = measurements[3] / 10.0f;
369 if (measurements[3] == 0xFFFF)
371 float humidity =
static_cast<int16_t
>(measurements[4]) / 100.0f;
372 if (measurements[4] == 0x7FFF)
374 float temperature =
static_cast<int16_t
>(measurements[5]) / 200.0f;
375 if (measurements[5] == 0x7FFF)
383 if (voc_index >= 0) {
384 voc =
static_cast<int16_t
>(measurements[voc_index]) / 10.0f;
385 if (measurements[voc_index] == 0x7FFF)
388 if (nox_index >= 0) {
389 nox =
static_cast<int16_t
>(measurements[nox_index]) / 10.0f;
390 if (measurements[nox_index] == 0x7FFF)
394 if (hcho_index >= 0) {
395 const uint16_t hcho_raw = measurements[hcho_index];
396 hcho = hcho_raw / 10.0f;
397 if (hcho_raw == 0xFFFF)
401 if (co2_index >= 0) {
403 const uint16_t co2_raw = measurements[co2_index];
404 co2 =
static_cast<float>(co2_raw);
405 if (co2_raw == 0xFFFF)
408 const int16_t co2_raw =
static_cast<int16_t
>(measurements[co2_index]);
409 co2 =
static_cast<float>(co2_raw);
410 if (co2_raw == 0x7FFF)
416 ESP_LOGD(TAG,
"Startup delay, ignoring values");
421 if (this->pm_1_0_sensor_ !=
nullptr)
422 this->pm_1_0_sensor_->publish_state(pm_1_0);
423 if (this->pm_2_5_sensor_ !=
nullptr)
424 this->pm_2_5_sensor_->publish_state(pm_2_5);
425 if (this->pm_4_0_sensor_ !=
nullptr)
426 this->pm_4_0_sensor_->publish_state(pm_4_0);
427 if (this->pm_10_0_sensor_ !=
nullptr)
428 this->pm_10_0_sensor_->publish_state(pm_10_0);
429 if (this->temperature_sensor_ !=
nullptr)
430 this->temperature_sensor_->publish_state(
temperature);
431 if (this->humidity_sensor_ !=
nullptr)
432 this->humidity_sensor_->publish_state(humidity);
433 if (this->voc_sensor_ !=
nullptr)
434 this->voc_sensor_->publish_state(voc);
435 if (this->nox_sensor_ !=
nullptr)
436 this->nox_sensor_->publish_state(nox);
437 if (this->hcho_sensor_ !=
nullptr)
438 this->hcho_sensor_->publish_state(hcho);
439 if (this->co2_sensor_ !=
nullptr)
440 this->co2_sensor_->publish_state(co2);
446 if (product_name ==
"SEN62")
448 if (product_name ==
"SEN63C")
450 if (product_name ==
"SEN65")
452 if (product_name ==
"SEN66")
454 if (product_name ==
"SEN68")
456 if (product_name ==
"SEN69C")