ESPHome 2026.2.1
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/hal.h"
10#include "esphome/core/log.h"
12#include <cinttypes>
13
14namespace esphome {
15namespace bmp3xx_base {
16
17static const char *const TAG = "bmp3xx.sensor";
18
19static const LogString *chip_type_to_str(uint8_t chip_type) {
20 switch (chip_type) {
21 case BMP388_ID:
22 return LOG_STR("BMP 388");
23 case BMP390_ID:
24 return LOG_STR("BMP 390");
25 default:
26 return LOG_STR("Unknown Chip Type");
27 }
28}
29
30// Oversampling strings indexed by Oversampling enum (0-5): NONE, X2, X4, X8, X16, X32
31PROGMEM_STRING_TABLE(OversamplingStrings, "None", "2x", "4x", "8x", "16x", "32x", "");
32
33static const LogString *oversampling_to_str(Oversampling oversampling) {
34 return OversamplingStrings::get_log_str(static_cast<uint8_t>(oversampling), OversamplingStrings::LAST_INDEX);
35}
36
37// IIR filter strings indexed by IIRFilter enum (0-7): OFF, 2, 4, 8, 16, 32, 64, 128
38PROGMEM_STRING_TABLE(IIRFilterStrings, "OFF", "2x", "4x", "8x", "16x", "32x", "64x", "128x", "");
39
40static const LogString *iir_filter_to_str(IIRFilter filter) {
41 return IIRFilterStrings::get_log_str(static_cast<uint8_t>(filter), IIRFilterStrings::LAST_INDEX);
42}
43
45 this->error_code_ = NONE;
46 // Call the Device base class "initialise" function
47 if (!reset()) {
48 ESP_LOGE(TAG, "Failed to reset");
49 this->error_code_ = ERROR_SENSOR_RESET;
50 this->mark_failed();
51 }
52
53 if (!read_byte(BMP388_CHIP_ID, &this->chip_id_.reg)) {
54 ESP_LOGE(TAG, "Can't read chip id");
55 this->error_code_ = ERROR_COMMUNICATION_FAILED;
56 this->mark_failed();
57 return;
58 }
59 ESP_LOGCONFIG(TAG, "Chip %s Id 0x%X", LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
60
61 if (chip_id_.reg != BMP388_ID && chip_id_.reg != BMP390_ID) {
62 ESP_LOGE(TAG, "Unknown chip id - is this really a BMP388 or BMP390?");
63 this->error_code_ = ERROR_WRONG_CHIP_ID;
64 this->mark_failed();
65 return;
66 }
67 // set sensor in sleep mode
69 // Read the calibration parameters into the params structure
70 if (!read_bytes(BMP388_TRIM_PARAMS, (uint8_t *) &compensation_params_, sizeof(compensation_params_))) {
71 ESP_LOGE(TAG, "Can't read calibration data");
72 this->error_code_ = ERROR_COMMUNICATION_FAILED;
73 this->mark_failed();
74 return;
75 }
77 (float) compensation_params_.param_T1 / powf(2.0f, -8.0f); // Calculate the floating point trim parameters
78 compensation_float_params_.param_T2 = (float) compensation_params_.param_T2 / powf(2.0f, 30.0f);
79 compensation_float_params_.param_T3 = (float) compensation_params_.param_T3 / powf(2.0f, 48.0f);
80 compensation_float_params_.param_P1 = ((float) compensation_params_.param_P1 - powf(2.0f, 14.0f)) / powf(2.0f, 20.0f);
81 compensation_float_params_.param_P2 = ((float) compensation_params_.param_P2 - powf(2.0f, 14.0f)) / powf(2.0f, 29.0f);
82 compensation_float_params_.param_P3 = (float) compensation_params_.param_P3 / powf(2.0f, 32.0f);
83 compensation_float_params_.param_P4 = (float) compensation_params_.param_P4 / powf(2.0f, 37.0f);
84 compensation_float_params_.param_P5 = (float) compensation_params_.param_P5 / powf(2.0f, -3.0f);
85 compensation_float_params_.param_P6 = (float) compensation_params_.param_P6 / powf(2.0f, 6.0f);
86 compensation_float_params_.param_P7 = (float) compensation_params_.param_P7 / powf(2.0f, 8.0f);
87 compensation_float_params_.param_P8 = (float) compensation_params_.param_P8 / powf(2.0f, 15.0f);
88 compensation_float_params_.param_P9 = (float) compensation_params_.param_P9 / powf(2.0f, 48.0f);
89 compensation_float_params_.param_P10 = (float) compensation_params_.param_P10 / powf(2.0f, 48.0f);
90 compensation_float_params_.param_P11 = (float) compensation_params_.param_P11 / powf(2.0f, 65.0f);
91
92 // Initialise the BMP388 IIR filter register
93 if (!set_iir_filter(this->iir_filter_)) {
94 ESP_LOGE(TAG, "Failed to set IIR filter");
95 this->error_code_ = ERROR_COMMUNICATION_FAILED;
96 this->mark_failed();
97 return;
98 }
99
100 // Set power control registers
101 pwr_ctrl_.bit.press_en = 1;
102 pwr_ctrl_.bit.temp_en = 1;
103 // Disable pressure if no sensor defined
104 // keep temperature enabled since it's needed for compensation
105 if (this->pressure_sensor_ == nullptr) {
106 pwr_ctrl_.bit.press_en = 0;
108 }
109 // just disable oeversampling for temp if not used
110 if (this->temperature_sensor_ == nullptr) {
112 }
113 // Initialise the BMP388 oversampling register
115 ESP_LOGE(TAG, "Failed to set oversampling register");
116 this->error_code_ = ERROR_COMMUNICATION_FAILED;
117 this->mark_failed();
118 return;
119 }
120}
121
123 ESP_LOGCONFIG(TAG,
124 "BMP3XX:\n"
125 " Type: %s (0x%X)",
126 LOG_STR_ARG(chip_type_to_str(this->chip_id_.reg)), this->chip_id_.reg);
127 switch (this->error_code_) {
128 case NONE:
129 break;
131 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
132 break;
134 ESP_LOGE(TAG, "Wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
135 this->chip_id_.reg);
136 break;
138 ESP_LOGE(TAG, "Failed to reset");
139 break;
140 default:
141 ESP_LOGE(TAG, "Error code %d", (int) this->error_code_);
142 break;
143 }
144 ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
145 LOG_UPDATE_INTERVAL(this);
146 if (this->temperature_sensor_) {
147 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
148 ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
149 }
150 if (this->pressure_sensor_) {
151 LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
152 ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
153 }
154}
155
156inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
157
159 // Enable sensor
160 ESP_LOGV(TAG, "Sending conversion request");
161 float meas_time = 1.0f;
162 // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
163 meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
164 meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f;
165 meas_time += 0.234f;
166 if (!set_mode(FORCED_MODE)) {
167 ESP_LOGE(TAG, "Failed start forced mode");
168 this->mark_failed();
169 return;
170 }
171
172 const uint32_t meas_timeout = uint32_t(ceilf(meas_time));
173 ESP_LOGVV(TAG, "measurement time %" PRIu32, meas_timeout);
174 this->set_timeout("data", meas_timeout, [this]() {
175 float temperature = 0.0f;
176 float pressure = 0.0f;
177 if (this->pressure_sensor_ != nullptr) {
178 if (!get_measurements(temperature, pressure)) {
179 ESP_LOGW(TAG, "Failed to read pressure and temperature - skipping update");
180 this->status_set_warning();
181 return;
182 }
183 ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa", temperature, pressure);
184 } else {
186 ESP_LOGW(TAG, "Failed to read temperature - skipping update");
187 this->status_set_warning();
188 return;
189 }
190 ESP_LOGD(TAG, "Got temperature=%.1f°C", temperature);
191 }
192
193 if (this->temperature_sensor_ != nullptr)
194 this->temperature_sensor_->publish_state(temperature);
195 if (this->pressure_sensor_ != nullptr)
196 this->pressure_sensor_->publish_state(pressure);
197 this->status_clear_warning();
199 });
200}
201
202// Reset the BMP3XX
204 write_byte(BMP388_CMD, RESET_CODE); // Write the reset code to the command register
205 // Wait for 10ms
206 delay(10);
207 this->read_byte(BMP388_EVENT, &event_.reg); // Read the BMP388's event register
208 return event_.bit.por_detected; // Return if device reset is complete
209}
210
211// Start a one shot measurement in FORCED_MODE
213 // Only set FORCED_MODE if we're already in SLEEP_MODE
214 if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
215 return set_mode(FORCED_MODE);
216 }
217 return true;
218}
219
220// Stop the conversion and return to SLEEP_MODE
222
223// Set the pressure oversampling rate
225 osr_.bit.osr_p = oversampling;
226 return this->write_byte(BMP388_OSR, osr_.reg);
227}
228
229// Set the temperature oversampling rate
231 osr_.bit.osr_t = oversampling;
232 return this->write_byte(BMP388_OSR, osr_.reg);
233}
234
235// Set the IIR filter setting
237 config_.bit.iir_filter = iir_filter;
238 return this->write_byte(BMP388_CONFIG, config_.reg);
239}
240
241// Get temperature
243 // Check if a measurement is ready
244 if (!data_ready()) {
245 return false;
246 }
247 uint8_t data[3];
248 // Read the temperature
249 if (!this->read_bytes(BMP388_DATA_3, &data[0], 3)) {
250 ESP_LOGE(TAG, "Failed to read temperature");
251 return false;
252 }
253 // Copy the temperature data into the adc variables
254 int32_t adc_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
255 // Temperature compensation (function from BMP388 datasheet)
257 return true;
258}
259
260// Get the pressure
265
266// Get temperature and pressure
268 // Check if a measurement is ready
269 if (!data_ready()) {
270 ESP_LOGD(TAG, "Get measurement - data not ready skipping update");
271 return false;
272 }
273
274 uint8_t data[6];
275 // Read the temperature and pressure data
276 if (!this->read_bytes(BMP388_DATA_0, &data[0], 6)) {
277 ESP_LOGE(TAG, "Failed to read measurements");
278 return false;
279 }
280 // Copy the temperature and pressure data into the adc variables
281 int32_t adc_pres = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
282 int32_t adc_temp = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
283
284 // Temperature compensation (function from BMP388 datasheet)
286 // Pressure compensation (function from BMP388 datasheet)
288 // Calculate the pressure in millibar/hPa
289 pressure /= 100.0f;
290 return true;
291}
292
293// Set the BMP388's mode in the power control register
298
299// Set the BMP388 oversampling register
301 Oversampling temperature_oversampling) {
302 osr_.reg = temperature_oversampling << 3 | pressure_oversampling;
303 return this->write_byte(BMP388_OSR, osr_.reg);
304}
305
306// Check if measurement data is ready
308 // If we're in SLEEP_MODE return immediately
309 if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
310 ESP_LOGD(TAG, "Not ready - sensor is in sleep mode");
311 return false;
312 }
313 // Read the interrupt status register
314 uint8_t status;
315 if (!this->read_byte(BMP388_INT_STATUS, &status)) {
316 ESP_LOGE(TAG, "Failed to read status register");
317 return false;
318 }
319 int_status_.reg = status;
320 ESP_LOGVV(TAG, "data ready status %d", status);
321 // If we're in FORCED_MODE switch back to SLEEP_MODE
322 if (int_status_.bit.drdy) {
323 if (pwr_ctrl_.bit.mode == FORCED_MODE) {
324 pwr_ctrl_.bit.mode = SLEEP_MODE;
325 }
326 return true; // The measurement is ready
327 }
328 return false; // The measurement is still pending
329}
330
332// Bosch BMP3XXComponent (Private) Member Functions
334
336 float partial_data1 = uncomp_temp - compensation_float_params_.param_T1;
337 float partial_data2 = partial_data1 * compensation_float_params_.param_T2;
338 return partial_data2 + partial_data1 * partial_data1 * compensation_float_params_.param_T3;
339}
340
341float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_lin) {
342 float partial_data1 = compensation_float_params_.param_P6 * t_lin;
343 float partial_data2 = compensation_float_params_.param_P7 * t_lin * t_lin;
344 float partial_data3 = compensation_float_params_.param_P8 * t_lin * t_lin * t_lin;
345 float partial_out1 = compensation_float_params_.param_P5 + partial_data1 + partial_data2 + partial_data3;
346 partial_data1 = compensation_float_params_.param_P2 * t_lin;
347 partial_data2 = compensation_float_params_.param_P3 * t_lin * t_lin;
348 partial_data3 = compensation_float_params_.param_P4 * t_lin * t_lin * t_lin;
349 float partial_out2 =
350 uncomp_press * (compensation_float_params_.param_P1 + partial_data1 + partial_data2 + partial_data3);
351 partial_data1 = uncomp_press * uncomp_press;
353 partial_data3 = partial_data1 * partial_data2;
354 float partial_data4 =
355 partial_data3 + uncomp_press * uncomp_press * uncomp_press * compensation_float_params_.param_P11;
356 return partial_out1 + partial_out2 + partial_data4;
357}
358
359} // namespace bmp3xx_base
360} // 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=nullptr)
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:429
void status_clear_warning()
union esphome::bmp3xx_base::BMP3XXComponent::@24 event_
union esphome::bmp3xx_base::BMP3XXComponent::@27 osr_
struct esphome::bmp3xx_base::BMP3XXComponent::FloatParams compensation_float_params_
bool get_pressure(float &pressure)
Get a pressure measurement.
union esphome::bmp3xx_base::BMP3XXComponent::@23 chip_id_
bool get_measurements(float &temperature, float &pressure)
Get a temperature and pressure measurement.
union esphome::bmp3xx_base::BMP3XXComponent::@26 pwr_ctrl_
enum esphome::bmp3xx_base::BMP3XXComponent::ErrorCode NONE
bool start_forced_conversion()
Start a one shot measurement in FORCED_MODE.
bool set_iir_filter(IIRFilter iir_filter)
Set the IIR filter setting: OFF, 2, 3, 8, 16, 32.
union esphome::bmp3xx_base::BMP3XXComponent::@29 config_
bool set_pressure_oversampling(Oversampling pressure_oversampling)
Set the pressure oversampling: OFF, X1, X2, X4, X8, X16, X32.
union esphome::bmp3xx_base::BMP3XXComponent::@25 int_status_
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)
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.
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
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:65
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
PROGMEM_STRING_TABLE(OversamplingStrings, "None", "2x", "4x", "8x", "16x", "32x", "")
IIRFilter
Infinite Impulse Response (IIR) filter bit field in the configuration register.
Definition bmp3xx_base.h:60
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:26
uint16_t temperature
Definition sun_gtil2.cpp:12
uint8_t pressure
Definition tt21100.cpp:7