9static const char *
const TAG =
"sps30";
11static const uint16_t SPS30_CMD_GET_ARTICLE_CODE = 0xD025;
12static const uint16_t SPS30_CMD_GET_SERIAL_NUMBER = 0xD033;
13static const uint16_t SPS30_CMD_GET_FIRMWARE_VERSION = 0xD100;
14static const uint16_t SPS30_CMD_START_CONTINUOUS_MEASUREMENTS = 0x0010;
15static const uint16_t SPS30_CMD_START_CONTINUOUS_MEASUREMENTS_ARG = 0x0300;
16static const uint16_t SPS30_CMD_GET_DATA_READY_STATUS = 0x0202;
17static const uint16_t SPS30_CMD_READ_MEASUREMENT = 0x0300;
18static const uint16_t SPS30_CMD_STOP_MEASUREMENTS = 0x0104;
19static const uint16_t SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS = 0x8004;
20static const uint16_t SPS30_CMD_START_FAN_CLEANING = 0x5607;
21static const uint16_t SPS30_CMD_SOFT_RESET = 0xD304;
22static const size_t SERIAL_NUMBER_LENGTH = 8;
23static const uint8_t MAX_SKIPPED_DATA_CYCLES_BEFORE_ERROR = 5;
24static const uint32_t SPS30_WARM_UP_SEC = 30;
37 uint16_t raw_serial_number[8];
38 if (!this->
get_register(SPS30_CMD_GET_SERIAL_NUMBER, raw_serial_number, 8, 1)) {
44 for (
size_t i = 0; i < 8; ++i) {
45 this->
serial_number_[i * 2] =
static_cast<char>(raw_serial_number[i] >> 8);
46 this->
serial_number_[i * 2 + 1] = uint16_t(uint16_t(raw_serial_number[i] & 0xFF));
55 result = this->
write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS);
69 this->next_state_ =
READ;
76 ESP_LOGCONFIG(TAG,
"SPS30:");
79 switch (this->error_code_) {
81 ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
84 ESP_LOGW(TAG,
"Measurement Initialization failed");
87 ESP_LOGW(TAG,
"Unable to request serial number");
90 ESP_LOGW(TAG,
"Unable to read serial number");
93 ESP_LOGW(TAG,
"Unable to request firmware version");
96 ESP_LOGW(TAG,
"Unable to read firmware version");
99 ESP_LOGW(TAG,
"Unknown setup error");
103 LOG_UPDATE_INTERVAL(
this);
105 " Serial number: %s\n"
106 " Firmware version v%0d.%0d",
109 ESP_LOGCONFIG(TAG,
" Idle interval: %" PRIu32
"s", this->
idle_interval_.value() / 1000);
111 LOG_SENSOR(
" ",
"PM1.0 Weight Concentration", this->
pm_1_0_sensor_);
112 LOG_SENSOR(
" ",
"PM2.5 Weight Concentration", this->
pm_2_5_sensor_);
113 LOG_SENSOR(
" ",
"PM4 Weight Concentration", this->
pm_4_0_sensor_);
127 ESP_LOGD(TAG,
"Reconnecting");
129 ESP_LOGD(TAG,
"Soft-reset successful; waiting 500 ms");
135 ESP_LOGD(TAG,
"Reconnected; resuming continuous measurement");
138 ESP_LOGD(TAG,
"Soft-reset failed");
145 if (this->next_state_ !=
NONE && (int32_t) (this->
next_state_ms_ - update_start_ms) > 0) {
146 ESP_LOGD(TAG,
"Sensor waiting for %" PRIu32
"ms before transitioning to state %d.",
151 switch (this->next_state_) {
168 uint16_t raw_read_status;
169 if (!this->
read_data(&raw_read_status, 1) || raw_read_status == 0x00) {
170 ESP_LOGD(TAG,
"Not ready");
175 ESP_LOGD(TAG,
"Exceeded max attempts; will reinitialize");
182 ESP_LOGW(TAG,
"Error reading status");
188 uint16_t raw_data[20];
190 ESP_LOGW(TAG,
"Error reading data");
195 union uint32_float_t {
201 uint32_float_t pm_1_0{.uint32 = (((
uint32_t(raw_data[0])) << 16) | (
uint32_t(raw_data[1])))};
202 uint32_float_t pm_2_5{.uint32 = (((
uint32_t(raw_data[2])) << 16) | (
uint32_t(raw_data[3])))};
203 uint32_float_t pm_4_0{.uint32 = (((
uint32_t(raw_data[4])) << 16) | (
uint32_t(raw_data[5])))};
204 uint32_float_t pm_10_0{.uint32 = (((
uint32_t(raw_data[6])) << 16) | (
uint32_t(raw_data[7])))};
207 uint32_float_t pmc_0_5{.uint32 = (((
uint32_t(raw_data[8])) << 16) | (
uint32_t(raw_data[9])))};
208 uint32_float_t pmc_1_0{.uint32 = (((
uint32_t(raw_data[10])) << 16) | (
uint32_t(raw_data[11])))};
209 uint32_float_t pmc_2_5{.uint32 = (((
uint32_t(raw_data[12])) << 16) | (
uint32_t(raw_data[13])))};
210 uint32_float_t pmc_4_0{.uint32 = (((
uint32_t(raw_data[14])) << 16) | (
uint32_t(raw_data[15])))};
211 uint32_float_t pmc_10_0{.uint32 = (((
uint32_t(raw_data[16])) << 16) | (
uint32_t(raw_data[17])))};
214 uint32_float_t pm_size{.uint32 = (((
uint32_t(raw_data[18])) << 16) | (
uint32_t(raw_data[19])))};
239 this->status_clear_warning();
240 this->skipped_data_read_cycles_ = 0;
245 this->stop_measurement();
246 this->next_state_ms_ = millis() + this->idle_interval_.value();
247 this->next_state_ = WAKE;
249 this->next_state_ms_ = millis();
255 if (!this->
write_command(SPS30_CMD_START_CONTINUOUS_MEASUREMENTS, SPS30_CMD_START_CONTINUOUS_MEASUREMENTS_ARG)) {
256 ESP_LOGE(TAG,
"Error initiating measurements");
259 ESP_LOGD(TAG,
"Started measurements");
263 this->next_state_ =
READ;
271 ESP_LOGE(TAG,
"Error stopping measurements");
274 ESP_LOGD(TAG,
"Stopped measurements");
277 this->next_state_ =
NONE;
285 ESP_LOGE(TAG,
"Start fan cleaning failed (%d)", this->
last_error_);
288 ESP_LOGD(TAG,
"Fan auto clean started");
void mark_failed()
Mark this component as failed.
void status_set_warning()
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
bool status_has_warning() const
void status_clear_warning()
i2c::ErrorCode last_error_
last error code from I2C operation
bool get_register(uint16_t command, uint16_t *data, uint8_t len, uint8_t delay=0)
get data words from I2C register.
bool write_command(T i2c_register)
Write a command to the I2C device.
bool read_data(uint16_t *data, uint8_t len)
Read data words from I2C device.
void publish_state(float state)
Publish a new state to the front-end.
uint16_t raw_firmware_version_
sensor::Sensor * pm_2_5_sensor_
optional< uint32_t > fan_interval_
@ FIRMWARE_VERSION_REQUEST_FAILED
@ FIRMWARE_VERSION_READ_FAILED
@ SERIAL_NUMBER_READ_FAILED
@ SERIAL_NUMBER_REQUEST_FAILED
@ MEASUREMENT_INIT_FAILED
bool start_continuous_measurement_()
sensor::Sensor * pmc_4_0_sensor_
sensor::Sensor * pmc_2_5_sensor_
sensor::Sensor * pm_10_0_sensor_
uint8_t skipped_data_read_cycles_
Terminating NULL character.
enum esphome::sps30::SPS30Component::NextState NONE
bool start_fan_cleaning()
sensor::Sensor * pm_1_0_sensor_
sensor::Sensor * pm_size_sensor_
void dump_config() override
sensor::Sensor * pmc_0_5_sensor_
sensor::Sensor * pm_4_0_sensor_
optional< uint32_t > idle_interval_
sensor::Sensor * pmc_10_0_sensor_
sensor::Sensor * pmc_1_0_sensor_
uint32_t IRAM_ATTR HOT millis()