ESPHome  2024.4.0
bmp3xx.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.h"
9 #include "esphome/core/log.h"
10 #include "esphome/core/hal.h"
11 #include <cinttypes>
12 
13 namespace esphome {
14 namespace bmp3xx {
15 
16 static const char *const TAG = "bmp3xx.sensor";
17 
18 static 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 
29 static 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 
48 static 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  LOG_I2C_DEVICE(this);
154  switch (this->error_code_) {
155  case NONE:
156  break;
158  ESP_LOGE(TAG, "Communication with BMP3XX failed!");
159  break;
160  case ERROR_WRONG_CHIP_ID:
161  ESP_LOGE(
162  TAG,
163  "BMP3XX has wrong chip ID (reported id: 0x%X) - please check if you are really using a BMP 388 or BMP 390",
164  this->chip_id_.reg);
165  break;
166  case ERROR_SENSOR_RESET:
167  ESP_LOGE(TAG, "BMP3XX failed to reset");
168  break;
169  default:
170  ESP_LOGE(TAG, "BMP3XX error code %d", (int) this->error_code_);
171  break;
172  }
173  ESP_LOGCONFIG(TAG, " IIR Filter: %s", LOG_STR_ARG(iir_filter_to_str(this->iir_filter_)));
174  LOG_UPDATE_INTERVAL(this);
175  if (this->temperature_sensor_) {
176  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
177  ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->temperature_oversampling_)));
178  }
179  if (this->pressure_sensor_) {
180  LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
181  ESP_LOGCONFIG(TAG, " Oversampling: %s", LOG_STR_ARG(oversampling_to_str(this->pressure_oversampling_)));
182  }
183 }
185 
186 inline uint8_t oversampling_to_time(Oversampling over_sampling) { return (1 << uint8_t(over_sampling)); }
187 
189  // Enable sensor
190  ESP_LOGV(TAG, "Sending conversion request...");
191  float meas_time = 1.0f;
192  // Ref: https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf 3.9.2
193  meas_time += 2.02f * oversampling_to_time(this->temperature_oversampling_) + 0.163f;
194  meas_time += 2.02f * oversampling_to_time(this->pressure_oversampling_) + 0.392f;
195  meas_time += 0.234f;
196  if (!set_mode(FORCED_MODE)) {
197  ESP_LOGE(TAG, "Failed start forced mode");
198  this->mark_failed();
199  return;
200  }
201 
202  const uint32_t meas_timeout = uint32_t(ceilf(meas_time));
203  ESP_LOGVV(TAG, "measurement time %" PRIu32, meas_timeout);
204  this->set_timeout("data", meas_timeout, [this]() {
205  float temperature = 0.0f;
206  float pressure = 0.0f;
207  if (this->pressure_sensor_ != nullptr) {
208  if (!get_measurements(temperature, pressure)) {
209  ESP_LOGW(TAG, "Failed to read pressure and temperature - skipping update");
210  this->status_set_warning();
211  return;
212  }
213  ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa", temperature, pressure);
214  } else {
215  if (!get_temperature(temperature)) {
216  ESP_LOGW(TAG, "Failed to read temperature - skipping update");
217  this->status_set_warning();
218  return;
219  }
220  ESP_LOGD(TAG, "Got temperature=%.1f°C", temperature);
221  }
222 
223  if (this->temperature_sensor_ != nullptr)
224  this->temperature_sensor_->publish_state(temperature);
225  if (this->pressure_sensor_ != nullptr)
226  this->pressure_sensor_->publish_state(pressure);
227  this->status_clear_warning();
229  });
230 }
231 
232 // Reset the BMP3XX
234  write_byte(BMP388_CMD, RESET_CODE); // Write the reset code to the command register
235  // Wait for 10ms
236  delay(10);
237  this->read_byte(BMP388_EVENT, &event_.reg); // Read the BMP388's event register
238  return event_.bit.por_detected; // Return if device reset is complete
239 }
240 
241 // Start a one shot measurement in FORCED_MODE
243  // Only set FORCED_MODE if we're already in SLEEP_MODE
244  if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
245  return set_mode(FORCED_MODE);
246  }
247  return true;
248 }
249 
250 // Stop the conversion and return to SLEEP_MODE
252 
253 // Set the pressure oversampling rate
255  osr_.bit.osr_p = oversampling;
256  return this->write_byte(BMP388_OSR, osr_.reg);
257 }
258 
259 // Set the temperature oversampling rate
261  osr_.bit.osr_t = oversampling;
262  return this->write_byte(BMP388_OSR, osr_.reg);
263 }
264 
265 // Set the IIR filter setting
267  config_.bit.iir_filter = iir_filter;
268  return this->write_byte(BMP388_CONFIG, config_.reg);
269 }
270 
271 // Get temperature
273  // Check if a measurement is ready
274  if (!data_ready()) {
275  return false;
276  }
277  uint8_t data[3];
278  // Read the temperature
279  if (!this->read_bytes(BMP388_DATA_3, &data[0], 3)) {
280  ESP_LOGE(TAG, "Failed to read temperature");
281  return false;
282  }
283  // Copy the temperature data into the adc variables
284  int32_t adc_temp = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
285  // Temperature compensation (function from BMP388 datasheet)
286  temperature = bmp388_compensate_temperature_((float) adc_temp);
287  return true;
288 }
289 
290 // Get the pressure
292  float temperature;
293  return get_measurements(temperature, pressure);
294 }
295 
296 // Get temperature and pressure
298  // Check if a measurement is ready
299  if (!data_ready()) {
300  ESP_LOGD(TAG, "BMP3XX Get measurement - data not ready skipping update");
301  return false;
302  }
303 
304  uint8_t data[6];
305  // Read the temperature and pressure data
306  if (!this->read_bytes(BMP388_DATA_0, &data[0], 6)) {
307  ESP_LOGE(TAG, "Failed to read measurements");
308  return false;
309  }
310  // Copy the temperature and pressure data into the adc variables
311  int32_t adc_pres = (int32_t) data[2] << 16 | (int32_t) data[1] << 8 | (int32_t) data[0];
312  int32_t adc_temp = (int32_t) data[5] << 16 | (int32_t) data[4] << 8 | (int32_t) data[3];
313 
314  // Temperature compensation (function from BMP388 datasheet)
315  temperature = bmp388_compensate_temperature_((float) adc_temp);
316  // Pressure compensation (function from BMP388 datasheet)
317  pressure = bmp388_compensate_pressure_((float) adc_pres, temperature);
318  // Calculate the pressure in millibar/hPa
319  pressure /= 100.0f;
320  return true;
321 }
322 
323 // Set the BMP388's mode in the power control register
325  pwr_ctrl_.bit.mode = mode;
326  return this->write_byte(BMP388_PWR_CTRL, pwr_ctrl_.reg);
327 }
328 
329 // Set the BMP388 oversampling register
331  Oversampling temperature_oversampling) {
332  osr_.reg = temperature_oversampling << 3 | pressure_oversampling;
333  return this->write_byte(BMP388_OSR, osr_.reg);
334 }
335 
336 // Check if measurement data is ready
338  // If we're in SLEEP_MODE return immediately
339  if (pwr_ctrl_.bit.mode == SLEEP_MODE) {
340  ESP_LOGD(TAG, "Not ready - sensor is in sleep mode");
341  return false;
342  }
343  // Read the interrupt status register
344  uint8_t status;
345  if (!this->read_byte(BMP388_INT_STATUS, &status)) {
346  ESP_LOGE(TAG, "Failed to read status register");
347  return false;
348  }
349  int_status_.reg = status;
350  ESP_LOGVV(TAG, "data ready status %d", status);
351  // If we're in FORCED_MODE switch back to SLEEP_MODE
352  if (int_status_.bit.drdy) {
353  if (pwr_ctrl_.bit.mode == FORCED_MODE) {
354  pwr_ctrl_.bit.mode = SLEEP_MODE;
355  }
356  return true; // The measurement is ready
357  }
358  return false; // The measurement is still pending
359 }
360 
362 // Bosch BMP3XXComponent (Private) Member Functions
364 
366  float partial_data1 = uncomp_temp - compensation_float_params_.param_T1;
367  float partial_data2 = partial_data1 * compensation_float_params_.param_T2;
368  return partial_data2 + partial_data1 * partial_data1 * compensation_float_params_.param_T3;
369 }
370 
371 float BMP3XXComponent::bmp388_compensate_pressure_(float uncomp_press, float t_lin) {
372  float partial_data1 = compensation_float_params_.param_P6 * t_lin;
373  float partial_data2 = compensation_float_params_.param_P7 * t_lin * t_lin;
374  float partial_data3 = compensation_float_params_.param_P8 * t_lin * t_lin * t_lin;
375  float partial_out1 = compensation_float_params_.param_P5 + partial_data1 + partial_data2 + partial_data3;
376  partial_data1 = compensation_float_params_.param_P2 * t_lin;
377  partial_data2 = compensation_float_params_.param_P3 * t_lin * t_lin;
378  partial_data3 = compensation_float_params_.param_P4 * t_lin * t_lin * t_lin;
379  float partial_out2 =
380  uncomp_press * (compensation_float_params_.param_P1 + partial_data1 + partial_data2 + partial_data3);
381  partial_data1 = uncomp_press * uncomp_press;
383  partial_data3 = partial_data1 * partial_data2;
384  float partial_data4 =
385  partial_data3 + uncomp_press * uncomp_press * uncomp_press * compensation_float_params_.param_P11;
386  return partial_out1 + partial_out2 + partial_data4;
387 }
388 
389 } // namespace bmp3xx
390 } // namespace esphome
union esphome::bmp3xx::BMP3XXComponent::@27 pwr_ctrl_
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:235
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
bool set_iir_filter(IIRFilter iir_filter)
Set the IIR filter setting: OFF, 2, 3, 8, 16, 32.
Definition: bmp3xx.cpp:266
OperationMode
Device mode bitfield in the control and measurement register.
Definition: bmp3xx.h:48
Oversampling temperature_oversampling_
Definition: bmp3xx.h:124
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
float bmp388_compensate_temperature_(float uncomp_temp)
Definition: bmp3xx.cpp:365
uint8_t pressure
Definition: tt21100.cpp:19
bool set_mode(OperationMode mode)
Set the barometer mode.
Definition: bmp3xx.cpp:324
union esphome::bmp3xx::BMP3XXComponent::@26 int_status_
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:69
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition: i2c.h:212
enum esphome::bmp3xx::BMP3XXComponent::ErrorCode NONE
union esphome::bmp3xx::BMP3XXComponent::@24 chip_id_
bool set_oversampling_register(Oversampling pressure_oversampling, Oversampling temperature_oversampling)
Set the BMP388 oversampling register.
Definition: bmp3xx.cpp:330
union esphome::bmp3xx::BMP3XXComponent::@30 config_
uint8_t reset()
Soft reset the sensor.
Definition: bmp3xx.cpp:233
float get_setup_priority() const override
Definition: bmp3xx.cpp:184
bool get_measurements(float &temperature, float &pressure)
Get a temperature and pressure measurement.
Definition: bmp3xx.cpp:297
bool get_temperature(float &temperature)
Get a temperature measurement.
Definition: bmp3xx.cpp:272
bool get_pressure(float &pressure)
Get a pressure measurement.
Definition: bmp3xx.cpp:291
uint8_t oversampling_to_time(Oversampling over_sampling)
Definition: bmp3xx.cpp:186
void status_clear_warning()
Definition: component.cpp:166
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
sensor::Sensor * pressure_sensor_
Definition: bmp3xx.h:129
float bmp388_compensate_pressure_(float uncomp_press, float t_lin)
Definition: bmp3xx.cpp:371
bool data_ready()
Checks if a measurement is ready.
Definition: bmp3xx.cpp:337
bool set_pressure_oversampling(Oversampling pressure_oversampling)
Set the pressure oversampling: OFF, X1, X2, X4, X8, X16, X32.
Definition: bmp3xx.cpp:254
bool set_temperature_oversampling(Oversampling temperature_oversampling)
Set the temperature oversampling: OFF, X1, X2, X4, X8, X16, X32.
Definition: bmp3xx.cpp:260
uint8_t status
Definition: bl0942.h:23
sensor::Sensor * temperature_sensor_
Definition: bmp3xx.h:128
union esphome::bmp3xx::BMP3XXComponent::@28 osr_
bool stop_conversion()
Stop the conversion and return to SLEEP_MODE.
Definition: bmp3xx.cpp:251
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:262
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
bool start_forced_conversion()
Start a one shot measurement in FORCED_MODE.
Definition: bmp3xx.cpp:242
struct esphome::bmp3xx::BMP3XXComponent::FloatParams compensation_float_params_
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
union esphome::bmp3xx::BMP3XXComponent::@25 event_
Oversampling pressure_oversampling_
Definition: bmp3xx.h:125
Oversampling
Oversampling bit fields in the control and measurement register.
Definition: bmp3xx.h:51
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26
IIRFilter
Infinite Impulse Response (IIR) filter bit field in the configuration register.
Definition: bmp3xx.h:61