[pmsx003] add support for luftmy ld15 (#15296)

Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
Tobias Stanzel
2026-10-11 09:49:06 -10:00
committed by GitHub
co-authored by J. Nick Koston J. Nick Koston
parent da43b6292d
commit b73f151619
8 changed files with 85 additions and 43 deletions
+41 -37
View File
@@ -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)
+1
View File
@@ -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 {
+11
View File
@@ -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],
@@ -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
+1 -2
View File
@@ -4,5 +4,4 @@ substitutions:
packages:
uart: !include ../../test_build_components/common/uart/esp32-idf.yaml
<<: !include common.yaml
pmsx003: !include common.yaml
@@ -4,5 +4,4 @@ substitutions:
packages:
uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml
<<: !include common.yaml
pmsx003: !include common.yaml
@@ -4,5 +4,4 @@ substitutions:
packages:
uart: !include ../../test_build_components/common/uart/rp2040-ard.yaml
<<: !include common.yaml
pmsx003: !include common.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