10static const char *
const TAG =
"sps30";
12static const uint16_t SPS30_CMD_GET_ARTICLE_CODE = 0xD025;
13static const uint16_t SPS30_CMD_GET_SERIAL_NUMBER = 0xD033;
14static const uint16_t SPS30_CMD_GET_FIRMWARE_VERSION = 0xD100;
15static const uint16_t SPS30_CMD_START_CONTINUOUS_MEASUREMENTS = 0x0010;
16static const uint16_t SPS30_CMD_START_CONTINUOUS_MEASUREMENTS_ARG = 0x0300;
17static const uint16_t SPS30_CMD_GET_DATA_READY_STATUS = 0x0202;
18static const uint16_t SPS30_CMD_READ_MEASUREMENT = 0x0300;
19static const uint16_t SPS30_CMD_STOP_MEASUREMENTS = 0x0104;
20static const uint16_t SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS = 0x8004;
21static const uint16_t SPS30_CMD_START_FAN_CLEANING = 0x5607;
22static const uint16_t SPS30_CMD_SOFT_RESET = 0xD304;
23static const size_t SERIAL_NUMBER_LENGTH = 8;
24static const uint8_t MAX_SKIPPED_DATA_CYCLES_BEFORE_ERROR = 5;
25static const uint32_t SPS30_WARM_UP_SEC = 30;
38 uint16_t raw_serial_number[8];
39 if (!this->
get_register(SPS30_CMD_GET_SERIAL_NUMBER, raw_serial_number, 8, 1)) {
45 for (
size_t i = 0; i < 8; ++i) {
46 this->
serial_number_[i * 2] =
static_cast<char>(raw_serial_number[i] >> 8);
47 this->
serial_number_[i * 2 + 1] = uint16_t(uint16_t(raw_serial_number[i] & 0xFF));
56 result = this->
write_command(SPS30_CMD_SET_AUTOMATIC_CLEANING_INTERVAL_SECONDS);
70 this->next_state_ =
READ;
77 ESP_LOGCONFIG(TAG,
"SPS30:");
80 switch (this->error_code_) {
82 ESP_LOGW(TAG, ESP_LOG_MSG_COMM_FAIL);
85 ESP_LOGW(TAG,
"Measurement Initialization failed");
88 ESP_LOGW(TAG,
"Unable to request serial number");
91 ESP_LOGW(TAG,
"Unable to read serial number");
94 ESP_LOGW(TAG,
"Unable to request firmware version");
97 ESP_LOGW(TAG,
"Unable to read firmware version");
100 ESP_LOGW(TAG,
"Unknown setup error");
104 LOG_UPDATE_INTERVAL(
this);
106 " Serial number: %s\n"
107 " Firmware version v%0d.%0d",
110 ESP_LOGCONFIG(TAG,
" Idle interval: %" PRIu32
"s", this->
idle_interval_.value() / 1000);
112 LOG_SENSOR(
" ",
"PM1.0 Weight Concentration", this->
pm_1_0_sensor_);
113 LOG_SENSOR(
" ",
"PM2.5 Weight Concentration", this->
pm_2_5_sensor_);
114 LOG_SENSOR(
" ",
"PM4 Weight Concentration", this->
pm_4_0_sensor_);
128 ESP_LOGD(TAG,
"Reconnecting");
130 ESP_LOGD(TAG,
"Soft-reset successful; waiting 500 ms");
136 ESP_LOGD(TAG,
"Reconnected; resuming continuous measurement");
139 ESP_LOGD(TAG,
"Soft-reset failed");
146 if (this->next_state_ !=
NONE && (int32_t) (this->
next_state_ms_ - update_start_ms) > 0) {
147 ESP_LOGD(TAG,
"Sensor waiting for %" PRIu32
"ms before transitioning to state %d.",
152 switch (this->next_state_) {
169 uint16_t raw_read_status;
170 if (!this->
read_data(&raw_read_status, 1) || raw_read_status == 0x00) {
171 ESP_LOGD(TAG,
"Not ready");
176 ESP_LOGD(TAG,
"Exceeded max attempts; will reinitialize");
183 ESP_LOGW(TAG,
"Error reading status");
189 uint16_t raw_data[20];
191 ESP_LOGW(TAG,
"Error reading data");
196 union uint32_float_t {
202 uint32_float_t pm_1_0{.uint32 = (((
uint32_t(raw_data[0])) << 16) | (
uint32_t(raw_data[1])))};
203 uint32_float_t pm_2_5{.uint32 = (((
uint32_t(raw_data[2])) << 16) | (
uint32_t(raw_data[3])))};
204 uint32_float_t pm_4_0{.uint32 = (((
uint32_t(raw_data[4])) << 16) | (
uint32_t(raw_data[5])))};
205 uint32_float_t pm_10_0{.uint32 = (((
uint32_t(raw_data[6])) << 16) | (
uint32_t(raw_data[7])))};
208 uint32_float_t pmc_0_5{.uint32 = (((
uint32_t(raw_data[8])) << 16) | (
uint32_t(raw_data[9])))};
209 uint32_float_t pmc_1_0{.uint32 = (((
uint32_t(raw_data[10])) << 16) | (
uint32_t(raw_data[11])))};
210 uint32_float_t pmc_2_5{.uint32 = (((
uint32_t(raw_data[12])) << 16) | (
uint32_t(raw_data[13])))};
211 uint32_float_t pmc_4_0{.uint32 = (((
uint32_t(raw_data[14])) << 16) | (
uint32_t(raw_data[15])))};
212 uint32_float_t pmc_10_0{.uint32 = (((
uint32_t(raw_data[16])) << 16) | (
uint32_t(raw_data[17])))};
215 uint32_float_t pm_size{.uint32 = (((
uint32_t(raw_data[18])) << 16) | (
uint32_t(raw_data[19])))};
240 this->status_clear_warning();
241 this->skipped_data_read_cycles_ = 0;
246 this->stop_measurement();
247 this->next_state_ms_ = millis() + this->idle_interval_.value();
248 this->next_state_ = WAKE;
250 this->next_state_ms_ = millis();
256 if (!this->
write_command(SPS30_CMD_START_CONTINUOUS_MEASUREMENTS, SPS30_CMD_START_CONTINUOUS_MEASUREMENTS_ARG)) {
257 ESP_LOGE(TAG,
"Error initiating measurements");
260 ESP_LOGD(TAG,
"Started measurements");
264 this->next_state_ =
READ;
272 ESP_LOGE(TAG,
"Error stopping measurements");
275 ESP_LOGD(TAG,
"Stopped measurements");
278 this->next_state_ =
NONE;
286 ESP_LOGE(TAG,
"Start fan cleaning failed (%d)", this->
last_error_);
289 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_
Providing packet encoding functions for exchanging data with a remote host.
uint32_t IRAM_ATTR HOT millis()