ESPHome 2025.5.0
Loading...
Searching...
No Matches
pmsa003i.cpp
Go to the documentation of this file.
1#include "pmsa003i.h"
2#include "esphome/core/log.h"
4#include <cstring>
5
6namespace esphome {
7namespace pmsa003i {
8
9static const char *const TAG = "pmsa003i";
10
11static const uint8_t COUNT_PAYLOAD_BYTES = 28;
12static const uint8_t COUNT_PAYLOAD_LENGTH_BYTES = 2;
13static const uint8_t COUNT_START_CHARACTER_BYTES = 2;
14static const uint8_t COUNT_DATA_BYTES = COUNT_START_CHARACTER_BYTES + COUNT_PAYLOAD_LENGTH_BYTES + COUNT_PAYLOAD_BYTES;
15static const uint8_t CHECKSUM_START_INDEX = COUNT_DATA_BYTES - 2;
16static const uint8_t COUNT_16_BIT_VALUES = (COUNT_PAYLOAD_LENGTH_BYTES + COUNT_PAYLOAD_BYTES) / 2;
17static const uint8_t START_CHARACTER_1 = 0x42;
18static const uint8_t START_CHARACTER_2 = 0x4D;
19static const uint8_t READ_DATA_RETRY_COUNT = 3;
20
22 ESP_LOGCONFIG(TAG, "Setting up pmsa003i...");
23
24 PM25AQIData data;
25 bool successful_read = this->read_data_(&data);
26
27 if (!successful_read) {
28 for (uint8_t i = 0; i < READ_DATA_RETRY_COUNT; i++) {
29 successful_read = this->read_data_(&data);
30 if (successful_read) {
31 break;
32 }
33 }
34 }
35
36 if (!successful_read) {
37 this->mark_failed();
38 return;
39 }
40}
41
43 ESP_LOGCONFIG(TAG, "PMSA003I:");
44 LOG_I2C_DEVICE(this);
45}
46
48 PM25AQIData data;
49
50 bool successful_read = this->read_data_(&data);
51
52 // Update sensors
53 if (successful_read) {
55 ESP_LOGV(TAG, "Read success. Updating sensors.");
56
57 if (this->standard_units_) {
58 if (this->pm_1_0_sensor_ != nullptr)
60 if (this->pm_2_5_sensor_ != nullptr)
62 if (this->pm_10_0_sensor_ != nullptr)
64 } else {
65 if (this->pm_1_0_sensor_ != nullptr)
67 if (this->pm_2_5_sensor_ != nullptr)
69 if (this->pm_10_0_sensor_ != nullptr)
71 }
72
73 if (this->pmc_0_3_sensor_ != nullptr)
75 if (this->pmc_0_5_sensor_ != nullptr)
77 if (this->pmc_1_0_sensor_ != nullptr)
79 if (this->pmc_2_5_sensor_ != nullptr)
81 if (this->pmc_5_0_sensor_ != nullptr)
83 if (this->pmc_10_0_sensor_ != nullptr)
85 } else {
86 this->status_set_warning();
87 ESP_LOGV(TAG, "Read failure. Skipping update.");
88 }
89}
90
92 uint8_t buffer[COUNT_DATA_BYTES];
93
94 this->read_bytes_raw(buffer, COUNT_DATA_BYTES);
95
96 // https://github.com/adafruit/Adafruit_PM25AQI
97
98 // Check that start byte is correct!
99 if (buffer[0] != START_CHARACTER_1 || buffer[1] != START_CHARACTER_2) {
100 ESP_LOGW(TAG, "Start character mismatch: %02X %02X != %02X %02X", buffer[0], buffer[1], START_CHARACTER_1,
101 START_CHARACTER_2);
102 return false;
103 }
104
105 const uint16_t payload_length = encode_uint16(buffer[2], buffer[3]);
106 if (payload_length != COUNT_PAYLOAD_BYTES) {
107 ESP_LOGW(TAG, "Payload length mismatch: %u != %u", payload_length, COUNT_PAYLOAD_BYTES);
108 return false;
109 }
110
111 // Calculate checksum
112 uint16_t checksum = 0;
113 for (uint8_t i = 0; i < CHECKSUM_START_INDEX; i++) {
114 checksum += buffer[i];
115 }
116
117 const uint16_t check = encode_uint16(buffer[CHECKSUM_START_INDEX], buffer[CHECKSUM_START_INDEX + 1]);
118 if (checksum != check) {
119 ESP_LOGW(TAG, "Checksum mismatch: %u != %u", checksum, check);
120 return false;
121 }
122
123 // The data comes in endian'd, this solves it so it works on all platforms
124 uint16_t buffer_u16[COUNT_16_BIT_VALUES];
125 for (uint8_t i = 0; i < COUNT_16_BIT_VALUES; i++) {
126 const uint8_t buffer_index = COUNT_START_CHARACTER_BYTES + i * 2;
127 buffer_u16[i] = encode_uint16(buffer[buffer_index], buffer[buffer_index + 1]);
128 }
129
130 // put it into a nice struct :)
131 memcpy((void *) data, (void *) buffer_u16, COUNT_16_BIT_VALUES * 2);
132
133 return true;
134}
135
136} // namespace pmsa003i
137} // namespace esphome
uint8_t checksum
Definition bl0906.h:3
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
optional< std::array< uint8_t, N > > read_bytes_raw()
Definition i2c.h:229
bool read_data_(PM25AQIData *data)
Definition pmsa003i.cpp:91
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
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:191
! Structure holding Plantower's standard packet
Definition pmsa003i.h:12
uint16_t particles_50um
5.0um Particle Count
Definition pmsa003i.h:24
uint16_t pm25_standard
Standard PM2.5.
Definition pmsa003i.h:15
uint16_t pm10_env
Environmental PM1.0.
Definition pmsa003i.h:17
uint16_t particles_25um
2.5um Particle Count
Definition pmsa003i.h:23
uint16_t pm100_env
Environmental PM10.0.
Definition pmsa003i.h:19
uint16_t particles_03um
0.3um Particle Count
Definition pmsa003i.h:20
uint16_t particles_05um
0.5um Particle Count
Definition pmsa003i.h:21
uint16_t particles_10um
1.0um Particle Count
Definition pmsa003i.h:22
uint16_t particles_100um
10.0um Particle Count
Definition pmsa003i.h:25
uint16_t pm10_standard
Standard PM1.0.
Definition pmsa003i.h:14
uint16_t pm25_env
Environmental PM2.5.
Definition pmsa003i.h:18
uint16_t pm100_standard
Standard PM10.0.
Definition pmsa003i.h:16