ESPHome 2026.9.0
Loading...
Searching...
No Matches
pmsa003i.cpp
Go to the documentation of this file.
1#include "pmsa003i.h"
3#include "esphome/core/log.h"
4#include <cstring>
5
7
8static const char *const TAG = "pmsa003i";
9
10static const uint8_t COUNT_PAYLOAD_BYTES = 28;
11static const uint8_t COUNT_PAYLOAD_LENGTH_BYTES = 2;
12static const uint8_t COUNT_START_CHARACTER_BYTES = 2;
13static const uint8_t COUNT_DATA_BYTES = COUNT_START_CHARACTER_BYTES + COUNT_PAYLOAD_LENGTH_BYTES + COUNT_PAYLOAD_BYTES;
14static const uint8_t CHECKSUM_START_INDEX = COUNT_DATA_BYTES - 2;
15static const uint8_t COUNT_16_BIT_VALUES = (COUNT_PAYLOAD_LENGTH_BYTES + COUNT_PAYLOAD_BYTES) / 2;
16static const uint8_t START_CHARACTER_1 = 0x42;
17static const uint8_t START_CHARACTER_2 = 0x4D;
18static const uint8_t READ_DATA_RETRY_COUNT = 3;
19
21 PM25AQIData data;
22 bool successful_read = this->read_data_(&data);
23
24 if (!successful_read) {
25 for (uint8_t i = 0; i < READ_DATA_RETRY_COUNT; i++) {
26 successful_read = this->read_data_(&data);
27 if (successful_read) {
28 break;
29 }
30 }
31 }
32
33 if (!successful_read) {
34 this->mark_failed();
35 return;
36 }
37}
38
40 ESP_LOGCONFIG(TAG, "PMSA003I:");
41 LOG_I2C_DEVICE(this);
42}
43
45 PM25AQIData data;
46
47 bool successful_read = this->read_data_(&data);
48
49 // Update sensors
50 if (successful_read) {
52 ESP_LOGV(TAG, "Read success. Updating sensors.");
53
54 if (this->standard_units_) {
55 if (this->pm_1_0_sensor_ != nullptr)
57 if (this->pm_2_5_sensor_ != nullptr)
59 if (this->pm_10_0_sensor_ != nullptr)
61 } else {
62 if (this->pm_1_0_sensor_ != nullptr)
64 if (this->pm_2_5_sensor_ != nullptr)
66 if (this->pm_10_0_sensor_ != nullptr)
68 }
69
70 if (this->pmc_0_3_sensor_ != nullptr)
72 if (this->pmc_0_5_sensor_ != nullptr)
74 if (this->pmc_1_0_sensor_ != nullptr)
76 if (this->pmc_2_5_sensor_ != nullptr)
78 if (this->pmc_5_0_sensor_ != nullptr)
80 if (this->pmc_10_0_sensor_ != nullptr)
82 } else {
83 this->status_set_warning();
84 ESP_LOGV(TAG, "Read failure. Skipping update.");
85 }
86}
87
89 uint8_t buffer[COUNT_DATA_BYTES];
90
91 const i2c::ErrorCode error = this->read(buffer, COUNT_DATA_BYTES);
92 if (error != i2c::ERROR_OK) {
93 ESP_LOGW(TAG, "I2C error %d", error);
94 return false;
95 }
96
97 // https://github.com/adafruit/Adafruit_PM25AQI
98
99 // Check that start byte is correct!
100 if (buffer[0] != START_CHARACTER_1 || buffer[1] != START_CHARACTER_2) {
101 ESP_LOGW(TAG, "Start character mismatch: %02X %02X != %02X %02X", buffer[0], buffer[1], START_CHARACTER_1,
102 START_CHARACTER_2);
103 return false;
104 }
105
106 const uint16_t payload_length = encode_uint16(buffer[2], buffer[3]);
107 if (payload_length != COUNT_PAYLOAD_BYTES) {
108 ESP_LOGW(TAG, "Payload length mismatch: %u != %u", payload_length, COUNT_PAYLOAD_BYTES);
109 return false;
110 }
111
112 // Calculate checksum
113 uint16_t checksum = 0;
114 for (uint8_t i = 0; i < CHECKSUM_START_INDEX; i++) {
115 checksum += buffer[i];
116 }
117
118 const uint16_t check = encode_uint16(buffer[CHECKSUM_START_INDEX], buffer[CHECKSUM_START_INDEX + 1]);
119 if (checksum != check) {
120 ESP_LOGW(TAG, "Checksum mismatch: %u != %u", checksum, check);
121 return false;
122 }
123
124 // The data comes in endian'd, this solves it so it works on all platforms
125 uint16_t buffer_u16[COUNT_16_BIT_VALUES];
126 for (uint8_t i = 0; i < COUNT_16_BIT_VALUES; i++) {
127 const uint8_t buffer_index = COUNT_START_CHARACTER_BYTES + i * 2;
128 buffer_u16[i] = encode_uint16(buffer[buffer_index], buffer[buffer_index + 1]);
129 }
130
131 // put it into a nice struct :)
132 memcpy((void *) data, (void *) buffer_u16, COUNT_16_BIT_VALUES * 2);
133
134 return true;
135}
136
137} // namespace esphome::pmsa003i
uint8_t checksum
Definition bl0906.h:3
void mark_failed()
Mark this component as failed.
void status_clear_warning()
Definition component.h:289
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:163
bool read_data_(PM25AQIData *data)
Definition pmsa003i.cpp:88
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:68
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:12
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:915
! Structure holding Plantower's standard packet
Definition pmsa003i.h:11
uint16_t particles_50um
5.0um Particle Count
Definition pmsa003i.h:23
uint16_t pm25_standard
Standard PM2.5.
Definition pmsa003i.h:14
uint16_t pm10_env
Environmental PM1.0.
Definition pmsa003i.h:16
uint16_t particles_25um
2.5um Particle Count
Definition pmsa003i.h:22
uint16_t pm100_env
Environmental PM10.0.
Definition pmsa003i.h:18
uint16_t particles_03um
0.3um Particle Count
Definition pmsa003i.h:19
uint16_t particles_05um
0.5um Particle Count
Definition pmsa003i.h:20
uint16_t particles_10um
1.0um Particle Count
Definition pmsa003i.h:21
uint16_t particles_100um
10.0um Particle Count
Definition pmsa003i.h:24
uint16_t pm10_standard
Standard PM1.0.
Definition pmsa003i.h:13
uint16_t pm25_env
Environmental PM2.5.
Definition pmsa003i.h:17
uint16_t pm100_standard
Standard PM10.0.
Definition pmsa003i.h:15