From b8ba26787e80f6b05107401c21b63e4b4e28b32b Mon Sep 17 00:00:00 2001 From: Thomas Rupprecht Date: Tue, 29 Apr 2025 02:24:48 +0200 Subject: [PATCH] [pmsx003] Refactor Imports, Extract Constants, Improve Data Handling & Logging (#8344) --- CODEOWNERS | 1 + esphome/components/pmsx003/pmsx003.cpp | 402 +++++++++++-------------- esphome/components/pmsx003/pmsx003.h | 74 +++-- esphome/components/pmsx003/sensor.py | 32 +- 4 files changed, 251 insertions(+), 258 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6f5eae1a9c..06d3601858 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -328,6 +328,7 @@ esphome/components/pipsolar/* @andreashergert1984 esphome/components/pm1006/* @habbie esphome/components/pm2005/* @andrewjswan esphome/components/pmsa003i/* @sjtrny +esphome/components/pmsx003/* @ximex esphome/components/pmwcs3/* @SeByDocKy esphome/components/pn532/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index de2b23b8eb..11626768d8 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -6,45 +6,39 @@ namespace pmsx003 { static const char *const TAG = "pmsx003"; -void PMSX003Component::set_pm_1_0_std_sensor(sensor::Sensor *pm_1_0_std_sensor) { - pm_1_0_std_sensor_ = pm_1_0_std_sensor; -} -void PMSX003Component::set_pm_2_5_std_sensor(sensor::Sensor *pm_2_5_std_sensor) { - pm_2_5_std_sensor_ = pm_2_5_std_sensor; -} -void PMSX003Component::set_pm_10_0_std_sensor(sensor::Sensor *pm_10_0_std_sensor) { - pm_10_0_std_sensor_ = pm_10_0_std_sensor; -} +static const uint8_t START_CHARACTER_1 = 0x42; +static const uint8_t START_CHARACTER_2 = 0x4D; -void PMSX003Component::set_pm_1_0_sensor(sensor::Sensor *pm_1_0_sensor) { pm_1_0_sensor_ = pm_1_0_sensor; } -void PMSX003Component::set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor) { pm_2_5_sensor_ = pm_2_5_sensor; } -void PMSX003Component::set_pm_10_0_sensor(sensor::Sensor *pm_10_0_sensor) { pm_10_0_sensor_ = pm_10_0_sensor; } +static const uint16_t PMS_STABILISING_MS = 30000; // time taken for the sensor to become stable after power on in ms -void PMSX003Component::set_pm_particles_03um_sensor(sensor::Sensor *pm_particles_03um_sensor) { - pm_particles_03um_sensor_ = pm_particles_03um_sensor; -} -void PMSX003Component::set_pm_particles_05um_sensor(sensor::Sensor *pm_particles_05um_sensor) { - pm_particles_05um_sensor_ = pm_particles_05um_sensor; -} -void PMSX003Component::set_pm_particles_10um_sensor(sensor::Sensor *pm_particles_10um_sensor) { - pm_particles_10um_sensor_ = pm_particles_10um_sensor; -} -void PMSX003Component::set_pm_particles_25um_sensor(sensor::Sensor *pm_particles_25um_sensor) { - pm_particles_25um_sensor_ = pm_particles_25um_sensor; -} -void PMSX003Component::set_pm_particles_50um_sensor(sensor::Sensor *pm_particles_50um_sensor) { - pm_particles_50um_sensor_ = pm_particles_50um_sensor; -} -void PMSX003Component::set_pm_particles_100um_sensor(sensor::Sensor *pm_particles_100um_sensor) { - pm_particles_100um_sensor_ = pm_particles_100um_sensor; -} +static const uint16_t PMS_CMD_MEASUREMENT_MODE_PASSIVE = + 0x0000; // use `PMS_CMD_MANUAL_MEASUREMENT` to trigger a measurement +static const uint16_t PMS_CMD_MEASUREMENT_MODE_ACTIVE = 0x0001; // automatically perform measurements +static const uint16_t PMS_CMD_SLEEP_MODE_SLEEP = 0x0000; // go to sleep mode +static const uint16_t PMS_CMD_SLEEP_MODE_WAKEUP = 0x0001; // wake up from sleep mode -void PMSX003Component::set_temperature_sensor(sensor::Sensor *temperature_sensor) { - temperature_sensor_ = temperature_sensor; -} -void PMSX003Component::set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; } -void PMSX003Component::set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sensor) { - formaldehyde_sensor_ = formaldehyde_sensor; +void PMSX003Component::dump_config() { + ESP_LOGCONFIG(TAG, "PMSX003:"); + LOG_SENSOR(" ", "PM1.0STD", this->pm_1_0_std_sensor_); + LOG_SENSOR(" ", "PM2.5STD", this->pm_2_5_std_sensor_); + LOG_SENSOR(" ", "PM10.0STD", this->pm_10_0_std_sensor_); + + LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_); + LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); + LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_); + + LOG_SENSOR(" ", "PM0.3um", this->pm_particles_03um_sensor_); + LOG_SENSOR(" ", "PM0.5um", this->pm_particles_05um_sensor_); + LOG_SENSOR(" ", "PM1.0um", this->pm_particles_10um_sensor_); + LOG_SENSOR(" ", "PM2.5um", this->pm_particles_25um_sensor_); + LOG_SENSOR(" ", "PM5.0um", this->pm_particles_50um_sensor_); + LOG_SENSOR(" ", "PM10.0um", this->pm_particles_100um_sensor_); + + LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_); + + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); + this->check_uart_settings(9600); } void PMSX003Component::loop() { @@ -55,8 +49,8 @@ void PMSX003Component::loop() { // need to keep track of what state we're in. if (this->update_interval_ > PMS_STABILISING_MS) { if (this->initialised_ == 0) { - this->send_command_(PMS_CMD_AUTO_MANUAL, 0); - this->send_command_(PMS_CMD_ON_STANDBY, 1); + this->send_command_(PMS_CMD_MEASUREMENT_MODE, PMS_CMD_MEASUREMENT_MODE_PASSIVE); + this->send_command_(PMS_CMD_SLEEP_MODE, PMS_CMD_SLEEP_MODE_WAKEUP); this->initialised_ = 1; } switch (this->state_) { @@ -66,7 +60,7 @@ void PMSX003Component::loop() { return; this->state_ = PMSX003_STATE_STABILISING; - this->send_command_(PMS_CMD_ON_STANDBY, 1); + this->send_command_(PMS_CMD_SLEEP_MODE, PMS_CMD_SLEEP_MODE_WAKEUP); this->fan_on_time_ = now; return; case PMSX003_STATE_STABILISING: @@ -77,7 +71,7 @@ void PMSX003Component::loop() { while (this->available()) this->read_byte(&this->data_[0]); // Trigger a new read - this->send_command_(PMS_CMD_TRIG_MANUAL, 0); + this->send_command_(PMS_CMD_MANUAL_MEASUREMENT, 0); this->state_ = PMSX003_STATE_WAITING; break; case PMSX003_STATE_WAITING: @@ -116,242 +110,212 @@ void PMSX003Component::loop() { } } } -float PMSX003Component::get_setup_priority() const { return setup_priority::DATA; } + optional PMSX003Component::check_byte_() { - uint8_t index = this->data_index_; - uint8_t byte = this->data_[index]; + const uint8_t index = this->data_index_; + const uint8_t byte = this->data_[index]; - if (index == 0) - return byte == 0x42; - - if (index == 1) - return byte == 0x4D; - - if (index == 2) - return true; - - uint16_t payload_length = this->get_16_bit_uint_(2); - if (index == 3) { - bool length_matches = false; - switch (this->type_) { - case PMSX003_TYPE_X003: - length_matches = payload_length == 28 || payload_length == 20; - break; - case PMSX003_TYPE_5003T: - case PMSX003_TYPE_5003S: - length_matches = payload_length == 28; - break; - case PMSX003_TYPE_5003ST: - length_matches = payload_length == 36; - break; + if (index == 0 || index == 1) { + const uint8_t start_char = index == 0 ? START_CHARACTER_1 : START_CHARACTER_2; + if (byte == start_char) { + return true; } - if (!length_matches) { - ESP_LOGW(TAG, "PMSX003 length %u doesn't match. Are you using the correct PMSX003 type?", payload_length); - return false; - } + ESP_LOGW(TAG, "Start character %u mismatch: 0x%02X != 0x%02X", index + 1, byte, START_CHARACTER_1); + return false; + } + + if (index == 2) { return true; } - // start (16bit) + length (16bit) + DATA (payload_length-2 bytes) + checksum (16bit) - uint8_t total_size = 4 + payload_length; + const uint16_t payload_length = this->get_16_bit_uint_(2); + if (index == 3) { + if (this->check_payload_length_(payload_length)) { + return true; + } else { + ESP_LOGW(TAG, "Payload length %u doesn't match. Are you using the correct PMSX003 type?", payload_length); + return false; + } + } - if (index < total_size - 1) + // start (16bit) + length (16bit) + DATA (payload_length - 16bit) + checksum (16bit) + const uint16_t total_size = 4 + payload_length; + + if (index < total_size - 1) { return true; + } // checksum is without checksum bytes uint16_t checksum = 0; - for (uint8_t i = 0; i < total_size - 2; i++) + for (uint16_t i = 0; i < total_size - 2; i++) { checksum += this->data_[i]; + } - uint16_t check = this->get_16_bit_uint_(total_size - 2); + const uint16_t check = this->get_16_bit_uint_(total_size - 2); if (checksum != check) { - ESP_LOGW(TAG, "PMSX003 checksum mismatch! 0x%02X!=0x%02X", checksum, check); + ESP_LOGW(TAG, "PMSX003 checksum mismatch! 0x%02X != 0x%02X", checksum, check); return false; } return {}; } -void PMSX003Component::send_command_(uint8_t cmd, uint16_t data) { - this->data_index_ = 0; - this->data_[data_index_++] = 0x42; - this->data_[data_index_++] = 0x4D; - this->data_[data_index_++] = cmd; - this->data_[data_index_++] = (data >> 8) & 0xFF; - this->data_[data_index_++] = (data >> 0) & 0xFF; - int sum = 0; - for (int i = 0; i < data_index_; i++) { - sum += this->data_[i]; +bool PMSX003Component::check_payload_length_(uint16_t payload_length) { + switch (this->type_) { + case PMSX003_TYPE_X003: + // The expected payload length is typically 28 bytes. + // However, a 20-byte payload check was already present in the code. + // No official documentation was found confirming this. + // Retaining this check to avoid breaking existing behavior. + return payload_length == 28 || payload_length == 20; // 2*13+2 + case PMSX003_TYPE_5003T: + case PMSX003_TYPE_5003S: + return payload_length == 28; // 2*13+2 (Data 13 not set/reserved) + case PMSX003_TYPE_5003ST: + return payload_length == 36; // 2*17+2 (Data 16 not set/reserved) } - this->data_[data_index_++] = (sum >> 8) & 0xFF; - this->data_[data_index_++] = (sum >> 0) & 0xFF; - for (int i = 0; i < data_index_; i++) { - this->write_byte(this->data_[i]); + return false; +} + +void PMSX003Component::send_command_(PMSX0003Command cmd, uint16_t data) { + uint8_t send_data[7] = { + START_CHARACTER_1, // Start Byte 1 + START_CHARACTER_2, // Start Byte 2 + cmd, // Command + uint8_t((data >> 8) & 0xFF), // Data 1 + uint8_t((data >> 0) & 0xFF), // Data 2 + 0, // Verify Byte 1 + 0, // Verify Byte 2 + }; + + // Calculate checksum + uint16_t checksum = 0; + for (uint8_t i = 0; i < 5; i++) { + checksum += send_data[i]; + } + send_data[5] = (checksum >> 8) & 0xFF; // Verify Byte 1 + send_data[6] = (checksum >> 0) & 0xFF; // Verify Byte 2 + + for (auto send_byte : send_data) { + this->write_byte(send_byte); } - this->data_index_ = 0; } void PMSX003Component::parse_data_() { - switch (this->type_) { - case PMSX003_TYPE_5003ST: { - float temperature = (int16_t) this->get_16_bit_uint_(30) / 10.0f; - float humidity = this->get_16_bit_uint_(32) / 10.0f; + // Particle Matter + const uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4); + const uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6); + const uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8); - ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity); + const uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10); + const uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12); + const uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14); - if (this->temperature_sensor_ != nullptr) - this->temperature_sensor_->publish_state(temperature); - if (this->humidity_sensor_ != nullptr) - this->humidity_sensor_->publish_state(humidity); - // The rest of the PMS5003ST matches the PMS5003S, continue on - } - case PMSX003_TYPE_5003S: { - uint16_t formaldehyde = this->get_16_bit_uint_(28); + const uint16_t pm_particles_03um = this->get_16_bit_uint_(16); + const uint16_t pm_particles_05um = this->get_16_bit_uint_(18); + const uint16_t pm_particles_10um = this->get_16_bit_uint_(20); + const uint16_t pm_particles_25um = this->get_16_bit_uint_(22); - ESP_LOGD(TAG, "Got Formaldehyde: %u µg/m^3", formaldehyde); + ESP_LOGD(TAG, + "Got PM1.0 Standard Concentration: %u µg/m³, PM2.5 Standard Concentration %u µg/m³, PM10.0 Standard " + "Concentration: %u µg/m³, PM1.0 Concentration: %u µg/m³, PM2.5 Concentration %u µg/m³, PM10.0 " + "Concentration: %u µg/m³", + pm_1_0_std_concentration, pm_2_5_std_concentration, pm_10_0_std_concentration, pm_1_0_concentration, + pm_2_5_concentration, pm_10_0_concentration); - if (this->formaldehyde_sensor_ != nullptr) - this->formaldehyde_sensor_->publish_state(formaldehyde); - // The rest of the PMS5003S matches the PMS5003, continue on - } - case PMSX003_TYPE_X003: { - uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4); - uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6); - uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8); + if (this->pm_1_0_std_sensor_ != nullptr) + this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration); + if (this->pm_2_5_std_sensor_ != nullptr) + this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration); + if (this->pm_10_0_std_sensor_ != nullptr) + this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration); - uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10); - uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12); - uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14); + if (this->pm_1_0_sensor_ != nullptr) + this->pm_1_0_sensor_->publish_state(pm_1_0_concentration); + if (this->pm_2_5_sensor_ != nullptr) + this->pm_2_5_sensor_->publish_state(pm_2_5_concentration); + if (this->pm_10_0_sensor_ != nullptr) + this->pm_10_0_sensor_->publish_state(pm_10_0_concentration); - uint16_t pm_particles_03um = this->get_16_bit_uint_(16); - uint16_t pm_particles_05um = this->get_16_bit_uint_(18); - uint16_t pm_particles_10um = this->get_16_bit_uint_(20); - uint16_t pm_particles_25um = this->get_16_bit_uint_(22); - uint16_t pm_particles_50um = this->get_16_bit_uint_(24); - uint16_t pm_particles_100um = this->get_16_bit_uint_(26); + if (this->pm_particles_03um_sensor_ != nullptr) + this->pm_particles_03um_sensor_->publish_state(pm_particles_03um); + if (this->pm_particles_05um_sensor_ != nullptr) + this->pm_particles_05um_sensor_->publish_state(pm_particles_05um); + if (this->pm_particles_10um_sensor_ != nullptr) + this->pm_particles_10um_sensor_->publish_state(pm_particles_10um); + if (this->pm_particles_25um_sensor_ != nullptr) + this->pm_particles_25um_sensor_->publish_state(pm_particles_25um); - ESP_LOGD(TAG, - "Got PM1.0 Concentration: %u µg/m^3, PM2.5 Concentration %u µg/m^3, PM10.0 Concentration: %u µg/m^3", - pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration); + if (this->type_ == PMSX003_TYPE_5003T) { + ESP_LOGD(TAG, + "Got PM0.3 Particles: %u Count/0.1L, PM0.5 Particles: %u Count/0.1L, PM1.0 Particles: %u Count/0.1L, " + "PM2.5 Particles %u Count/0.1L", + pm_particles_03um, pm_particles_05um, pm_particles_10um, pm_particles_25um); + } else { + // Note the pm particles 50um & 100um are not returned, + // as PMS5003T uses those data values for temperature and humidity. + const uint16_t pm_particles_50um = this->get_16_bit_uint_(24); + const uint16_t pm_particles_100um = this->get_16_bit_uint_(26); - if (this->pm_1_0_std_sensor_ != nullptr) - this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration); - if (this->pm_2_5_std_sensor_ != nullptr) - this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration); - if (this->pm_10_0_std_sensor_ != nullptr) - this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration); + ESP_LOGD(TAG, + "Got PM0.3 Particles: %u Count/0.1L, PM0.5 Particles: %u Count/0.1L, PM1.0 Particles: %u Count/0.1L, " + "PM2.5 Particles %u Count/0.1L, PM5.0 Particles: %u Count/0.1L, PM10.0 Particles %u Count/0.1L", + pm_particles_03um, pm_particles_05um, pm_particles_10um, pm_particles_25um, pm_particles_50um, + pm_particles_100um); - if (this->pm_1_0_sensor_ != nullptr) - this->pm_1_0_sensor_->publish_state(pm_1_0_concentration); - if (this->pm_2_5_sensor_ != nullptr) - this->pm_2_5_sensor_->publish_state(pm_2_5_concentration); - if (this->pm_10_0_sensor_ != nullptr) - this->pm_10_0_sensor_->publish_state(pm_10_0_concentration); + if (this->pm_particles_50um_sensor_ != nullptr) + this->pm_particles_50um_sensor_->publish_state(pm_particles_50um); + if (this->pm_particles_100um_sensor_ != nullptr) + this->pm_particles_100um_sensor_->publish_state(pm_particles_100um); + } - if (this->pm_particles_03um_sensor_ != nullptr) - this->pm_particles_03um_sensor_->publish_state(pm_particles_03um); - if (this->pm_particles_05um_sensor_ != nullptr) - this->pm_particles_05um_sensor_->publish_state(pm_particles_05um); - if (this->pm_particles_10um_sensor_ != nullptr) - this->pm_particles_10um_sensor_->publish_state(pm_particles_10um); - if (this->pm_particles_25um_sensor_ != nullptr) - this->pm_particles_25um_sensor_->publish_state(pm_particles_25um); - if (this->pm_particles_50um_sensor_ != nullptr) - this->pm_particles_50um_sensor_->publish_state(pm_particles_50um); - if (this->pm_particles_100um_sensor_ != nullptr) - this->pm_particles_100um_sensor_->publish_state(pm_particles_100um); - break; - } - case PMSX003_TYPE_5003T: { - uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4); - uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6); - uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8); + // Formaldehyde + if (this->type_ == PMSX003_TYPE_5003ST || this->type_ == PMSX003_TYPE_5003S) { + const uint16_t formaldehyde = this->get_16_bit_uint_(28); - uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10); - uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12); - uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14); + ESP_LOGD(TAG, "Got Formaldehyde: %u µg/m^3", formaldehyde); - uint16_t pm_particles_03um = this->get_16_bit_uint_(16); - uint16_t pm_particles_05um = this->get_16_bit_uint_(18); - uint16_t pm_particles_10um = this->get_16_bit_uint_(20); - uint16_t pm_particles_25um = this->get_16_bit_uint_(22); - // Note the pm particles 50um & 100um are not returned, - // as PMS5003T uses those data values for temperature and humidity. + if (this->formaldehyde_sensor_ != nullptr) + this->formaldehyde_sensor_->publish_state(formaldehyde); + } - float temperature = (int16_t) this->get_16_bit_uint_(24) / 10.0f; - float humidity = this->get_16_bit_uint_(26) / 10.0f; + // Temperature and Humidity + if (this->type_ == PMSX003_TYPE_5003ST || this->type_ == PMSX003_TYPE_5003T) { + const uint8_t temperature_offset = (this->type_ == PMSX003_TYPE_5003T) ? 24 : 30; - ESP_LOGD(TAG, - "Got PM1.0 Concentration: %u µg/m^3, PM2.5 Concentration %u µg/m^3, PM10.0 Concentration: %u µg/m^3, " - "Temperature: %.1f°C, Humidity: %.1f%%", - pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration, temperature, humidity); + const float temperature = static_cast(this->get_16_bit_uint_(temperature_offset)) / 10.0f; + const float humidity = this->get_16_bit_uint_(temperature_offset + 2) / 10.0f; - if (this->pm_1_0_std_sensor_ != nullptr) - this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration); - if (this->pm_2_5_std_sensor_ != nullptr) - this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration); - if (this->pm_10_0_std_sensor_ != nullptr) - this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration); + ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity); - if (this->pm_1_0_sensor_ != nullptr) - this->pm_1_0_sensor_->publish_state(pm_1_0_concentration); - if (this->pm_2_5_sensor_ != nullptr) - this->pm_2_5_sensor_->publish_state(pm_2_5_concentration); - if (this->pm_10_0_sensor_ != nullptr) - this->pm_10_0_sensor_->publish_state(pm_10_0_concentration); + if (this->temperature_sensor_ != nullptr) + this->temperature_sensor_->publish_state(temperature); + if (this->humidity_sensor_ != nullptr) + this->humidity_sensor_->publish_state(humidity); + } - if (this->pm_particles_03um_sensor_ != nullptr) - this->pm_particles_03um_sensor_->publish_state(pm_particles_03um); - if (this->pm_particles_05um_sensor_ != nullptr) - this->pm_particles_05um_sensor_->publish_state(pm_particles_05um); - if (this->pm_particles_10um_sensor_ != nullptr) - this->pm_particles_10um_sensor_->publish_state(pm_particles_10um); - if (this->pm_particles_25um_sensor_ != nullptr) - this->pm_particles_25um_sensor_->publish_state(pm_particles_25um); + // Firmware Version and Error Code + if (this->type_ == PMSX003_TYPE_5003ST) { + const uint8_t firmware_version = this->data_[36]; + const uint8_t error_code = this->data_[37]; - if (this->temperature_sensor_ != nullptr) - this->temperature_sensor_->publish_state(temperature); - if (this->humidity_sensor_ != nullptr) - this->humidity_sensor_->publish_state(humidity); - break; - } + ESP_LOGD(TAG, "Got Firmware Version: 0x%02X, Error Code: 0x%02X", firmware_version, error_code); } // Spin down the sensor again if we aren't going to need it until more time has // passed than it takes to stabilise if (this->update_interval_ > PMS_STABILISING_MS) { - this->send_command_(PMS_CMD_ON_STANDBY, 0); + this->send_command_(PMS_CMD_SLEEP_MODE, PMS_CMD_SLEEP_MODE_SLEEP); this->state_ = PMSX003_STATE_IDLE; } this->status_clear_warning(); } + uint16_t PMSX003Component::get_16_bit_uint_(uint8_t start_index) { return (uint16_t(this->data_[start_index]) << 8) | uint16_t(this->data_[start_index + 1]); } -void PMSX003Component::dump_config() { - ESP_LOGCONFIG(TAG, "PMSX003:"); - LOG_SENSOR(" ", "PM1.0STD", this->pm_1_0_std_sensor_); - LOG_SENSOR(" ", "PM2.5STD", this->pm_2_5_std_sensor_); - LOG_SENSOR(" ", "PM10.0STD", this->pm_10_0_std_sensor_); - - LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_); - LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_); - LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_); - - LOG_SENSOR(" ", "PM0.3um", this->pm_particles_03um_sensor_); - LOG_SENSOR(" ", "PM0.5um", this->pm_particles_05um_sensor_); - LOG_SENSOR(" ", "PM1.0um", this->pm_particles_10um_sensor_); - LOG_SENSOR(" ", "PM2.5um", this->pm_particles_25um_sensor_); - LOG_SENSOR(" ", "PM5.0um", this->pm_particles_50um_sensor_); - LOG_SENSOR(" ", "PM10.0um", this->pm_particles_100um_sensor_); - - LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); - LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); - LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_); - this->check_uart_settings(9600); -} } // namespace pmsx003 } // namespace esphome diff --git a/esphome/components/pmsx003/pmsx003.h b/esphome/components/pmsx003/pmsx003.h index cb5c16aecf..85bb1ff9f3 100644 --- a/esphome/components/pmsx003/pmsx003.h +++ b/esphome/components/pmsx003/pmsx003.h @@ -7,13 +7,12 @@ namespace esphome { namespace pmsx003 { -// known command bytes -static const uint8_t PMS_CMD_AUTO_MANUAL = - 0xE1; // data=0: perform measurement manually, data=1: perform measurement automatically -static const uint8_t PMS_CMD_TRIG_MANUAL = 0xE2; // trigger a manual measurement -static const uint8_t PMS_CMD_ON_STANDBY = 0xE4; // data=0: go to standby mode, data=1: go to normal mode - -static const uint16_t PMS_STABILISING_MS = 30000; // time taken for the sensor to become stable after power on +enum PMSX0003Command : uint8_t { + PMS_CMD_MEASUREMENT_MODE = + 0xE1, // Data Options: `PMS_CMD_MEASUREMENT_MODE_PASSIVE`, `PMS_CMD_MEASUREMENT_MODE_ACTIVE` + PMS_CMD_MANUAL_MEASUREMENT = 0xE2, + PMS_CMD_SLEEP_MODE = 0xE4, // Data Options: `PMS_CMD_SLEEP_MODE_SLEEP`, `PMS_CMD_SLEEP_MODE_WAKEUP` +}; enum PMSX003Type { PMSX003_TYPE_X003 = 0, @@ -31,37 +30,53 @@ enum PMSX003State { class PMSX003Component : public uart::UARTDevice, public Component { public: PMSX003Component() = default; - void loop() override; - float get_setup_priority() const override; + float get_setup_priority() const override { return setup_priority::DATA; } void dump_config() override; + void loop() override; - void set_type(PMSX003Type type) { type_ = type; } + void set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; } - void set_update_interval(uint32_t val) { update_interval_ = val; }; + void set_type(PMSX003Type type) { this->type_ = type; } - void set_pm_1_0_std_sensor(sensor::Sensor *pm_1_0_std_sensor); - void set_pm_2_5_std_sensor(sensor::Sensor *pm_2_5_std_sensor); - void set_pm_10_0_std_sensor(sensor::Sensor *pm_10_0_std_sensor); + void set_pm_1_0_std_sensor(sensor::Sensor *pm_1_0_std_sensor) { this->pm_1_0_std_sensor_ = pm_1_0_std_sensor; } + void set_pm_2_5_std_sensor(sensor::Sensor *pm_2_5_std_sensor) { this->pm_2_5_std_sensor_ = pm_2_5_std_sensor; } + void set_pm_10_0_std_sensor(sensor::Sensor *pm_10_0_std_sensor) { this->pm_10_0_std_sensor_ = pm_10_0_std_sensor; } - void set_pm_1_0_sensor(sensor::Sensor *pm_1_0_sensor); - void set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor); - void set_pm_10_0_sensor(sensor::Sensor *pm_10_0_sensor); + void set_pm_1_0_sensor(sensor::Sensor *pm_1_0_sensor) { this->pm_1_0_sensor_ = pm_1_0_sensor; } + void set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor) { this->pm_2_5_sensor_ = pm_2_5_sensor; } + void set_pm_10_0_sensor(sensor::Sensor *pm_10_0_sensor) { this->pm_10_0_sensor_ = pm_10_0_sensor; } - void set_pm_particles_03um_sensor(sensor::Sensor *pm_particles_03um_sensor); - void set_pm_particles_05um_sensor(sensor::Sensor *pm_particles_05um_sensor); - void set_pm_particles_10um_sensor(sensor::Sensor *pm_particles_10um_sensor); - void set_pm_particles_25um_sensor(sensor::Sensor *pm_particles_25um_sensor); - void set_pm_particles_50um_sensor(sensor::Sensor *pm_particles_50um_sensor); - void set_pm_particles_100um_sensor(sensor::Sensor *pm_particles_100um_sensor); + void set_pm_particles_03um_sensor(sensor::Sensor *pm_particles_03um_sensor) { + this->pm_particles_03um_sensor_ = pm_particles_03um_sensor; + } + void set_pm_particles_05um_sensor(sensor::Sensor *pm_particles_05um_sensor) { + this->pm_particles_05um_sensor_ = pm_particles_05um_sensor; + } + void set_pm_particles_10um_sensor(sensor::Sensor *pm_particles_10um_sensor) { + this->pm_particles_10um_sensor_ = pm_particles_10um_sensor; + } + void set_pm_particles_25um_sensor(sensor::Sensor *pm_particles_25um_sensor) { + this->pm_particles_25um_sensor_ = pm_particles_25um_sensor; + } + void set_pm_particles_50um_sensor(sensor::Sensor *pm_particles_50um_sensor) { + this->pm_particles_50um_sensor_ = pm_particles_50um_sensor; + } + void set_pm_particles_100um_sensor(sensor::Sensor *pm_particles_100um_sensor) { + this->pm_particles_100um_sensor_ = pm_particles_100um_sensor; + } - void set_temperature_sensor(sensor::Sensor *temperature_sensor); - void set_humidity_sensor(sensor::Sensor *humidity_sensor); - void set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sensor); + void set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sensor) { + this->formaldehyde_sensor_ = formaldehyde_sensor; + } + + void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; } + void set_humidity_sensor(sensor::Sensor *humidity_sensor) { this->humidity_sensor_ = humidity_sensor; } protected: optional check_byte_(); void parse_data_(); - void send_command_(uint8_t cmd, uint16_t data); + bool check_payload_length_(uint16_t payload_length); + void send_command_(PMSX0003Command cmd, uint16_t data); uint16_t get_16_bit_uint_(uint8_t start_index); uint8_t data_[64]; @@ -92,9 +107,12 @@ class PMSX003Component : public uart::UARTDevice, public Component { sensor::Sensor *pm_particles_50um_sensor_{nullptr}; sensor::Sensor *pm_particles_100um_sensor_{nullptr}; + // Formaldehyde + sensor::Sensor *formaldehyde_sensor_{nullptr}; + + // Temperature and Humidity sensor::Sensor *temperature_sensor_{nullptr}; sensor::Sensor *humidity_sensor_{nullptr}; - sensor::Sensor *formaldehyde_sensor_{nullptr}; }; } // namespace pmsx003 diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index 1556b3c983..bebd3a01ee 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -33,6 +33,7 @@ from esphome.const import ( UNIT_PERCENT, ) +CODEOWNERS = ["@ximex"] DEPENDENCIES = ["uart"] pmsx003_ns = cg.esphome_ns.namespace("pmsx003") @@ -57,9 +58,18 @@ SENSORS_TO_TYPE = { CONF_PM_1_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], CONF_PM_2_5: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], CONF_PM_10_0: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_1_0_STD: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_2_5_STD: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_10_0_STD: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_0_3UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_0_5UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_1_0UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_2_5UM: [TYPE_PMSX003, TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_5_0UM: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_PM_10_0UM: [TYPE_PMSX003, TYPE_PMS5003ST, TYPE_PMS5003S], + CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S], CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST], CONF_HUMIDITY: [TYPE_PMS5003T, TYPE_PMS5003ST], - CONF_FORMALDEHYDE: [TYPE_PMS5003ST, TYPE_PMS5003S], } @@ -164,6 +174,12 @@ CONFIG_SCHEMA = ( accuracy_decimals=0, state_class=STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema( + unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, + icon=ICON_CHEMICAL_WEAPON, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + ), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema( unit_of_measurement=UNIT_CELSIUS, accuracy_decimals=1, @@ -176,12 +192,6 @@ CONFIG_SCHEMA = ( device_class=DEVICE_CLASS_HUMIDITY, state_class=STATE_CLASS_MEASUREMENT, ), - cv.Optional(CONF_FORMALDEHYDE): sensor.sensor_schema( - unit_of_measurement=UNIT_MICROGRAMS_PER_CUBIC_METER, - icon=ICON_CHEMICAL_WEAPON, - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - ), cv.Optional(CONF_UPDATE_INTERVAL, default="0s"): validate_update_interval, } ) @@ -256,6 +266,10 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_PM_10_0UM]) cg.add(var.set_pm_particles_100um_sensor(sens)) + if CONF_FORMALDEHYDE in config: + sens = await sensor.new_sensor(config[CONF_FORMALDEHYDE]) + cg.add(var.set_formaldehyde_sensor(sens)) + if CONF_TEMPERATURE in config: sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) cg.add(var.set_temperature_sensor(sens)) @@ -264,8 +278,4 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_HUMIDITY]) cg.add(var.set_humidity_sensor(sens)) - if CONF_FORMALDEHYDE in config: - sens = await sensor.new_sensor(config[CONF_FORMALDEHYDE]) - cg.add(var.set_formaldehyde_sensor(sens)) - cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))