ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
sds011.cpp
Go to the documentation of this file.
1#include "sds011.h"
2#include "esphome/core/log.h"
4
5namespace esphome::sds011 {
6
7ESPHOME_LOG_TAG(TAG, "sds011");
8
9static const uint8_t SDS011_MSG_REQUEST_LENGTH = 19;
10static const uint8_t SDS011_MSG_RESPONSE_LENGTH = 10;
11static const uint8_t SDS011_DATA_REQUEST_LENGTH = 15;
12static const uint8_t SDS011_DATA_RESPONSE_LENGTH = 6;
13static const uint8_t SDS011_MSG_HEAD = 0xaa;
14static const uint8_t SDS011_MSG_TAIL = 0xab;
15static const uint8_t SDS011_COMMAND_ID_REQUEST = 0xb4;
16static const uint8_t SDS011_COMMAND_ID_RESPONSE = 0xc5;
17static const uint8_t SDS011_COMMAND_ID_DATA = 0xc0;
18static const uint8_t SDS011_COMMAND_REPORT_MODE = 0x02;
19static const uint8_t SDS011_COMMAND_QUERY_DATA = 0x04;
20static const uint8_t SDS011_COMMAND_SET_DEVICE_ID = 0x05;
21static const uint8_t SDS011_COMMAND_SLEEP = 0x06;
22static const uint8_t SDS011_COMMAND_FIRMWARE = 0x07;
23static const uint8_t SDS011_COMMAND_PERIOD = 0x08;
24static const uint8_t SDS011_GET_MODE = 0x00;
25static const uint8_t SDS011_SET_MODE = 0x01;
26static const uint8_t SDS011_MODE_REPORT_ACTIVE = 0x00;
27static const uint8_t SDS011_MODE_REPORT_QUERY = 0x01;
28static const uint8_t SDS011_MODE_SLEEP = 0x00;
29static const uint8_t SDS011_MODE_WORK = 0x01;
30
32 if (this->rx_mode_only_) {
33 // In RX-only mode we do not setup the sensor, it is assumed to be setup
34 // already
35 return;
36 }
37 uint8_t command_data[SDS011_DATA_REQUEST_LENGTH] = {0};
38 command_data[0] = SDS011_COMMAND_REPORT_MODE;
39 command_data[1] = SDS011_SET_MODE;
40 command_data[2] = SDS011_MODE_REPORT_ACTIVE;
41 command_data[13] = 0xff;
42 command_data[14] = 0xff;
43 this->sds011_write_command_(command_data);
44
45 command_data[0] = SDS011_COMMAND_PERIOD;
46 command_data[1] = SDS011_SET_MODE;
47 command_data[2] = this->update_interval_min_;
48 command_data[13] = 0xff;
49 command_data[14] = 0xff;
50 this->sds011_write_command_(command_data);
51}
52
53void SDS011Component::set_working_state(bool working_state) {
54 if (this->rx_mode_only_) {
55 // In RX-only mode we do not setup the sensor, it is assumed to be setup
56 // already
57 return;
58 }
59 uint8_t command_data[SDS011_DATA_REQUEST_LENGTH] = {0};
60 command_data[0] = SDS011_COMMAND_SLEEP;
61 command_data[1] = SDS011_SET_MODE;
62 command_data[2] = working_state ? SDS011_MODE_WORK : SDS011_MODE_SLEEP;
63 command_data[13] = 0xff;
64 command_data[14] = 0xff;
65 this->sds011_write_command_(command_data);
66}
67
69 ESP_LOGCONFIG(TAG,
70 "SDS011:\n"
71 " Update Interval: %u min\n"
72 " RX-only mode: %s",
73 this->update_interval_min_, ONOFF(this->rx_mode_only_));
74 LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_);
75 LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_);
76}
77
80 if ((now - this->last_transmission_ >= 500) && this->data_index_) {
81 // last transmission too long ago. Reset RX index.
82 ESP_LOGV(TAG, "Last transmission too long ago. Reset RX index.");
83 this->data_index_ = 0;
84 }
85
86 const size_t avail = this->available();
87 if (avail == 0) {
88 return;
89 }
90
91 this->last_transmission_ = now;
92 this->read_available(
93 [this](uint8_t byte) {
94 this->data_[this->data_index_] = byte;
95 auto check = this->check_byte_();
96 if (!check.has_value()) {
97 // finished
98 this->parse_data_();
99 this->data_index_ = 0;
100 } else if (!*check) {
101 // wrong data
102 ESP_LOGV(TAG, "Byte %i of received data frame is invalid.", this->data_index_);
103 this->data_index_ = 0;
104 } else {
105 // next byte
106 this->data_index_++;
107 }
108 },
109 avail);
110}
111
112void SDS011Component::sds011_write_command_(const uint8_t *command_data) {
113 this->write_byte(SDS011_MSG_HEAD);
114 this->write_byte(SDS011_COMMAND_ID_REQUEST);
115 this->write_array(command_data, SDS011_DATA_REQUEST_LENGTH);
116 this->write_byte(sds011_checksum_(command_data, SDS011_DATA_REQUEST_LENGTH));
117 this->write_byte(SDS011_MSG_TAIL);
118}
119
120uint8_t SDS011Component::sds011_checksum_(const uint8_t *command_data, uint8_t length) const {
121 return sum_bytes(command_data, length);
122}
123
124optional<bool> SDS011Component::check_byte_() const {
125 uint8_t index = this->data_index_;
126 uint8_t byte = this->data_[index];
127
128 if (index == 0) {
129 return byte == SDS011_MSG_HEAD;
130 }
131
132 if (index == 1) {
133 return byte == SDS011_COMMAND_ID_DATA;
134 }
135
136 if ((index >= 2) && (index <= 7)) {
137 return true;
138 }
139
140 if (index == 8) {
141 // checksum is without checksum bytes
142 uint8_t checksum = sds011_checksum_(this->data_ + 2, SDS011_DATA_RESPONSE_LENGTH);
143 if (checksum != byte) {
144 ESP_LOGW(TAG, "SDS011 Checksum doesn't match: 0x%02X!=0x%02X", byte, checksum);
145 return false;
146 }
147 return true;
148 }
149
150 if (index == 9) {
151 if (byte != SDS011_MSG_TAIL) {
152 return false;
153 }
154 }
155
156 return {};
157}
158
160 this->status_clear_warning();
161 const float pm_2_5_concentration = this->get_16_bit_uint_(2) / 10.0f;
162 const float pm_10_0_concentration = this->get_16_bit_uint_(4) / 10.0f;
163
164 ESP_LOGD(TAG, "Got PM2.5 Concentration: %.1f µg/m³, PM10.0 Concentration: %.1f µg/m³", pm_2_5_concentration,
165 pm_10_0_concentration);
166 if (pm_2_5_concentration <= 0 && pm_10_0_concentration <= 0) {
167 // not yet any valid data
168 return;
169 }
170 if (this->pm_2_5_sensor_ != nullptr) {
171 this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
172 }
173 if (this->pm_10_0_sensor_ != nullptr) {
174 this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
175 }
176}
177
178void SDS011Component::set_update_interval_min(uint8_t update_interval_min) {
179 this->update_interval_min_ = update_interval_min;
180}
181
182} // namespace esphome::sds011
uint8_t checksum
Definition bl0906.h:3
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void status_clear_warning()
Definition component.h:296
sensor::Sensor * pm_10_0_sensor_
Definition sds011.h:36
void set_update_interval_min(uint8_t update_interval_min)
Definition sds011.cpp:178
uint16_t get_16_bit_uint_(uint8_t start_index) const
Definition sds011.h:31
sensor::Sensor * pm_2_5_sensor_
Definition sds011.h:35
void sds011_write_command_(const uint8_t *command)
Definition sds011.cpp:112
void set_working_state(bool working_state)
Definition sds011.cpp:53
optional< bool > check_byte_() const
Definition sds011.cpp:124
uint8_t sds011_checksum_(const uint8_t *command_data, uint8_t length) const
Definition sds011.cpp:120
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
void write_byte(uint8_t data)
Definition uart.h:22
bool read_available(F &&on_byte)
Same as read_available_chunks(), calling on_byte(uint8_t) once per byte.
Definition uart.h:77
void write_array(const uint8_t *data, size_t len)
Definition uart.h:30
ESPHOME_LOG_TAG(TAG, "sds011")
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t
uint16_t length
Definition tt21100.cpp:0