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 POLL_INTERVAL = 50;
14static constexpr uint32_t TIMEOUT_POLL = 1;
15static constexpr uint16_t SEN6X_CMD_GET_DATA_READY_STATUS = 0x0202;
16static constexpr uint16_t SEN6X_CMD_GET_FIRMWARE_VERSION = 0xD100;
17static constexpr uint16_t SEN6X_CMD_GET_PRODUCT_NAME = 0xD014;
18static constexpr uint16_t SEN6X_CMD_GET_SERIAL_NUMBER = 0xD033;
20static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT = 0x0300;
21static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN62 = 0x04A3;
22static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN63C = 0x0471;
23static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN65 = 0x0446;
24static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN68 = 0x0467;
25static constexpr uint16_t SEN6X_CMD_READ_MEASUREMENT_SEN69C = 0x04B5;
27static constexpr uint16_t SEN6X_CMD_START_MEASUREMENTS = 0x0021;
28static constexpr uint16_t SEN6X_CMD_RESET = 0xD304;
30static inline void set_read_command_and_words(SEN6XComponent::Sen6xType
type, uint16_t &read_cmd, uint8_t &read_words) {
31 read_cmd = SEN6X_CMD_READ_MEASUREMENT;
34 case SEN6XComponent::SEN62:
35 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN62;
38 case SEN6XComponent::SEN63C:
39 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN63C;
42 case SEN6XComponent::SEN65:
43 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN65;
46 case SEN6XComponent::SEN66:
47 read_cmd = SEN6X_CMD_READ_MEASUREMENT;
50 case SEN6XComponent::SEN68:
51 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN68;
54 case SEN6XComponent::SEN69C:
55 read_cmd = SEN6X_CMD_READ_MEASUREMENT_SEN69C;
64 ESP_LOGCONFIG(TAG,
"Setting up sen6x...");
70 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
78 uint16_t raw_serial_number[16];
79 if (!this->
get_register(SEN6X_CMD_GET_SERIAL_NUMBER, raw_serial_number, 16, 20)) {
80 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
89 uint16_t raw_product_name[16];
90 if (!this->
get_register(SEN6X_CMD_GET_PRODUCT_NAME, raw_product_name, 16, 20)) {
91 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
101 if (inferred_type == UNKNOWN) {
102 ESP_LOGE(TAG,
"Unknown product '%s'", this->
product_name_.c_str());
106 ESP_LOGD(TAG,
"Type inferred from product: %s", this->
product_name_.c_str());
107 }
else if (this->
sen6x_type_ != inferred_type && inferred_type != UNKNOWN) {
108 ESP_LOGW(TAG,
"Configured type (used) mismatches product '%s'", this->
product_name_.c_str());
117 if (this->voc_sensor_ && !has_voc_nox) {
118 ESP_LOGE(TAG,
"VOC requires SEN65, SEN66, SEN68, or SEN69C");
119 this->voc_sensor_ =
nullptr;
121 if (this->nox_sensor_ && !has_voc_nox) {
122 ESP_LOGE(TAG,
"NOx requires SEN65, SEN66, SEN68, or SEN69C");
123 this->nox_sensor_ =
nullptr;
125 if (this->co2_sensor_ && !has_co2) {
126 ESP_LOGE(TAG,
"CO2 requires SEN63C, SEN66, or SEN69C");
127 this->co2_sensor_ =
nullptr;
129 if (this->hcho_sensor_ && !has_hcho) {
130 ESP_LOGE(TAG,
"Formaldehyde requires SEN68 or SEN69C");
131 this->hcho_sensor_ =
nullptr;
136 uint16_t raw_firmware_version = 0;
137 if (!this->
get_register(SEN6X_CMD_GET_FIRMWARE_VERSION, raw_firmware_version, 20)) {
138 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
147 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
154 ESP_LOGD(TAG,
"Initialized");
161void SEN6XComponent::dump_config() {
168 this->
product_name_.c_str(), this->serial_number_.c_str(), this->firmware_version_major_,
169 this->firmware_version_minor_, this->address_);
170 LOG_UPDATE_INTERVAL(
this);
171 LOG_SENSOR(
" ",
"PM 1.0", this->pm_1_0_sensor_);
172 LOG_SENSOR(
" ",
"PM 2.5", this->pm_2_5_sensor_);
173 LOG_SENSOR(
" ",
"PM 4.0", this->pm_4_0_sensor_);
174 LOG_SENSOR(
" ",
"PM 10.0", this->pm_10_0_sensor_);
175 LOG_SENSOR(
" ",
"Temperature", this->temperature_sensor_);
176 LOG_SENSOR(
" ",
"Humidity", this->humidity_sensor_);
177 LOG_SENSOR(
" ",
"VOC", this->voc_sensor_);
178 LOG_SENSOR(
" ",
"NOx", this->nox_sensor_);
179 LOG_SENSOR(
" ",
"HCHO", this->hcho_sensor_);
180 LOG_SENSOR(
" ",
"CO2", this->co2_sensor_);
183void SEN6XComponent::update() {
215 ESP_LOGD(TAG,
"Data not ready");
218 ESP_LOGV(TAG,
"Data ready polling attempt %u",
224 ESP_LOGD(TAG,
"write data ready status error (%d)", this->
last_error_);
228 this->
set_timeout(TIMEOUT_POLL, I2C_READ_DELAY, [
this]() {
229 uint16_t raw_read_status;
230 if (!this->
read_data(&raw_read_status, 1)) {
232 ESP_LOGD(TAG,
"read data ready status error (%d)", this->
last_error_);
236 if ((raw_read_status & 0x0001) == 0) {
249 ESP_LOGD(TAG,
"Read measurement failed (%d)", this->
last_error_);
257 uint16_t measurements[10];
261 ESP_LOGD(TAG,
"Read data failed (%d)", this->
last_error_);
264 int8_t voc_index = -1;
265 int8_t nox_index = -1;
266 int8_t hcho_index = -1;
267 int8_t co2_index = -1;
268 bool co2_uint16 =
false;
300 float pm_1_0 = measurements[0] / 10.0f;
301 if (measurements[0] == 0xFFFF)
303 float pm_2_5 = measurements[1] / 10.0f;
304 if (measurements[1] == 0xFFFF)
306 float pm_4_0 = measurements[2] / 10.0f;
307 if (measurements[2] == 0xFFFF)
309 float pm_10_0 = measurements[3] / 10.0f;
310 if (measurements[3] == 0xFFFF)
312 float humidity =
static_cast<int16_t
>(measurements[4]) / 100.0f;
313 if (measurements[4] == 0x7FFF)
315 float temperature =
static_cast<int16_t
>(measurements[5]) / 200.0f;
316 if (measurements[5] == 0x7FFF)
324 if (voc_index >= 0) {
325 voc =
static_cast<int16_t
>(measurements[voc_index]) / 10.0f;
326 if (measurements[voc_index] == 0x7FFF)
329 if (nox_index >= 0) {
330 nox =
static_cast<int16_t
>(measurements[nox_index]) / 10.0f;
331 if (measurements[nox_index] == 0x7FFF)
335 if (hcho_index >= 0) {
336 const uint16_t hcho_raw = measurements[hcho_index];
337 hcho = hcho_raw / 10.0f;
338 if (hcho_raw == 0xFFFF)
342 if (co2_index >= 0) {
344 const uint16_t co2_raw = measurements[co2_index];
345 co2 =
static_cast<float>(co2_raw);
346 if (co2_raw == 0xFFFF)
349 const int16_t co2_raw =
static_cast<int16_t
>(measurements[co2_index]);
350 co2 =
static_cast<float>(co2_raw);
351 if (co2_raw == 0x7FFF)
357 ESP_LOGD(TAG,
"Startup delay, ignoring values");
362 if (this->pm_1_0_sensor_ !=
nullptr)
363 this->pm_1_0_sensor_->publish_state(pm_1_0);
364 if (this->pm_2_5_sensor_ !=
nullptr)
365 this->pm_2_5_sensor_->publish_state(pm_2_5);
366 if (this->pm_4_0_sensor_ !=
nullptr)
367 this->pm_4_0_sensor_->publish_state(pm_4_0);
368 if (this->pm_10_0_sensor_ !=
nullptr)
369 this->pm_10_0_sensor_->publish_state(pm_10_0);
370 if (this->temperature_sensor_ !=
nullptr)
371 this->temperature_sensor_->publish_state(
temperature);
372 if (this->humidity_sensor_ !=
nullptr)
373 this->humidity_sensor_->publish_state(humidity);
374 if (this->voc_sensor_ !=
nullptr)
375 this->voc_sensor_->publish_state(voc);
376 if (this->nox_sensor_ !=
nullptr)
377 this->nox_sensor_->publish_state(nox);
378 if (this->hcho_sensor_ !=
nullptr)
379 this->hcho_sensor_->publish_state(hcho);
380 if (this->co2_sensor_ !=
nullptr)
381 this->co2_sensor_->publish_state(co2);
387 if (product_name ==
"SEN62")
389 if (product_name ==
"SEN63C")
391 if (product_name ==
"SEN65")
393 if (product_name ==
"SEN66")
395 if (product_name ==
"SEN68")
397 if (product_name ==
"SEN69C")