ESPHome 2026.4.0-dev
Loading...
Searching...
No Matches
spa06_base.cpp
Go to the documentation of this file.
1#include "spa06_base.h"
2
3#include <cinttypes>
4
6
8
9static const char *const TAG = "spa06";
10
11// Sign extension function for <=16 bit types
12inline int16_t decode16(uint8_t msb, uint8_t lsb, size_t bits, size_t head = 0) {
13 return static_cast<int16_t>(encode_uint16(msb, lsb) << head) >> (16 - bits);
14}
15
16// Sign extension function for <=32 bit types
17inline int32_t decode32(uint8_t xmsb, uint8_t msb, uint8_t lsb, uint8_t xlsb, size_t bits, size_t head = 0) {
18 return static_cast<int32_t>(encode_uint32(xmsb, msb, lsb, xlsb) << head) >> (32 - bits);
19}
20
22 ESP_LOGCONFIG(TAG, "SPA06:");
23 LOG_UPDATE_INTERVAL(this);
24 ESP_LOGCONFIG(TAG, " Measurement conversion time: %ums", this->conversion_time_);
25 if (this->temperature_sensor_) {
26 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
27 ESP_LOGCONFIG(TAG,
28 " Oversampling: %s\n"
29 " Rate: %s",
30 LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)),
31 LOG_STR_ARG(meas_rate_to_str(this->temperature_rate_)));
32 }
33 if (this->pressure_sensor_) {
34 LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
35 ESP_LOGCONFIG(TAG,
36 " Oversampling: %s\n"
37 " Rate: %s",
38 LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)),
39 LOG_STR_ARG(meas_rate_to_str(this->pressure_rate_)));
40 }
41}
42
44 // Startup sequence for SPA06 (Pg. 16, Figure 4.6.4):
45 // 1. Perform a soft reset
46 // 2. Verify sensor chip ID matches
47 // 3. Verify coefficients are ready
48 // 4. Read coefficients
49 // 5. Configure temperature and pressure sensors
50 // 6. Write communication settings
51 // 7. Write measurement settings (background measurement mode)
52
53 // 1. Soft reset
54 if (!this->soft_reset_()) {
55 this->mark_failed(LOG_STR("Reset failed"));
56 return;
57 }
58
59 // soft_reset_() internally delays by 3ms to make sure that
60 // the sensor is in a ready state and coefficients are ready.
61
62 // 2. Read chip ID
63 // TODO: check ID for consistency?
64 if (!spa_read_byte(SPA06_ID, &this->prod_id_.reg)) {
65 this->mark_failed(LOG_STR("Chip ID read failure"));
66 return;
67 }
68 ESP_LOGV(TAG,
69 "Product Info:\n"
70 " Prod ID: %u\n"
71 " Rev ID: %u",
72 this->prod_id_.bit.prod_id, this->prod_id_.bit.rev_id);
73
74 // 3. Read chip readiness from MEAS_CFG
75 // First check if the sensor reports ready
76 if (!spa_read_byte(SPA06_MEAS_CFG, &this->meas_.reg)) {
77 this->mark_failed(LOG_STR("Sensor status read failure"));
78 return;
79 }
80 // Check if the sensor reports coefficients are ready
81 if (!meas_.bit.coef_ready) {
82 this->mark_failed(LOG_STR("Coefficients not ready"));
83 return;
84 }
85
86 // 4. Read coefficients
87 if (!this->read_coefficients_()) {
88 this->mark_failed(LOG_STR("Coefficients read error"));
89 return;
90 }
91
92 // 5. Configure temperature and pressure sensors
93 // Default to measuring both temperature and pressure
94
95 // Temperature must be read regardless of configuration to compute pressure
96 // If temperature is not configured in config:
97 // - No oversampling is used
98 // - Lowest possible rate is configured
99 if (!this->temperature_sensor_) {
103 }
104
105 // If pressure is not configured in config
106 // - No oversampling is used
107 // - Lowest possible rate is configured
108 if (!this->pressure_sensor_) {
112 }
113
114 // Write temperature settings
116 this->mark_failed(LOG_STR("Temperature settings write fail"));
117 return;
118 }
119
120 // Write pressure settings
122 this->mark_failed(LOG_STR("Pressure settings write fail"));
123 return;
124 }
125 // 6. Write communication settings
126 // This call sets the bit shifts for pressure and temperature if
127 // their respective oversampling config is > X8
128 // This call also disables interrupts, FIFO, and specifies SPI 4-wire
131 this->mark_failed(LOG_STR("Comm settings write fail"));
132 return;
133 }
134
135 // 7. Write measurement settings
136 // This function sets background measurement mode without FIFO
138 this->mark_failed(LOG_STR("Measurement settings write fail"));
139 return;
140 }
141}
142
144 return this->write_sensor_settings_(oversampling, rate, SPA06_TMP_CFG);
145}
146
148 return this->write_sensor_settings_(oversampling, rate, SPA06_PSR_CFG);
149}
150
152 if (reg != SPA06_PSR_CFG && reg != SPA06_TMP_CFG) {
153 return false;
154 }
155 this->pt_meas_cfg_.bit.rate = rate;
156 this->pt_meas_cfg_.bit.prc = oversampling;
157 ESP_LOGD(TAG, "Config write: %02x", this->pt_meas_cfg_.reg);
158 return spa_write_byte(reg, this->pt_meas_cfg_.reg);
159}
160
162 this->meas_.bit.meas_crtl = crtl;
163 return spa_write_byte(SPA06_MEAS_CFG, this->meas_.reg);
164}
165
166bool SPA06Component::write_communication_settings_(bool pressure_shift, bool temperature_shift, bool interrupt_hl,
167 bool interrupt_fifo, bool interrupt_tmp, bool interrupt_prs,
168 bool enable_fifo, bool spi_3wire) {
169 this->cfg_.bit.p_shift = pressure_shift;
170 this->cfg_.bit.t_shift = temperature_shift;
171 this->cfg_.bit.int_hl = interrupt_hl;
172 this->cfg_.bit.int_fifo = interrupt_fifo;
173 this->cfg_.bit.int_tmp = interrupt_tmp;
174 this->cfg_.bit.int_prs = interrupt_prs;
175 this->cfg_.bit.fifo_en = enable_fifo;
176 this->cfg_.bit.spi_3wire = spi_3wire;
177 return spa_write_byte(SPA06_CFG_REG, this->cfg_.reg);
178}
179
181 uint8_t coef[SPA06_COEF_LEN];
183 return false;
184 }
185 this->c0_ = decode16(coef[0], coef[1], 12);
186 this->c1_ = decode16(coef[1], coef[2], 12, 4);
187 this->c00_ = decode32(coef[3], coef[4], coef[5], 0, 20);
188 this->c10_ = decode32(coef[5], coef[6], coef[7], 0, 20, 4);
189 this->c01_ = decode16(coef[8], coef[9], 16);
190 this->c11_ = decode16(coef[10], coef[11], 16);
191 this->c20_ = decode16(coef[12], coef[13], 16);
192 this->c21_ = decode16(coef[14], coef[15], 16);
193 this->c30_ = decode16(coef[16], coef[17], 16);
194 this->c31_ = decode16(coef[18], coef[19], 12);
195 this->c40_ = decode16(coef[19], coef[20], 12, 4);
196
197 ESP_LOGV(TAG,
198 "Coefficients:\n"
199 " c0: %i, c1: %i,\n"
200 " c00: %" PRIi32 ", c10: %" PRIi32 ", c20: %i, c30: %i, c40: %i,\n"
201 " c01: %i, c11: %i, c21: %i, c31: %i",
202 this->c0_, this->c1_, this->c00_, this->c10_, this->c20_, this->c30_, this->c40_, this->c01_, this->c11_,
203 this->c21_, this->c31_);
204 return true;
205}
206
208 // Setup steps for SPA06:
209 // 1. Perform a protocol reset (required to write command for SPI code, noop for I2C)
210 this->protocol_reset();
211
212 // 2. Perform the actual reset
213 this->reset_.bit.fifo_flush = true;
214 this->reset_.bit.soft_rst = SPA06_SOFT_RESET;
215 if (!this->spa_write_byte(SPA06_RESET, this->reset_.reg)) {
216 return false;
217 }
218
219 // 3. Wait for chip to become ready. Datasheet specifies 2 ms; wait 3
220 delay(3);
221 // 4. Perform another protocol reset (required for SPI code, noop for I2C)
222 this->protocol_reset();
223 return true;
224}
225
226// Temperature conversion formula. See datasheet pg. 14
227float SPA06Component::convert_temperature_(const float &t_raw_sc) { return this->c0_ * 0.5 + this->c1_ * t_raw_sc; }
228// Pressure conversion formula. See datasheet pg. 14
229float SPA06Component::convert_pressure_(const float &p_raw_sc, const float &t_raw_sc) {
230 float p2_raw_sc = p_raw_sc * p_raw_sc;
231 float p3_raw_sc = p2_raw_sc * p_raw_sc;
232 float p4_raw_sc = p3_raw_sc * p_raw_sc;
233 return this->c00_ + (float) this->c10_ * p_raw_sc + (float) this->c20_ * p2_raw_sc + (float) this->c30_ * p3_raw_sc +
234 (float) this->c40_ * p4_raw_sc +
235 t_raw_sc * ((float) this->c01_ + (float) this->c11_ * p_raw_sc + (float) this->c21_ * p2_raw_sc +
236 (float) this->c31_ * p3_raw_sc);
237}
238
240 // Verify either a temperature or pressure sensor is defined before proceeding
241 if ((!this->temperature_sensor_) && (!this->pressure_sensor_)) {
242 return;
243 }
244
245 // Queue a background task for retrieving the measurement
246 this->set_timeout("measurement", this->conversion_time_, [this]() {
247 float raw_temperature;
248 float temperature = 0.0;
249 float pressure = 0.0;
250
251 // Check measurement register for readiness
252 if (!this->spa_read_byte(SPA06_MEAS_CFG, &this->meas_.reg)) {
253 ESP_LOGW(TAG, "Cannot read meas config");
254 this->status_set_warning();
255 return;
256 }
257 if (this->pressure_sensor_) {
258 if (!this->meas_.bit.prs_ready || !this->meas_.bit.tmp_ready) {
259 ESP_LOGW(TAG, "Temperature and pressure not ready");
260 this->status_set_warning();
261 return;
262 }
263 if (!this->read_temperature_and_pressure_(temperature, pressure, raw_temperature)) {
264 ESP_LOGW(TAG, "Temperature and pressure read failure");
265 this->status_set_warning();
266 return;
267 }
268 } else {
269 if (!this->meas_.bit.tmp_ready) {
270 ESP_LOGW(TAG, "Temperature not ready");
271 this->status_set_warning();
272 return;
273 }
274 if (!this->read_temperature_(temperature, raw_temperature)) {
275 ESP_LOGW(TAG, "Temperature read fail");
276 this->status_set_warning();
277 return;
278 }
279 }
280 if (this->temperature_sensor_) {
281 this->temperature_sensor_->publish_state(temperature);
282 } else {
283 ESP_LOGV(TAG, "No temperature sensor configured");
284 }
285 if (this->pressure_sensor_) {
286 this->pressure_sensor_->publish_state(pressure);
287 } else {
288 ESP_LOGV(TAG, "No pressure sensor configured");
289 }
290 this->status_clear_warning();
291 });
292}
293
295 // Temperature read and decode
296 if (!this->read_temperature_(temperature, t_raw_sc)) {
297 return false;
298 }
299 // Read raw pressure from device
300 uint8_t buf[3];
301 if (!this->spa_read_bytes(SPA06_PSR, buf, 3)) {
302 return false;
303 }
304 // Calculate raw scaled pressure value
305 float p_raw_sc = (float) decode32(buf[0], buf[1], buf[2], 0, 24) / (float) this->kp_;
306
307 // Calculate full pressure values
308 pressure = this->convert_pressure_(p_raw_sc, t_raw_sc);
309 return true;
310}
311
312bool SPA06Component::read_temperature_(float &temperature, float &t_raw_sc) {
313 uint8_t buf[3];
314 if (!this->spa_read_bytes(SPA06_TMP, buf, 3)) {
315 return false;
316 }
317
318 t_raw_sc = (float) decode32(buf[0], buf[1], buf[2], 0, 24) / (float) this->kt_;
319 temperature = this->convert_temperature_(t_raw_sc);
320 return true;
321}
322} // namespace esphome::spa06_base
void mark_failed()
Mark this component as failed.
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.
Definition component.h:497
void status_clear_warning()
Definition component.h:293
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:68
union esphome::spa06_base::SPA06Component::@159 reset_
union esphome::spa06_base::SPA06Component::@155 meas_
union esphome::spa06_base::SPA06Component::@160 prod_id_
union esphome::spa06_base::SPA06Component::@157 cfg_
virtual bool spa_read_bytes(uint8_t reg, uint8_t *data, size_t len)=0
union esphome::spa06_base::SPA06Component::@154 pt_meas_cfg_
bool write_measurement_settings_(MeasCrtl crtl)
float convert_temperature_(const float &t_raw_sc)
bool read_temperature_(float &temperature, float &t_raw_sc)
virtual bool spa_write_byte(uint8_t reg, uint8_t data)=0
bool read_temperature_and_pressure_(float &temperature, float &pressure, float &t_raw_sc)
bool write_temperature_settings_(Oversampling oversampling, SampleRate rate)
bool write_pressure_settings_(Oversampling oversampling, SampleRate rate)
virtual bool spa_read_byte(uint8_t reg, uint8_t *data)=0
bool write_communication_settings_(bool pressure_shift, bool temperature_shift, bool interrupt_hl=false, bool interrupt_fifo=false, bool interrupt_tmp=false, bool interrupt_prs=false, bool enable_fifo=false, bool spi_3wire=false)
bool write_sensor_settings_(Oversampling oversampling, SampleRate rate, uint8_t reg)
float convert_pressure_(const float &p_raw_sc, const float &t_raw_sc)
constexpr size_t SPA06_COEF_LEN
Definition spa06_base.h:15
int32_t decode32(uint8_t xmsb, uint8_t msb, uint8_t lsb, uint8_t xlsb, size_t bits, size_t head=0)
uint32_t oversampling_to_scale_factor(const Oversampling oversampling)
Definition spa06_base.h:96
constexpr uint8_t SPA06_SOFT_RESET
Definition spa06_base.h:18
int16_t decode16(uint8_t msb, uint8_t lsb, size_t bits, size_t head=0)
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:889
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:881
void HOT delay(uint32_t ms)
Definition core.cpp:28
uint16_t temperature
Definition sun_gtil2.cpp:12
uint8_t pressure
Definition tt21100.cpp:7