ESPHome  2024.11.0
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 
13 namespace esphome {
14 namespace bmp3xx_base {
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  switch (this->error_code_) {
154  case NONE:
155  break;
157  ESP_LOGE(TAG, "Communication with BMP3XX failed!");
158  break;
159  case ERROR_WRONG_CHIP_ID:
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;
165  case ERROR_SENSOR_RESET:
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 
185 inline 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 {
214  if (!get_temperature(temperature)) {
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)
285  temperature = bmp388_compensate_temperature_((float) adc_temp);
286  return true;
287 }
288 
289 // Get the pressure
291  float temperature;
292  return get_measurements(temperature, pressure);
293 }
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)
314  temperature = bmp388_compensate_temperature_((float) adc_temp);
315  // Pressure compensation (function from BMP388 datasheet)
316  pressure = bmp388_compensate_pressure_((float) adc_pres, temperature);
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
324  pwr_ctrl_.bit.mode = mode;
325  return this->write_byte(BMP388_PWR_CTRL, pwr_ctrl_.reg);
326 }
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 
370 float 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
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
float get_setup_priority() const override
union esphome::bmp3xx_base::BMP3XXComponent::@31 config_
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
uint8_t pressure
Definition: tt21100.cpp:19
Oversampling
Oversampling bit fields in the control and measurement register.
Definition: bmp3xx_base.h:50
bool set_mode(OperationMode mode)
Set the barometer mode.
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
union esphome::bmp3xx_base::BMP3XXComponent::@27 int_status_
virtual bool read_bytes(uint8_t a_register, uint8_t *data, size_t len)=0
union esphome::bmp3xx_base::BMP3XXComponent::@25 chip_id_
bool data_ready()
Checks if a measurement is ready.
bool start_forced_conversion()
Start a one shot measurement in FORCED_MODE.
float bmp388_compensate_temperature_(float uncomp_temp)
bool set_iir_filter(IIRFilter iir_filter)
Set the IIR filter setting: OFF, 2, 3, 8, 16, 32.
bool get_measurements(float &temperature, float &pressure)
Get a temperature and pressure measurement.
bool get_pressure(float &pressure)
Get a pressure measurement.
bool stop_conversion()
Stop the conversion and return to SLEEP_MODE.
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
bool set_temperature_oversampling(Oversampling temperature_oversampling)
Set the temperature oversampling: OFF, X1, X2, X4, X8, X16, X32.
bool get_temperature(float &temperature)
Get a temperature measurement.
enum esphome::bmp3xx_base::BMP3XXComponent::ErrorCode NONE
uint8_t reset()
Soft reset the sensor.
union esphome::bmp3xx_base::BMP3XXComponent::@28 pwr_ctrl_
union esphome::bmp3xx_base::BMP3XXComponent::@29 osr_
uint8_t status
Definition: bl0942.h:74
IIRFilter
Infinite Impulse Response (IIR) filter bit field in the configuration register.
Definition: bmp3xx_base.h:60
bool set_pressure_oversampling(Oversampling pressure_oversampling)
Set the pressure oversampling: OFF, X1, X2, X4, X8, X16, X32.
virtual bool write_byte(uint8_t a_register, uint8_t data)=0
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
uint8_t oversampling_to_time(Oversampling over_sampling)
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
OperationMode
Device mode bitfield in the control and measurement register.
Definition: bmp3xx_base.h:47
float bmp388_compensate_pressure_(float uncomp_press, float t_lin)
bool set_oversampling_register(Oversampling pressure_oversampling, Oversampling temperature_oversampling)
Set the BMP388 oversampling register.
struct esphome::bmp3xx_base::BMP3XXComponent::FloatParams compensation_float_params_
union esphome::bmp3xx_base::BMP3XXComponent::@26 event_
virtual bool read_byte(uint8_t a_register, uint8_t *data)=0
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26