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