ESPHome 2025.5.0
Loading...
Searching...
No Matches
bmp3xx_base.cpp
Go to the documentation of this file.
1/*
2 based on BMP388_DEV by Martin Lindupp
3 under MIT License (MIT)
4 Copyright (C) Martin Lindupp 2020
5 http://github.com/MartinL1/BMP388_DEV
6*/
7
8#include "bmp3xx_base.h"
9#include "esphome/core/log.h"
10#include "esphome/core/hal.h"
11#include <cinttypes>
12
13namespace esphome {
14namespace bmp3xx_base {
15
16static const char *const TAG = "bmp3xx.sensor";
17
18static const LogString *chip_type_to_str(uint8_t chip_type) {
19 switch (chip_type) {
20 case BMP388_ID:
21 return LOG_STR("BMP 388");
22 case BMP390_ID:
23 return LOG_STR("BMP 390");
24 default:
25 return LOG_STR("Unknown Chip Type");
26 }
27}
28
29static const LogString *oversampling_to_str(Oversampling oversampling) {
30 switch (oversampling) {
32 return LOG_STR("None");
34 return LOG_STR("2x");
36 return LOG_STR("4x");
38 return LOG_STR("8x");
40 return LOG_STR("16x");
42 return LOG_STR("32x");
43 default:
44 return LOG_STR("");
45 }
46}
47
48static const LogString *iir_filter_to_str(IIRFilter filter) {
49 switch (filter) {
51 return LOG_STR("OFF");
53 return LOG_STR("2x");
55 return LOG_STR("4x");
57 return LOG_STR("8x");
59 return LOG_STR("16x");
61 return LOG_STR("32x");
63 return LOG_STR("64x");
65 return LOG_STR("128x");
66 default:
67 return LOG_STR("");
68 }
69}
70
72 this->error_code_ = NONE;
73 ESP_LOGCONFIG(TAG, "Setting up BMP3XX...");
74 // Call the Device base class "initialise" function
75 if (!reset()) {
76 ESP_LOGE(TAG, "Failed to reset BMP3XX...");
77 this->error_code_ = ERROR_SENSOR_RESET;
78 this->mark_failed();
79 }
80
81 if (!read_byte(BMP388_CHIP_ID, &this->chip_id_.reg)) {
82 ESP_LOGE(TAG, "Can't read chip id");
83 this->error_code_ = ERROR_COMMUNICATION_FAILED;
84 this->mark_failed();
85 return;
86 }
87 ESP_LOGCONFIG(TAG, "Chip %s Id 0x%X", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
88
89 if (chip_id_.reg != BMP388_ID && chip_id_.reg != BMP390_ID) {
90 ESP_LOGE(TAG, "Unknown chip id - is this really a BMP388 or BMP390?");
91 this->error_code_ = ERROR_WRONG_CHIP_ID;
92 this->mark_failed();
93 return;
94 }
95 // set sensor in sleep mode
97 // Read the calibration parameters into the params structure
98 if (!read_bytes(BMP388_TRIM_PARAMS, (uint8_t *) &compensation_params_, sizeof(compensation_params_))) {
99 ESP_LOGE(TAG, "Can't read calibration data");
100 this->error_code_ = ERROR_COMMUNICATION_FAILED;
101 this->mark_failed();
102 return;
103 }
105 (float) compensation_params_.param_T1 / powf(2.0f, -8.0f); // Calculate the floating point trim parameters
106 compensation_float_params_.param_T2 = (float) compensation_params_.param_T2 / powf(2.0f, 30.0f);
107 compensation_float_params_.param_T3 = (float) compensation_params_.param_T3 / powf(2.0f, 48.0f);
108 compensation_float_params_.param_P1 = ((float) compensation_params_.param_P1 - powf(2.0f, 14.0f)) / powf(2.0f, 20.0f);
109 compensation_float_params_.param_P2 = ((float) compensation_params_.param_P2 - powf(2.0f, 14.0f)) / powf(2.0f, 29.0f);
110 compensation_float_params_.param_P3 = (float) compensation_params_.param_P3 / powf(2.0f, 32.0f);
111 compensation_float_params_.param_P4 = (float) compensation_params_.param_P4 / powf(2.0f, 37.0f);
112 compensation_float_params_.param_P5 = (float) compensation_params_.param_P5 / powf(2.0f, -3.0f);
113 compensation_float_params_.param_P6 = (float) compensation_params_.param_P6 / powf(2.0f, 6.0f);
114 compensation_float_params_.param_P7 = (float) compensation_params_.param_P7 / powf(2.0f, 8.0f);
115 compensation_float_params_.param_P8 = (float) compensation_params_.param_P8 / powf(2.0f, 15.0f);
116 compensation_float_params_.param_P9 = (float) compensation_params_.param_P9 / powf(2.0f, 48.0f);
117 compensation_float_params_.param_P10 = (float) compensation_params_.param_P10 / powf(2.0f, 48.0f);
118 compensation_float_params_.param_P11 = (float) compensation_params_.param_P11 / powf(2.0f, 65.0f);
119
120 // Initialise the BMP388 IIR filter register
121 if (!set_iir_filter(this->iir_filter_)) {
122 ESP_LOGE(TAG, "Failed to set IIR filter");
123 this->error_code_ = ERROR_COMMUNICATION_FAILED;
124 this->mark_failed();
125 return;
126 }
127
128 // Set power control registers
129 pwr_ctrl_.bit.press_en = 1;
130 pwr_ctrl_.bit.temp_en = 1;
131 // Disable pressure if no sensor defined
132 // keep temperature enabled since it's needed for compensation
133 if (this->pressure_sensor_ == nullptr) {
134 pwr_ctrl_.bit.press_en = 0;
136 }
137 // just disable oeversampling for temp if not used
138 if (this->temperature_sensor_ == nullptr) {
140 }
141 // Initialise the BMP388 oversampling register
143 ESP_LOGE(TAG, "Failed to set oversampling register");
144 this->error_code_ = ERROR_COMMUNICATION_FAILED;
145 this->mark_failed();
146 return;
147 }
148}
149
151 ESP_LOGCONFIG(TAG, "BMP3XX:");
152 ESP_LOGCONFIG(TAG, " Type: %s (0x%X)", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
153 switch (this->error_code_) {
154 case NONE:
155 break;
157 ESP_LOGE(TAG, "Communication with BMP3XX failed!");
158 break;
160 ESP_LOGE(
161 TAG,
162 "BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
163 this->chip_id_.reg);
164 break;
166 ESP_LOGE(TAG, "BMP3XX failed to reset");
167 break;
168 default:
169 ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_);
170 break;
171 }
172 ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
173 LOG_UPDATE_INTERVAL(this);
174 if (this->temperature_sensor_) {
175 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
176 ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
177 }
178 if (this->pressure_sensor_) {
179 LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
180 ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
181 }
182}
184
185inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
186
188 // Enable sensor
189 ESP_LOGV(TAG, "Sending conversion request...");
190 float meas_time = 1.0f;
191 // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
192 meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
193 meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f;
194 meas_time += 0.234f;
195 if (!set_mode(FORCED_MODE)) {
196 ESP_LOGE(TAG, "Failed start forced mode");
197 this->mark_failed();
198 return;
199 }
200
201 const uint32_t meas_timeout = uint32_t(ceilf(meas_time));
202 ESP_LOGVV(TAG, "measurement time %" PRIu32, meas_timeout);
203 this->set_timeout("data", meas_timeout, [this]() {
204 float temperature = 0.0f;
205 float pressure = 0.0f;
206 if (this->pressure_sensor_ != nullptr) {
207 if (!get_measurements(temperature, pressure)) {
208 ESP_LOGW(TAG, "Failed to read pressure and temperature - skipping update");
209 this->status_set_warning();
210 return;
211 }
212 ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa", temperature, pressure);
213 } else {
215 ESP_LOGW(TAG, "Failed to read temperature - skipping update");
216 this->status_set_warning();
217 return;
218 }
219 ESP_LOGD(TAG, "Got temperature=%.1f°C", temperature);
220 }
221
222 if (this->temperature_sensor_ != nullptr)
223 this->temperature_sensor_->publish_state(temperature);
224 if (this->pressure_sensor_ != nullptr)
225 this->pressure_sensor_->publish_state(pressure);
226 this->status_clear_warning();
228 });
229}
230
231// Reset the BMP3XX
233 write_byte(BMP388_CMD, RESET_CODE); // Write the reset code to the command register
234 // Wait for 10ms
235 delay(10);
236 this->read_byte(BMP388_EVENT, &event_.reg); // Read the BMP388's event register
237 return event_.bit.por_detected; // Return if device reset is complete
238}
239
240// Start a one shot measurement in FORCED_MODE
242 // Only set FORCED_MODE if we're already in SLEEP_MODE
243 if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
244 return set_mode(FORCED_MODE);
245 }
246 return true;
247}
248
249// Stop the conversion and return to SLEEP_MODE
251
252// Set the pressure oversampling rate
254 osr_.bit.osr_p = oversampling;
255 return this->write_byte(BMP388_OSR, osr_.reg);
256}
257
258// Set the temperature oversampling rate
260 osr_.bit.osr_t = oversampling;
261 return this->write_byte(BMP388_OSR, osr_.reg);
262}
263
264// Set the IIR filter setting
266 config_.bit.iir_filter = iir_filter;
267 return this->write_byte(BMP388_CONFIG, config_.reg);
268}
269
270// Get temperature
272 // Check if a measurement is ready
273 if (!data_ready()) {
274 return false;
275 }
276 uint8_t data[3];
277 // Read the temperature
278 if (!this->read_bytes(BMP388_DATA_3, &data[0], 3)) {
279 ESP_LOGE(TAG, "Failed to read temperature");
280 return false;
281 }
282 // Copy the temperature data into the adc variables
283 int32_t adc_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
284 // Temperature compensation (function from BMP388 datasheet)
286 return true;
287}
288
289// Get the pressure
294
295// Get temperature and pressure
297 // Check if a measurement is ready
298 if (!data_ready()) {
299 ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update");
300 return false;
301 }
302
303 uint8_t data[6];
304 // Read the temperature and pressure data
305 if (!this->read_bytes(BMP388_DATA_0, &data[0], 6)) {
306 ESP_LOGE(TAG, "Failed to read measurements");
307 return false;
308 }
309 // Copy the temperature and pressure data into the adc variables
310 int32_t adc_pres = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
311 int32_t adc_temp = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
312
313 // Temperature compensation (function from BMP388 datasheet)
315 // Pressure compensation (function from BMP388 datasheet)
317 // Calculate the pressure in millibar/hPa
318 pressure /= 100.0f;
319 return true;
320}
321
322// Set the BMP388's mode in the power control register
327
328// Set the BMP388 oversampling register
330 Oversampling temperature_oversampling) {
331 osr_.reg = temperature_oversampling << 3 | pressure_oversampling;
332 return this->write_byte(BMP388_OSR, osr_.reg);
333}
334
335// Check if measurement data is ready
337 // If we're in SLEEP_MODE return immediately
338 if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
339 ESP_LOGD(TAG, "Not ready - sensor is in sleep mode");
340 return false;
341 }
342 // Read the interrupt status register
343 uint8_t status;
344 if (!this->read_byte(BMP388_INT_STATUS, &status)) {
345 ESP_LOGE(TAG, "Failed to read status register");
346 return false;
347 }
348 int_status_.reg = status;
349 ESP_LOGVV(TAG, "data ready status %d", status);
350 // If we're in FORCED_MODE switch back to SLEEP_MODE
351 if (int_status_.bit.drdy) {
352 if (pwr_ctrl_.bit.mode == FORCED_MODE) {
353 pwr_ctrl_.bit.mode = SLEEP_MODE;
354 }
355 return true; // The measurement is ready
356 }
357 return false; // The measurement is still pending
358}
359
361// Bosch BMP3XXComponent (Private) Member Functions
363
365 float partial_data1 = uncomp_temp - compensation_float_params_.param_T1;
366 float partial_data2 = partial_data1 * compensation_float_params_.param_T2;
367 return partial_data2 + partial_data1 * partial_data1 * compensation_float_params_.param_T3;
368}
369
370float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_lin) {
371 float partial_data1 = compensation_float_params_.param_P6 * t_lin;
372 float partial_data2 = compensation_float_params_.param_P7 * t_lin * t_lin;
373 float partial_data3 = compensation_float_params_.param_P8 * t_lin * t_lin * t_lin;
374 float partial_out1 = compensation_float_params_.param_P5 + partial_data1 + partial_data2 + partial_data3;
375 partial_data1 = compensation_float_params_.param_P2 * t_lin;
376 partial_data2 = compensation_float_params_.param_P3 * t_lin * t_lin;
377 partial_data3 = compensation_float_params_.param_P4 * t_lin * t_lin * t_lin;
378 float partial_out2 =
379 uncomp_press * (compensation_float_params_.param_P1 + partial_data1 + partial_data2 + partial_data3);
380 partial_data1 = uncomp_press * uncomp_press;
382 partial_data3 = partial_data1 * partial_data2;
383 float partial_data4 =
384 partial_data3 + uncomp_press * uncomp_press * uncomp_press * compensation_float_params_.param_P11;
385 return partial_out1 + partial_out2 + partial_data4;
386}
387
388} // namespace bmp3xx_base
389} // namespace esphome
BedjetMode mode
BedJet operating mode.
uint8_t status
Definition bl0942.h:8
virtual void mark_failed()
Mark this component as failed.
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.cpp:72
union esphome::bmp3xx_base::BMP3XXComponent::@26 event_
struct esphome::bmp3xx_base::BMP3XXComponent::FloatParams compensation_float_params_
bool get_pressure(float &pressure)
Get a pressure measurement.
bool get_measurements(float &temperature, float &pressure)
Get a temperature and pressure measurement.
enum esphome::bmp3xx_base::BMP3XXComponent::ErrorCode NONE
bool start_forced_conversion()
Start a one shot measurement in FORCED_MODE.
union esphome::bmp3xx_base::BMP3XXComponent::@25 chip_id_
union esphome::bmp3xx_base::BMP3XXComponent::@31 config_
bool set_iir_filter(IIRFilter iir_filter)
Set the IIR filter setting: OFF, 2, 3, 8, 16, 32.
bool set_pressure_oversampling(Oversampling pressure_oversampling)
Set the pressure oversampling: OFF, X1, X2, X4, X8, X16, X32.
union esphome::bmp3xx_base::BMP3XXComponent::@28 pwr_ctrl_
bool get_temperature(float &temperature)
Get a temperature measurement.
bool stop_conversion()
Stop the conversion and return to SLEEP_MODE.
float bmp388_compensate_temperature_(float uncomp_temp)
float get_setup_priority() const override
virtual bool write_byte(uint8_t a_register, uint8_t data)=0
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len)=0
uint8_t reset()
Soft reset the sensor.
union esphome::bmp3xx_base::BMP3XXComponent::@27 int_status_
bool set_oversampling_register(Oversampling pressure_oversampling, Oversampling temperature_oversampling)
Set the BMP388 oversampling register.
bool data_ready()
Checks if a measurement is ready.
virtual bool read_byte(uint8_t a_register, uint8_t *data)=0
union esphome::bmp3xx_base::BMP3XXComponent::@29 osr_
bool set_mode(OperationMode mode)
Set the barometer mode.
float bmp388_compensate_pressure_(float uncomp_press, float t_lin)
bool set_temperature_oversampling(Oversampling temperature_oversampling)
Set the temperature oversampling: OFF, X1, X2, X4, X8, X16, X32.
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
uint8_t oversampling_to_time(Oversampling over_sampling)
Oversampling
Oversampling bit fields in the control and measurement register.
Definition bmp3xx_base.h:50
OperationMode
Device mode bitfield in the control and measurement register.
Definition bmp3xx_base.h:47
IIRFilter
Infinite Impulse Response (IIR) filter bit field in the configuration register.
Definition bmp3xx_base.h:60
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
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
uint16_t temperature
Definition sun_gtil2.cpp:12
uint8_t pressure
Definition tt21100.cpp:7