diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index 14b1f60df54..f99e5adaa2c 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -189,6 +189,7 @@ bool PMSX003Component::check_payload_length_(uint16_t payload_length) { case Type::PMS5003ST: // Data 16 not set/reserved return payload_length == 36; // 2*17+2 case Type::PMS9003M: + case Type::LUFTMY: return payload_length == 28; // 2*13+2 } return false; @@ -219,40 +220,48 @@ void PMSX003Component::send_command_(Command cmd, uint16_t data) { } void PMSX003Component::parse_data_() { - // Particle Matter + // PM mass concentrations 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); - 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); - - 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 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->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 PM1.0 Standard Concentration: %u µg/m³, PM2.5 Standard Concentration %u µg/m³, PM10.0 Standard " + "Concentration: %u µg/m³", + pm_1_0_std_concentration, pm_2_5_std_concentration, pm_10_0_std_concentration); - 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); + // The Luftmy LD10, LD11, LD13, LD15 and LD16 report no atmospheric PM, so their particle counts start at byte 10. + // All PMS types have atmospheric PM at bytes 10-15, so their particle counts start at byte 16. + const bool is_luftmy = this->type_ == Type::LUFTMY; + const uint8_t pc_offset = is_luftmy ? 10 : 16; + + if (!is_luftmy) { + 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); + + ESP_LOGD(TAG, "Got PM1.0 Concentration: %u µg/m³, PM2.5 Concentration %u µg/m³, PM10.0 Concentration: %u µg/m³", + pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration); + + 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); + } + + // Particle counts at pc_offset: >0.3, >0.5, >1.0, >2.5µm + const uint16_t pm_particles_03um = this->get_16_bit_uint_(pc_offset); + const uint16_t pm_particles_05um = this->get_16_bit_uint_(pc_offset + 2); + const uint16_t pm_particles_10um = this->get_16_bit_uint_(pc_offset + 4); + const uint16_t pm_particles_25um = this->get_16_bit_uint_(pc_offset + 6); if (this->pm_particles_03um_sensor_ != nullptr) this->pm_particles_03um_sensor_->publish_state(pm_particles_03um); @@ -262,22 +271,17 @@ void PMSX003Component::parse_data_() { 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 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); - if (this->type_ == Type::PMS5003T) { - 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); + // PMS5003T uses pc_offset+8 and pc_offset+10 for temperature/humidity, not particle counts + if (this->type_ != Type::PMS5003T) { + const uint16_t pm_particles_50um = this->get_16_bit_uint_(pc_offset + 8); + const uint16_t pm_particles_100um = this->get_16_bit_uint_(pc_offset + 10); - 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, + ESP_LOGD(TAG, "Got PM5.0 Particles: %u Count/0.1L, PM10.0 Particles: %u Count/0.1L", pm_particles_50um, pm_particles_100um); if (this->pm_particles_50um_sensor_ != nullptr) diff --git a/esphome/components/pmsx003/pmsx003.h b/esphome/components/pmsx003/pmsx003.h index c62960e7c31..c123a42b9e6 100644 --- a/esphome/components/pmsx003/pmsx003.h +++ b/esphome/components/pmsx003/pmsx003.h @@ -15,6 +15,7 @@ enum class Type : uint8_t { PMS5003T, PMS5003ST, PMS9003M, + LUFTMY, // Luftmy LD10, LD11, LD13, LD15, LD16: standard PM + particle counts, no atmospheric PM values }; enum class Command : uint8_t { diff --git a/esphome/components/pmsx003/sensor.py b/esphome/components/pmsx003/sensor.py index 5eac5415ed3..723b99a5040 100644 --- a/esphome/components/pmsx003/sensor.py +++ b/esphome/components/pmsx003/sensor.py @@ -51,6 +51,7 @@ TYPE_PMS5003S = "PMS5003S" TYPE_PMS5003T = "PMS5003T" TYPE_PMS5003ST = "PMS5003ST" TYPE_PMS9003M = "PMS9003M" +TYPE_LUFTMY = "LUFTMY" Type = pmsx003_ns.enum("Type", is_class=True) @@ -62,6 +63,7 @@ PMSX003_TYPES = { TYPE_PMS5003T: Type.PMS5003T, TYPE_PMS5003ST: Type.PMS5003ST, TYPE_PMS9003M: Type.PMS9003M, + TYPE_LUFTMY: Type.LUFTMY, } SENSORS_TO_TYPE = { @@ -73,6 +75,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_2_5_STD: [ TYPE_PMS1003, @@ -82,6 +85,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_10_0_STD: [ TYPE_PMS1003, @@ -91,6 +95,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_1_0: [ TYPE_PMS1003, @@ -126,6 +131,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_0_5UM: [ TYPE_PMS1003, @@ -134,6 +140,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_1_0UM: [ TYPE_PMS1003, @@ -142,6 +149,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_2_5UM: [ TYPE_PMS1003, @@ -150,6 +158,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003T, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_5_0UM: [ TYPE_PMS1003, @@ -157,6 +166,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003S, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_PM_10_0UM: [ TYPE_PMS1003, @@ -164,6 +174,7 @@ SENSORS_TO_TYPE = { TYPE_PMS5003S, TYPE_PMS5003ST, TYPE_PMS9003M, + TYPE_LUFTMY, ], CONF_FORMALDEHYDE: [TYPE_PMS5003S, TYPE_PMS5003ST], CONF_TEMPERATURE: [TYPE_PMS5003T, TYPE_PMS5003ST], diff --git a/tests/components/pmsx003/common-luftmy.yaml b/tests/components/pmsx003/common-luftmy.yaml new file mode 100644 index 00000000000..538b2481a49 --- /dev/null +++ b/tests/components/pmsx003/common-luftmy.yaml @@ -0,0 +1,22 @@ +sensor: + - platform: pmsx003 + type: LUFTMY + pm_1_0_std: + name: PM 1.0 Standard Atmospheric Concentration + pm_2_5_std: + name: PM 2.5 Standard Atmospheric Concentration + pm_10_0_std: + name: PM 10.0 Standard Atmospheric Concentration + pm_0_3um: + name: Particulate Count >0.3um + pm_0_5um: + name: Particulate Count >0.5um + pm_1_0um: + name: Particulate Count >1.0um + pm_2_5um: + name: Particulate Count >2.5um + pm_5_0um: + name: Particulate Count >5.0um + pm_10_0um: + name: Particulate Count >10.0um + update_interval: 30s diff --git a/tests/components/pmsx003/test.esp32-idf.yaml b/tests/components/pmsx003/test.esp32-idf.yaml index b415125e84b..9e5b478a31e 100644 --- a/tests/components/pmsx003/test.esp32-idf.yaml +++ b/tests/components/pmsx003/test.esp32-idf.yaml @@ -4,5 +4,4 @@ substitutions: packages: uart: !include ../../test_build_components/common/uart/esp32-idf.yaml - -<<: !include common.yaml + pmsx003: !include common.yaml diff --git a/tests/components/pmsx003/test.esp8266-ard.yaml b/tests/components/pmsx003/test.esp8266-ard.yaml index 96ab4ef6aca..8e7bd3ebe86 100644 --- a/tests/components/pmsx003/test.esp8266-ard.yaml +++ b/tests/components/pmsx003/test.esp8266-ard.yaml @@ -4,5 +4,4 @@ substitutions: packages: uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml - -<<: !include common.yaml + pmsx003: !include common.yaml diff --git a/tests/components/pmsx003/test.rp2040-ard.yaml b/tests/components/pmsx003/test.rp2040-ard.yaml index b28f2b5e05e..58779cc0956 100644 --- a/tests/components/pmsx003/test.rp2040-ard.yaml +++ b/tests/components/pmsx003/test.rp2040-ard.yaml @@ -4,5 +4,4 @@ substitutions: packages: uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml - -<<: !include common.yaml + pmsx003: !include common.yaml diff --git a/tests/components/pmsx003/validate-luftmy.esp32-idf.yaml b/tests/components/pmsx003/validate-luftmy.esp32-idf.yaml new file mode 100644 index 00000000000..c0c648e0389 --- /dev/null +++ b/tests/components/pmsx003/validate-luftmy.esp32-idf.yaml @@ -0,0 +1,7 @@ +substitutions: + tx_pin: GPIO4 + rx_pin: GPIO5 + +packages: + uart: !include ../../test_build_components/common/uart/esp32-idf.yaml + pmsx003: !include common-luftmy.yaml