ESPHome  2024.4.0
pmsx003.cpp
Go to the documentation of this file.
1 #include "pmsx003.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace pmsx003 {
6 
7 static const char *const TAG = "pmsx003";
8 
10  pm_1_0_std_sensor_ = pm_1_0_std_sensor;
11 }
13  pm_2_5_std_sensor_ = pm_2_5_std_sensor;
14 }
16  pm_10_0_std_sensor_ = pm_10_0_std_sensor;
17 }
18 
19 void PMSX003Component::set_pm_1_0_sensor(sensor::Sensor *pm_1_0_sensor) { pm_1_0_sensor_ = pm_1_0_sensor; }
20 void PMSX003Component::set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor) { pm_2_5_sensor_ = pm_2_5_sensor; }
21 void PMSX003Component::set_pm_10_0_sensor(sensor::Sensor *pm_10_0_sensor) { pm_10_0_sensor_ = pm_10_0_sensor; }
22 
24  pm_particles_03um_sensor_ = pm_particles_03um_sensor;
25 }
27  pm_particles_05um_sensor_ = pm_particles_05um_sensor;
28 }
30  pm_particles_10um_sensor_ = pm_particles_10um_sensor;
31 }
33  pm_particles_25um_sensor_ = pm_particles_25um_sensor;
34 }
36  pm_particles_50um_sensor_ = pm_particles_50um_sensor;
37 }
39  pm_particles_100um_sensor_ = pm_particles_100um_sensor;
40 }
41 
43  temperature_sensor_ = temperature_sensor;
44 }
45 void PMSX003Component::set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
47  formaldehyde_sensor_ = formaldehyde_sensor;
48 }
49 
51  const uint32_t now = millis();
52 
53  // If we update less often than it takes the device to stabilise, spin the fan down
54  // rather than running it constantly. It does take some time to stabilise, so we
55  // need to keep track of what state we're in.
56  if (this->update_interval_ > PMS_STABILISING_MS) {
57  if (this->initialised_ == 0) {
58  this->send_command_(PMS_CMD_AUTO_MANUAL, 0);
59  this->send_command_(PMS_CMD_ON_STANDBY, 1);
60  this->initialised_ = 1;
61  }
62  switch (this->state_) {
63  case PMSX003_STATE_IDLE:
64  // Power on the sensor now so it'll be ready when we hit the update time
65  if (now - this->last_update_ < (this->update_interval_ - PMS_STABILISING_MS))
66  return;
67 
69  this->send_command_(PMS_CMD_ON_STANDBY, 1);
70  this->fan_on_time_ = now;
71  return;
73  // wait for the sensor to be stable
74  if (now - this->fan_on_time_ < PMS_STABILISING_MS)
75  return;
76  // consume any command responses that are in the serial buffer
77  while (this->available())
78  this->read_byte(&this->data_[0]);
79  // Trigger a new read
80  this->send_command_(PMS_CMD_TRIG_MANUAL, 0);
82  break;
84  // Just go ahead and read stuff
85  break;
86  }
87  } else if (now - this->last_update_ < this->update_interval_) {
88  // Otherwise just leave the sensor powered up and come back when we hit the update
89  // time
90  return;
91  }
92 
93  if (now - this->last_transmission_ >= 500) {
94  // last transmission too long ago. Reset RX index.
95  this->data_index_ = 0;
96  }
97 
98  if (this->available() == 0)
99  return;
100 
101  this->last_transmission_ = now;
102  while (this->available() != 0) {
103  this->read_byte(&this->data_[this->data_index_]);
104  auto check = this->check_byte_();
105  if (!check.has_value()) {
106  // finished
107  this->parse_data_();
108  this->data_index_ = 0;
109  this->last_update_ = now;
110  } else if (!*check) {
111  // wrong data
112  this->data_index_ = 0;
113  } else {
114  // next byte
115  this->data_index_++;
116  }
117  }
118 }
121  uint8_t index = this->data_index_;
122  uint8_t byte = this->data_[index];
123 
124  if (index == 0)
125  return byte == 0x42;
126 
127  if (index == 1)
128  return byte == 0x4D;
129 
130  if (index == 2)
131  return true;
132 
133  uint16_t payload_length = this->get_16_bit_uint_(2);
134  if (index == 3) {
135  bool length_matches = false;
136  switch (this->type_) {
137  case PMSX003_TYPE_X003:
138  length_matches = payload_length == 28 || payload_length == 20;
139  break;
140  case PMSX003_TYPE_5003T:
141  case PMSX003_TYPE_5003S:
142  length_matches = payload_length == 28;
143  break;
144  case PMSX003_TYPE_5003ST:
145  length_matches = payload_length == 36;
146  break;
147  }
148 
149  if (!length_matches) {
150  ESP_LOGW(TAG, "PMSX003 length %u doesn't match. Are you using the correct PMSX003 type?", payload_length);
151  return false;
152  }
153  return true;
154  }
155 
156  // start (16bit) + length (16bit) + DATA (payload_length-2 bytes) + checksum (16bit)
157  uint8_t total_size = 4 + payload_length;
158 
159  if (index < total_size - 1)
160  return true;
161 
162  // checksum is without checksum bytes
163  uint16_t checksum = 0;
164  for (uint8_t i = 0; i < total_size - 2; i++)
165  checksum += this->data_[i];
166 
167  uint16_t check = this->get_16_bit_uint_(total_size - 2);
168  if (checksum != check) {
169  ESP_LOGW(TAG, "PMSX003 checksum mismatch! 0x%02X!=0x%02X", checksum, check);
170  return false;
171  }
172 
173  return {};
174 }
175 
176 void PMSX003Component::send_command_(uint8_t cmd, uint16_t data) {
177  this->data_index_ = 0;
178  this->data_[data_index_++] = 0x42;
179  this->data_[data_index_++] = 0x4D;
180  this->data_[data_index_++] = cmd;
181  this->data_[data_index_++] = (data >> 8) & 0xFF;
182  this->data_[data_index_++] = (data >> 0) & 0xFF;
183  int sum = 0;
184  for (int i = 0; i < data_index_; i++) {
185  sum += this->data_[i];
186  }
187  this->data_[data_index_++] = (sum >> 8) & 0xFF;
188  this->data_[data_index_++] = (sum >> 0) & 0xFF;
189  for (int i = 0; i < data_index_; i++) {
190  this->write_byte(this->data_[i]);
191  }
192  this->data_index_ = 0;
193 }
194 
196  switch (this->type_) {
197  case PMSX003_TYPE_5003ST: {
198  float temperature = (int16_t) this->get_16_bit_uint_(30) / 10.0f;
199  float humidity = this->get_16_bit_uint_(32) / 10.0f;
200 
201  ESP_LOGD(TAG, "Got Temperature: %.1f°C, Humidity: %.1f%%", temperature, humidity);
202 
203  if (this->temperature_sensor_ != nullptr)
204  this->temperature_sensor_->publish_state(temperature);
205  if (this->humidity_sensor_ != nullptr)
206  this->humidity_sensor_->publish_state(humidity);
207  // The rest of the PMS5003ST matches the PMS5003S, continue on
208  }
209  case PMSX003_TYPE_5003S: {
210  uint16_t formaldehyde = this->get_16_bit_uint_(28);
211 
212  ESP_LOGD(TAG, "Got Formaldehyde: %u µg/m^3", formaldehyde);
213 
214  if (this->formaldehyde_sensor_ != nullptr)
215  this->formaldehyde_sensor_->publish_state(formaldehyde);
216  // The rest of the PMS5003S matches the PMS5003, continue on
217  }
218  case PMSX003_TYPE_X003: {
219  uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4);
220  uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6);
221  uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8);
222 
223  uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10);
224  uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12);
225  uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14);
226 
227  uint16_t pm_particles_03um = this->get_16_bit_uint_(16);
228  uint16_t pm_particles_05um = this->get_16_bit_uint_(18);
229  uint16_t pm_particles_10um = this->get_16_bit_uint_(20);
230  uint16_t pm_particles_25um = this->get_16_bit_uint_(22);
231  uint16_t pm_particles_50um = this->get_16_bit_uint_(24);
232  uint16_t pm_particles_100um = this->get_16_bit_uint_(26);
233 
234  ESP_LOGD(TAG,
235  "Got PM1.0 Concentration: %u µg/m^3, PM2.5 Concentration %u µg/m^3, PM10.0 Concentration: %u µg/m^3",
236  pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration);
237 
238  if (this->pm_1_0_std_sensor_ != nullptr)
239  this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration);
240  if (this->pm_2_5_std_sensor_ != nullptr)
241  this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration);
242  if (this->pm_10_0_std_sensor_ != nullptr)
243  this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration);
244 
245  if (this->pm_1_0_sensor_ != nullptr)
246  this->pm_1_0_sensor_->publish_state(pm_1_0_concentration);
247  if (this->pm_2_5_sensor_ != nullptr)
248  this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
249  if (this->pm_10_0_sensor_ != nullptr)
250  this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
251 
252  if (this->pm_particles_03um_sensor_ != nullptr)
253  this->pm_particles_03um_sensor_->publish_state(pm_particles_03um);
254  if (this->pm_particles_05um_sensor_ != nullptr)
255  this->pm_particles_05um_sensor_->publish_state(pm_particles_05um);
256  if (this->pm_particles_10um_sensor_ != nullptr)
257  this->pm_particles_10um_sensor_->publish_state(pm_particles_10um);
258  if (this->pm_particles_25um_sensor_ != nullptr)
259  this->pm_particles_25um_sensor_->publish_state(pm_particles_25um);
260  if (this->pm_particles_50um_sensor_ != nullptr)
261  this->pm_particles_50um_sensor_->publish_state(pm_particles_50um);
262  if (this->pm_particles_100um_sensor_ != nullptr)
263  this->pm_particles_100um_sensor_->publish_state(pm_particles_100um);
264  break;
265  }
266  case PMSX003_TYPE_5003T: {
267  uint16_t pm_1_0_std_concentration = this->get_16_bit_uint_(4);
268  uint16_t pm_2_5_std_concentration = this->get_16_bit_uint_(6);
269  uint16_t pm_10_0_std_concentration = this->get_16_bit_uint_(8);
270 
271  uint16_t pm_1_0_concentration = this->get_16_bit_uint_(10);
272  uint16_t pm_2_5_concentration = this->get_16_bit_uint_(12);
273  uint16_t pm_10_0_concentration = this->get_16_bit_uint_(14);
274 
275  uint16_t pm_particles_03um = this->get_16_bit_uint_(16);
276  uint16_t pm_particles_05um = this->get_16_bit_uint_(18);
277  uint16_t pm_particles_10um = this->get_16_bit_uint_(20);
278  uint16_t pm_particles_25um = this->get_16_bit_uint_(22);
279  // Note the pm particles 50um & 100um are not returned,
280  // as PMS5003T uses those data values for temperature and humidity.
281 
282  float temperature = (int16_t) this->get_16_bit_uint_(24) / 10.0f;
283  float humidity = this->get_16_bit_uint_(26) / 10.0f;
284 
285  ESP_LOGD(TAG,
286  "Got PM1.0 Concentration: %u µg/m^3, PM2.5 Concentration %u µg/m^3, PM10.0 Concentration: %u µg/m^3, "
287  "Temperature: %.1f°C, Humidity: %.1f%%",
288  pm_1_0_concentration, pm_2_5_concentration, pm_10_0_concentration, temperature, humidity);
289 
290  if (this->pm_1_0_std_sensor_ != nullptr)
291  this->pm_1_0_std_sensor_->publish_state(pm_1_0_std_concentration);
292  if (this->pm_2_5_std_sensor_ != nullptr)
293  this->pm_2_5_std_sensor_->publish_state(pm_2_5_std_concentration);
294  if (this->pm_10_0_std_sensor_ != nullptr)
295  this->pm_10_0_std_sensor_->publish_state(pm_10_0_std_concentration);
296 
297  if (this->pm_1_0_sensor_ != nullptr)
298  this->pm_1_0_sensor_->publish_state(pm_1_0_concentration);
299  if (this->pm_2_5_sensor_ != nullptr)
300  this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
301  if (this->pm_10_0_sensor_ != nullptr)
302  this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
303 
304  if (this->pm_particles_03um_sensor_ != nullptr)
305  this->pm_particles_03um_sensor_->publish_state(pm_particles_03um);
306  if (this->pm_particles_05um_sensor_ != nullptr)
307  this->pm_particles_05um_sensor_->publish_state(pm_particles_05um);
308  if (this->pm_particles_10um_sensor_ != nullptr)
309  this->pm_particles_10um_sensor_->publish_state(pm_particles_10um);
310  if (this->pm_particles_25um_sensor_ != nullptr)
311  this->pm_particles_25um_sensor_->publish_state(pm_particles_25um);
312 
313  if (this->temperature_sensor_ != nullptr)
314  this->temperature_sensor_->publish_state(temperature);
315  if (this->humidity_sensor_ != nullptr)
316  this->humidity_sensor_->publish_state(humidity);
317  break;
318  }
319  }
320 
321  // Spin down the sensor again if we aren't going to need it until more time has
322  // passed than it takes to stabilise
323  if (this->update_interval_ > PMS_STABILISING_MS) {
324  this->send_command_(PMS_CMD_ON_STANDBY, 0);
325  this->state_ = PMSX003_STATE_IDLE;
326  }
327 
328  this->status_clear_warning();
329 }
330 uint16_t PMSX003Component::get_16_bit_uint_(uint8_t start_index) {
331  return (uint16_t(this->data_[start_index]) << 8) | uint16_t(this->data_[start_index + 1]);
332 }
334  ESP_LOGCONFIG(TAG, "PMSX003:");
335  LOG_SENSOR(" ", "PM1.0STD", this->pm_1_0_std_sensor_);
336  LOG_SENSOR(" ", "PM2.5STD", this->pm_2_5_std_sensor_);
337  LOG_SENSOR(" ", "PM10.0STD", this->pm_10_0_std_sensor_);
338 
339  LOG_SENSOR(" ", "PM1.0", this->pm_1_0_sensor_);
340  LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_);
341  LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_);
342 
343  LOG_SENSOR(" ", "PM0.3um", this->pm_particles_03um_sensor_);
344  LOG_SENSOR(" ", "PM0.5um", this->pm_particles_05um_sensor_);
345  LOG_SENSOR(" ", "PM1.0um", this->pm_particles_10um_sensor_);
346  LOG_SENSOR(" ", "PM2.5um", this->pm_particles_25um_sensor_);
347  LOG_SENSOR(" ", "PM5.0um", this->pm_particles_50um_sensor_);
348  LOG_SENSOR(" ", "PM10.0um", this->pm_particles_100um_sensor_);
349 
350  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
351  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
352  LOG_SENSOR(" ", "Formaldehyde", this->formaldehyde_sensor_);
353  this->check_uart_settings(9600);
354 }
355 
356 } // namespace pmsx003
357 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
sensor::Sensor * pm_10_0_sensor_
Definition: pmsx003.h:85
sensor::Sensor * pm_particles_50um_sensor_
Definition: pmsx003.h:92
void write_byte(uint8_t data)
Definition: uart.h:19
sensor::Sensor * pm_1_0_std_sensor_
Definition: pmsx003.h:78
sensor::Sensor * pm_10_0_std_sensor_
Definition: pmsx003.h:80
sensor::Sensor * pm_2_5_std_sensor_
Definition: pmsx003.h:79
void set_pm_2_5_std_sensor(sensor::Sensor *pm_2_5_std_sensor)
Definition: pmsx003.cpp:12
void set_pm_particles_10um_sensor(sensor::Sensor *pm_particles_10um_sensor)
Definition: pmsx003.cpp:29
void set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor)
Definition: pmsx003.cpp:20
sensor::Sensor * pm_particles_10um_sensor_
Definition: pmsx003.h:90
void set_pm_particles_100um_sensor(sensor::Sensor *pm_particles_100um_sensor)
Definition: pmsx003.cpp:38
sensor::Sensor * pm_particles_25um_sensor_
Definition: pmsx003.h:91
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
void set_humidity_sensor(sensor::Sensor *humidity_sensor)
Definition: pmsx003.cpp:45
sensor::Sensor * formaldehyde_sensor_
Definition: pmsx003.h:97
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition: uart.cpp:13
sensor::Sensor * pm_2_5_sensor_
Definition: pmsx003.h:84
sensor::Sensor * pm_particles_03um_sensor_
Definition: pmsx003.h:88
bool read_byte(uint8_t *data)
Definition: uart.h:29
void set_pm_10_0_std_sensor(sensor::Sensor *pm_10_0_std_sensor)
Definition: pmsx003.cpp:15
void status_clear_warning()
Definition: component.cpp:166
sensor::Sensor * pm_1_0_sensor_
Definition: pmsx003.h:83
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
float get_setup_priority() const override
Definition: pmsx003.cpp:119
sensor::Sensor * temperature_sensor_
Definition: pmsx003.h:95
uint16_t temperature
Definition: sun_gtil2.cpp:26
uint8_t checksum
Definition: bl0939.h:35
uint16_t get_16_bit_uint_(uint8_t start_index)
Definition: pmsx003.cpp:330
void set_pm_particles_50um_sensor(sensor::Sensor *pm_particles_50um_sensor)
Definition: pmsx003.cpp:35
optional< bool > check_byte_()
Definition: pmsx003.cpp:120
sensor::Sensor * pm_particles_05um_sensor_
Definition: pmsx003.h:89
void set_pm_1_0_sensor(sensor::Sensor *pm_1_0_sensor)
Definition: pmsx003.cpp:19
sensor::Sensor * pm_particles_100um_sensor_
Definition: pmsx003.h:93
sensor::Sensor * humidity_sensor_
Definition: pmsx003.h:96
void set_pm_particles_25um_sensor(sensor::Sensor *pm_particles_25um_sensor)
Definition: pmsx003.cpp:32
void send_command_(uint8_t cmd, uint16_t data)
Definition: pmsx003.cpp:176
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
Base-class for all sensors.
Definition: sensor.h:57
void set_pm_particles_03um_sensor(sensor::Sensor *pm_particles_03um_sensor)
Definition: pmsx003.cpp:23
void set_formaldehyde_sensor(sensor::Sensor *formaldehyde_sensor)
Definition: pmsx003.cpp:46
void set_pm_10_0_sensor(sensor::Sensor *pm_10_0_sensor)
Definition: pmsx003.cpp:21
stm32_cmd_t * cmd
Definition: stm32flash.h:96
void set_pm_1_0_std_sensor(sensor::Sensor *pm_1_0_std_sensor)
Definition: pmsx003.cpp:9
void set_temperature_sensor(sensor::Sensor *temperature_sensor)
Definition: pmsx003.cpp:42
void set_pm_particles_05um_sensor(sensor::Sensor *pm_particles_05um_sensor)
Definition: pmsx003.cpp:26