ESPHome 2025.5.0
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 {
6namespace sds011 {
7
8static const char *const TAG = "sds011";
9
10static const uint8_t SDS011_MSG_REQUEST_LENGTH = 19;
11static const uint8_t SDS011_MSG_RESPONSE_LENGTH = 10;
12static const uint8_t SDS011_DATA_REQUEST_LENGTH = 15;
13static const uint8_t SDS011_DATA_RESPONSE_LENGTH = 6;
14static const uint8_t SDS011_MSG_HEAD = 0xaa;
15static const uint8_t SDS011_MSG_TAIL = 0xab;
16static const uint8_t SDS011_COMMAND_ID_REQUEST = 0xb4;
17static const uint8_t SDS011_COMMAND_ID_RESPONSE = 0xc5;
18static const uint8_t SDS011_COMMAND_ID_DATA = 0xc0;
19static const uint8_t SDS011_COMMAND_REPORT_MODE = 0x02;
20static const uint8_t SDS011_COMMAND_QUERY_DATA = 0x04;
21static const uint8_t SDS011_COMMAND_SET_DEVICE_ID = 0x05;
22static const uint8_t SDS011_COMMAND_SLEEP = 0x06;
23static const uint8_t SDS011_COMMAND_FIRMWARE = 0x07;
24static const uint8_t SDS011_COMMAND_PERIOD = 0x08;
25static const uint8_t SDS011_GET_MODE = 0x00;
26static const uint8_t SDS011_SET_MODE = 0x01;
27static const uint8_t SDS011_MODE_REPORT_ACTIVE = 0x00;
28static const uint8_t SDS011_MODE_REPORT_QUERY = 0x01;
29static const uint8_t SDS011_MODE_SLEEP = 0x00;
30static const uint8_t SDS011_MODE_WORK = 0x01;
31
33 if (this->rx_mode_only_) {
34 // In RX-only mode we do not setup the sensor, it is assumed to be setup
35 // already
36 return;
37 }
38 uint8_t command_data[SDS011_DATA_REQUEST_LENGTH] = {0};
39 command_data[0] = SDS011_COMMAND_REPORT_MODE;
40 command_data[1] = SDS011_SET_MODE;
41 command_data[2] = SDS011_MODE_REPORT_ACTIVE;
42 command_data[13] = 0xff;
43 command_data[14] = 0xff;
44 this->sds011_write_command_(command_data);
45
46 command_data[0] = SDS011_COMMAND_PERIOD;
47 command_data[1] = SDS011_SET_MODE;
48 command_data[2] = this->update_interval_min_;
49 command_data[13] = 0xff;
50 command_data[14] = 0xff;
51 this->sds011_write_command_(command_data);
52}
53
54void SDS011Component::set_working_state(bool working_state) {
55 if (this->rx_mode_only_) {
56 // In RX-only mode we do not setup the sensor, it is assumed to be setup
57 // already
58 return;
59 }
60 uint8_t command_data[SDS011_DATA_REQUEST_LENGTH] = {0};
61 command_data[0] = SDS011_COMMAND_SLEEP;
62 command_data[1] = SDS011_SET_MODE;
63 command_data[2] = working_state ? SDS011_MODE_WORK : SDS011_MODE_SLEEP;
64 command_data[13] = 0xff;
65 command_data[14] = 0xff;
66 this->sds011_write_command_(command_data);
67}
68
70 ESP_LOGCONFIG(TAG, "SDS011:");
71 ESP_LOGCONFIG(TAG, " Update Interval: %u min", this->update_interval_min_);
72 ESP_LOGCONFIG(TAG, " RX-only mode: %s", ONOFF(this->rx_mode_only_));
73 LOG_SENSOR(" ", "PM2.5", this->pm_2_5_sensor_);
74 LOG_SENSOR(" ", "PM10.0", this->pm_10_0_sensor_);
75 this->check_uart_settings(9600);
76}
77
79 const uint32_t now = App.get_loop_component_start_time();
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 if (this->available() == 0) {
87 return;
88 }
89
90 this->last_transmission_ = now;
91 while (this->available() != 0) {
92 this->read_byte(&this->data_[this->data_index_]);
93 auto check = this->check_byte_();
94 if (!check.has_value()) {
95 // finished
96 this->parse_data_();
97 this->data_index_ = 0;
98 } else if (!*check) {
99 // wrong data
100 ESP_LOGV(TAG, "Byte %i of received data frame is invalid.", this->data_index_);
101 this->data_index_ = 0;
102 } else {
103 // next byte
104 this->data_index_++;
105 }
106 }
107}
108
110
111void SDS011Component::set_rx_mode_only(bool rx_mode_only) { this->rx_mode_only_ = rx_mode_only; }
112
113void SDS011Component::sds011_write_command_(const uint8_t *command_data) {
114 this->write_byte(SDS011_MSG_HEAD);
115 this->write_byte(SDS011_COMMAND_ID_REQUEST);
116 this->write_array(command_data, SDS011_DATA_REQUEST_LENGTH);
117 this->write_byte(sds011_checksum_(command_data, SDS011_DATA_REQUEST_LENGTH));
118 this->write_byte(SDS011_MSG_TAIL);
119}
120
121uint8_t SDS011Component::sds011_checksum_(const uint8_t *command_data, uint8_t length) const {
122 uint8_t sum = 0;
123 for (uint8_t i = 0; i < length; i++) {
124 sum += command_data[i];
125 }
126 return sum;
127}
128
130 uint8_t index = this->data_index_;
131 uint8_t byte = this->data_[index];
132
133 if (index == 0) {
134 return byte == SDS011_MSG_HEAD;
135 }
136
137 if (index == 1) {
138 return byte == SDS011_COMMAND_ID_DATA;
139 }
140
141 if ((index >= 2) && (index <= 7)) {
142 return true;
143 }
144
145 if (index == 8) {
146 // checksum is without checksum bytes
147 uint8_t checksum = sds011_checksum_(this->data_ + 2, SDS011_DATA_RESPONSE_LENGTH);
148 if (checksum != byte) {
149 ESP_LOGW(TAG, "SDS011 Checksum doesn't match: 0x%02X!=0x%02X", byte, checksum);
150 return false;
151 }
152 return true;
153 }
154
155 if (index == 9) {
156 if (byte != SDS011_MSG_TAIL) {
157 return false;
158 }
159 }
160
161 return {};
162}
163
165 this->status_clear_warning();
166 const float pm_2_5_concentration = this->get_16_bit_uint_(2) / 10.0f;
167 const float pm_10_0_concentration = this->get_16_bit_uint_(4) / 10.0f;
168
169 ESP_LOGD(TAG, "Got PM2.5 Concentration: %.1f µg/m³, PM10.0 Concentration: %.1f µg/m³", pm_2_5_concentration,
170 pm_10_0_concentration);
171 if (pm_2_5_concentration <= 0 && pm_10_0_concentration <= 0) {
172 // not yet any valid data
173 return;
174 }
175 if (this->pm_2_5_sensor_ != nullptr) {
176 this->pm_2_5_sensor_->publish_state(pm_2_5_concentration);
177 }
178 if (this->pm_10_0_sensor_ != nullptr) {
179 this->pm_10_0_sensor_->publish_state(pm_10_0_concentration);
180 }
181}
182
183uint16_t SDS011Component::get_16_bit_uint_(uint8_t start_index) const {
184 return (uint16_t(this->data_[start_index + 1]) << 8) | uint16_t(this->data_[start_index]);
185}
186void SDS011Component::set_update_interval_min(uint8_t update_interval_min) {
187 this->update_interval_min_ = update_interval_min;
188}
189
190} // namespace sds011
191} // namespace esphome
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()
sensor::Sensor * pm_10_0_sensor_
Definition sds011.h:38
void set_update_interval_min(uint8_t update_interval_min)
Definition sds011.cpp:186
uint16_t get_16_bit_uint_(uint8_t start_index) const
Definition sds011.cpp:183
sensor::Sensor * pm_2_5_sensor_
Definition sds011.h:37
float get_setup_priority() const override
Definition sds011.cpp:109
void sds011_write_command_(const uint8_t *command)
Definition sds011.cpp:113
void set_working_state(bool working_state)
Definition sds011.cpp:54
optional< bool > check_byte_() const
Definition sds011.cpp:129
void set_rx_mode_only(bool rx_mode_only)
Manually set the rx-only mode. Defaults to false.
Definition sds011.cpp:111
uint8_t sds011_checksum_(const uint8_t *command_data, uint8_t length) const
Definition sds011.cpp:121
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
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
bool read_byte(uint8_t *data)
Definition uart.h:29
void write_byte(uint8_t data)
Definition uart.h:19
void write_array(const uint8_t *data, size_t len)
Definition uart.h:21
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:19
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
Application App
Global storage of Application pointer - only one Application can exist.
uint16_t length
Definition tt21100.cpp:0