ESPHome  2022.12.8
bme280.cpp
Go to the documentation of this file.
1 #include "bme280.h"
2 #include "esphome/core/hal.h"
3 #include "esphome/core/log.h"
4 
5 namespace esphome {
6 namespace bme280 {
7 
8 static const char *const TAG = "bme280.sensor";
9 
10 static const uint8_t BME280_REGISTER_DIG_T1 = 0x88;
11 static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A;
12 static const uint8_t BME280_REGISTER_DIG_T3 = 0x8C;
13 
14 static const uint8_t BME280_REGISTER_DIG_P1 = 0x8E;
15 static const uint8_t BME280_REGISTER_DIG_P2 = 0x90;
16 static const uint8_t BME280_REGISTER_DIG_P3 = 0x92;
17 static const uint8_t BME280_REGISTER_DIG_P4 = 0x94;
18 static const uint8_t BME280_REGISTER_DIG_P5 = 0x96;
19 static const uint8_t BME280_REGISTER_DIG_P6 = 0x98;
20 static const uint8_t BME280_REGISTER_DIG_P7 = 0x9A;
21 static const uint8_t BME280_REGISTER_DIG_P8 = 0x9C;
22 static const uint8_t BME280_REGISTER_DIG_P9 = 0x9E;
23 
24 static const uint8_t BME280_REGISTER_DIG_H1 = 0xA1;
25 static const uint8_t BME280_REGISTER_DIG_H2 = 0xE1;
26 static const uint8_t BME280_REGISTER_DIG_H3 = 0xE3;
27 static const uint8_t BME280_REGISTER_DIG_H4 = 0xE4;
28 static const uint8_t BME280_REGISTER_DIG_H5 = 0xE5;
29 static const uint8_t BME280_REGISTER_DIG_H6 = 0xE7;
30 
31 static const uint8_t BME280_REGISTER_CHIPID = 0xD0;
32 static const uint8_t BME280_REGISTER_RESET = 0xE0;
33 
34 static const uint8_t BME280_REGISTER_CONTROLHUMID = 0xF2;
35 static const uint8_t BME280_REGISTER_STATUS = 0xF3;
36 static const uint8_t BME280_REGISTER_CONTROL = 0xF4;
37 static const uint8_t BME280_REGISTER_CONFIG = 0xF5;
38 static const uint8_t BME280_REGISTER_MEASUREMENTS = 0xF7;
39 static const uint8_t BME280_REGISTER_PRESSUREDATA = 0xF7;
40 static const uint8_t BME280_REGISTER_TEMPDATA = 0xFA;
41 static const uint8_t BME280_REGISTER_HUMIDDATA = 0xFD;
42 
43 static const uint8_t BME280_MODE_FORCED = 0b01;
44 static const uint8_t BME280_SOFT_RESET = 0xB6;
45 static const uint8_t BME280_STATUS_IM_UPDATE = 0b01;
46 
47 inline uint16_t combine_bytes(uint8_t msb, uint8_t lsb) { return ((msb & 0xFF) << 8) | (lsb & 0xFF); }
48 
49 static const char *oversampling_to_str(BME280Oversampling oversampling) {
50  switch (oversampling) {
52  return "None";
54  return "1x";
56  return "2x";
58  return "4x";
60  return "8x";
62  return "16x";
63  default:
64  return "UNKNOWN";
65  }
66 }
67 
68 static const char *iir_filter_to_str(BME280IIRFilter filter) {
69  switch (filter) {
71  return "OFF";
73  return "2x";
75  return "4x";
77  return "8x";
79  return "16x";
80  default:
81  return "UNKNOWN";
82  }
83 }
84 
86  ESP_LOGCONFIG(TAG, "Setting up BME280...");
87  uint8_t chip_id = 0;
88 
89  // Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
90  // and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
92  this->component_state_ &= ~COMPONENT_STATE_MASK;
94  }
95 
96  if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
97  this->error_code_ = COMMUNICATION_FAILED;
98  this->mark_failed();
99  return;
100  }
101  if (chip_id != 0x60) {
102  this->error_code_ = WRONG_CHIP_ID;
103  this->mark_failed();
104  return;
105  }
106 
107  // Send a soft reset.
108  if (!this->write_byte(BME280_REGISTER_RESET, BME280_SOFT_RESET)) {
109  this->mark_failed();
110  return;
111  }
112  // Wait until the NVM data has finished loading.
113  uint8_t status;
114  uint8_t retry = 5;
115  do {
116  delay(2);
117  if (!this->read_byte(BME280_REGISTER_STATUS, &status)) {
118  ESP_LOGW(TAG, "Error reading status register.");
119  this->mark_failed();
120  return;
121  }
122  } while ((status & BME280_STATUS_IM_UPDATE) && (--retry));
123  if (status & BME280_STATUS_IM_UPDATE) {
124  ESP_LOGW(TAG, "Timeout loading NVM.");
125  this->mark_failed();
126  return;
127  }
128 
129  // Read calibration
130  this->calibration_.t1 = read_u16_le_(BME280_REGISTER_DIG_T1);
131  this->calibration_.t2 = read_s16_le_(BME280_REGISTER_DIG_T2);
132  this->calibration_.t3 = read_s16_le_(BME280_REGISTER_DIG_T3);
133 
134  this->calibration_.p1 = read_u16_le_(BME280_REGISTER_DIG_P1);
135  this->calibration_.p2 = read_s16_le_(BME280_REGISTER_DIG_P2);
136  this->calibration_.p3 = read_s16_le_(BME280_REGISTER_DIG_P3);
137  this->calibration_.p4 = read_s16_le_(BME280_REGISTER_DIG_P4);
138  this->calibration_.p5 = read_s16_le_(BME280_REGISTER_DIG_P5);
139  this->calibration_.p6 = read_s16_le_(BME280_REGISTER_DIG_P6);
140  this->calibration_.p7 = read_s16_le_(BME280_REGISTER_DIG_P7);
141  this->calibration_.p8 = read_s16_le_(BME280_REGISTER_DIG_P8);
142  this->calibration_.p9 = read_s16_le_(BME280_REGISTER_DIG_P9);
143 
144  this->calibration_.h1 = read_u8_(BME280_REGISTER_DIG_H1);
145  this->calibration_.h2 = read_s16_le_(BME280_REGISTER_DIG_H2);
146  this->calibration_.h3 = read_u8_(BME280_REGISTER_DIG_H3);
147  this->calibration_.h4 = read_u8_(BME280_REGISTER_DIG_H4) << 4 | (read_u8_(BME280_REGISTER_DIG_H4 + 1) & 0x0F);
148  this->calibration_.h5 = read_u8_(BME280_REGISTER_DIG_H5 + 1) << 4 | (read_u8_(BME280_REGISTER_DIG_H5) >> 4);
149  this->calibration_.h6 = read_u8_(BME280_REGISTER_DIG_H6);
150 
151  uint8_t humid_control_val = 0;
152  if (!this->read_byte(BME280_REGISTER_CONTROLHUMID, &humid_control_val)) {
153  this->mark_failed();
154  return;
155  }
156  humid_control_val &= ~0b00000111;
157  humid_control_val |= this->humidity_oversampling_ & 0b111;
158  if (!this->write_byte(BME280_REGISTER_CONTROLHUMID, humid_control_val)) {
159  this->mark_failed();
160  return;
161  }
162 
163  uint8_t config_register = 0;
164  if (!this->read_byte(BME280_REGISTER_CONFIG, &config_register)) {
165  this->mark_failed();
166  return;
167  }
168  config_register &= ~0b11111100;
169  config_register |= 0b101 << 5; // 1000 ms standby time
170  config_register |= (this->iir_filter_ & 0b111) << 2;
171  if (!this->write_byte(BME280_REGISTER_CONFIG, config_register)) {
172  this->mark_failed();
173  return;
174  }
175 }
177  ESP_LOGCONFIG(TAG, "BME280:");
178  LOG_I2C_DEVICE(this);
179  switch (this->error_code_) {
181  ESP_LOGE(TAG, "Communication with BME280 failed!");
182  break;
183  case WRONG_CHIP_ID:
184  ESP_LOGE(TAG, "BME280 has wrong chip ID! Is it a BME280?");
185  break;
186  case NONE:
187  default:
188  break;
189  }
190  ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
191  LOG_UPDATE_INTERVAL(this);
192 
193  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
194  ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->temperature_oversampling_));
195  LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
196  ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
197  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
198  ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->humidity_oversampling_));
199 }
201 
202 inline uint8_t oversampling_to_time(BME280Oversampling over_sampling) { return (1 << uint8_t(over_sampling)) >> 1; }
203 
205  // Enable sensor
206  ESP_LOGV(TAG, "Sending conversion request...");
207  uint8_t meas_value = 0;
208  meas_value |= (this->temperature_oversampling_ & 0b111) << 5;
209  meas_value |= (this->pressure_oversampling_ & 0b111) << 2;
210  meas_value |= BME280_MODE_FORCED;
211  if (!this->write_byte(BME280_REGISTER_CONTROL, meas_value)) {
212  this->status_set_warning();
213  return;
214  }
215 
216  float meas_time = 1.5f;
217  meas_time += 2.3f * oversampling_to_time(this->temperature_oversampling_);
218  meas_time += 2.3f * oversampling_to_time(this->pressure_oversampling_) + 0.575f;
219  meas_time += 2.3f * oversampling_to_time(this->humidity_oversampling_) + 0.575f;
220 
221  this->set_timeout("data", uint32_t(ceilf(meas_time)), [this]() {
222  uint8_t data[8];
223  if (!this->read_bytes(BME280_REGISTER_MEASUREMENTS, data, 8)) {
224  ESP_LOGW(TAG, "Error reading registers.");
225  this->status_set_warning();
226  return;
227  }
228  int32_t t_fine = 0;
229  float temperature = this->read_temperature_(data, &t_fine);
230  if (std::isnan(temperature)) {
231  ESP_LOGW(TAG, "Invalid temperature, cannot read pressure & humidity values.");
232  this->status_set_warning();
233  return;
234  }
235  float pressure = this->read_pressure_(data, t_fine);
236  float humidity = this->read_humidity_(data, t_fine);
237 
238  ESP_LOGV(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%%", temperature, pressure, humidity);
239  if (this->temperature_sensor_ != nullptr)
240  this->temperature_sensor_->publish_state(temperature);
241  if (this->pressure_sensor_ != nullptr)
242  this->pressure_sensor_->publish_state(pressure);
243  if (this->humidity_sensor_ != nullptr)
244  this->humidity_sensor_->publish_state(humidity);
245  this->status_clear_warning();
246  });
247 }
248 float BME280Component::read_temperature_(const uint8_t *data, int32_t *t_fine) {
249  int32_t adc = ((data[3] & 0xFF) << 16) | ((data[4] & 0xFF) << 8) | (data[5] & 0xFF);
250  adc >>= 4;
251  if (adc == 0x80000) {
252  // temperature was disabled
253  return NAN;
254  }
255 
256  const int32_t t1 = this->calibration_.t1;
257  const int32_t t2 = this->calibration_.t2;
258  const int32_t t3 = this->calibration_.t3;
259 
260  int32_t var1 = (((adc >> 3) - (t1 << 1)) * t2) >> 11;
261  int32_t var2 = (((((adc >> 4) - t1) * ((adc >> 4) - t1)) >> 12) * t3) >> 14;
262  *t_fine = var1 + var2;
263 
264  float temperature = (*t_fine * 5 + 128) >> 8;
265  return temperature / 100.0f;
266 }
267 
268 float BME280Component::read_pressure_(const uint8_t *data, int32_t t_fine) {
269  int32_t adc = ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF);
270  adc >>= 4;
271  if (adc == 0x80000) {
272  // pressure was disabled
273  return NAN;
274  }
275  const int64_t p1 = this->calibration_.p1;
276  const int64_t p2 = this->calibration_.p2;
277  const int64_t p3 = this->calibration_.p3;
278  const int64_t p4 = this->calibration_.p4;
279  const int64_t p5 = this->calibration_.p5;
280  const int64_t p6 = this->calibration_.p6;
281  const int64_t p7 = this->calibration_.p7;
282  const int64_t p8 = this->calibration_.p8;
283  const int64_t p9 = this->calibration_.p9;
284 
285  int64_t var1, var2, p;
286  var1 = int64_t(t_fine) - 128000;
287  var2 = var1 * var1 * p6;
288  var2 = var2 + ((var1 * p5) << 17);
289  var2 = var2 + (p4 << 35);
290  var1 = ((var1 * var1 * p3) >> 8) + ((var1 * p2) << 12);
291  var1 = ((int64_t(1) << 47) + var1) * p1 >> 33;
292 
293  if (var1 == 0)
294  return NAN;
295 
296  p = 1048576 - adc;
297  p = (((p << 31) - var2) * 3125) / var1;
298  var1 = (p9 * (p >> 13) * (p >> 13)) >> 25;
299  var2 = (p8 * p) >> 19;
300 
301  p = ((p + var1 + var2) >> 8) + (p7 << 4);
302  return (p / 256.0f) / 100.0f;
303 }
304 
305 float BME280Component::read_humidity_(const uint8_t *data, int32_t t_fine) {
306  uint16_t raw_adc = ((data[6] & 0xFF) << 8) | (data[7] & 0xFF);
307  if (raw_adc == 0x8000)
308  return NAN;
309 
310  int32_t adc = raw_adc;
311 
312  const int32_t h1 = this->calibration_.h1;
313  const int32_t h2 = this->calibration_.h2;
314  const int32_t h3 = this->calibration_.h3;
315  const int32_t h4 = this->calibration_.h4;
316  const int32_t h5 = this->calibration_.h5;
317  const int32_t h6 = this->calibration_.h6;
318 
319  int32_t v_x1_u32r = t_fine - 76800;
320 
321  v_x1_u32r = ((((adc << 14) - (h4 << 20) - (h5 * v_x1_u32r)) + 16384) >> 15) *
322  (((((((v_x1_u32r * h6) >> 10) * (((v_x1_u32r * h3) >> 11) + 32768)) >> 10) + 2097152) * h2 + 8192) >> 14);
323 
324  v_x1_u32r = v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * h1) >> 4);
325 
326  v_x1_u32r = v_x1_u32r < 0 ? 0 : v_x1_u32r;
327  v_x1_u32r = v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r;
328  float h = v_x1_u32r >> 12;
329 
330  return h / 1024.0f;
331 }
333  this->temperature_oversampling_ = temperature_over_sampling;
334 }
336  this->pressure_oversampling_ = pressure_over_sampling;
337 }
339  this->humidity_oversampling_ = humidity_over_sampling;
340 }
341 void BME280Component::set_iir_filter(BME280IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
342 uint8_t BME280Component::read_u8_(uint8_t a_register) {
343  uint8_t data = 0;
344  this->read_byte(a_register, &data);
345  return data;
346 }
347 uint16_t BME280Component::read_u16_le_(uint8_t a_register) {
348  uint16_t data = 0;
349  this->read_byte_16(a_register, &data);
350  return (data >> 8) | (data << 8);
351 }
352 int16_t BME280Component::read_s16_le_(uint8_t a_register) { return this->read_u16_le_(a_register); }
353 
354 } // namespace bme280
355 } // namespace esphome
const uint32_t COMPONENT_STATE_FAILED
Definition: component.cpp:35
sensor::Sensor * pressure_sensor_
Definition: bme280.h:100
float pressure
Definition: qmp6988.h:72
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:96
bool read_byte_16(uint8_t a_register, uint16_t *data)
Definition: i2c.h:107
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
uint16_t read_u16_le_(uint8_t a_register)
Definition: bme280.cpp:347
float read_temperature_(const uint8_t *data, int32_t *t_fine)
Read the temperature value and store the calculated ambient temperature in t_fine.
Definition: bme280.cpp:248
sensor::Sensor * temperature_sensor_
Definition: bme280.h:99
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:68
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Definition: i2c.h:68
float read_humidity_(const uint8_t *data, int32_t t_fine)
Read the humidity value in % using the provided t_fine value.
Definition: bme280.cpp:305
float temperature
Definition: qmp6988.h:71
uint32_t component_state_
State of this component.
Definition: component.h:256
uint8_t read_u8_(uint8_t a_register)
Definition: bme280.cpp:342
enum esphome::bme280::BME280Component::ErrorCode NONE
BME280Oversampling humidity_oversampling_
Definition: bme280.h:97
void status_clear_warning()
Definition: component.cpp:149
const uint32_t COMPONENT_STATE_CONSTRUCTION
Definition: component.cpp:32
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:72
uint8_t oversampling_to_time(BME280Oversampling over_sampling)
Definition: bme280.cpp:202
sensor::Sensor * humidity_sensor_
Definition: bme280.h:101
BME280Oversampling
Enum listing all Oversampling values for the BME280.
Definition: bme280.h:39
void status_set_warning()
Definition: component.cpp:141
const uint32_t COMPONENT_STATE_MASK
Definition: component.cpp:31
BME280IIRFilter
Enum listing all Infinite Impulse Filter values for the BME280.
Definition: bme280.h:52
uint8_t status
Definition: bl0942.h:23
void set_iir_filter(BME280IIRFilter iir_filter)
Set the IIR Filter used to increase accuracy, defaults to no IIR Filter.
Definition: bme280.cpp:341
BME280IIRFilter iir_filter_
Definition: bme280.h:98
float get_setup_priority() const override
Definition: bme280.cpp:200
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:123
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:112
void set_humidity_oversampling(BME280Oversampling humidity_over_sampling)
Set the oversampling value for the humidity sensor. Default is 16x.
Definition: bme280.cpp:338
uint8_t h
Definition: bl0939.h:21
Definition: a4988.cpp:4
float read_pressure_(const uint8_t *data, int32_t t_fine)
Read the pressure value in hPa using the provided t_fine value.
Definition: bme280.cpp:268
BME280CalibrationData calibration_
Definition: bme280.h:94
uint16_t combine_bytes(uint8_t msb, uint8_t lsb)
Definition: bme280.cpp:47
int16_t read_s16_le_(uint8_t a_register)
Definition: bme280.cpp:352
BME280Oversampling pressure_oversampling_
Definition: bme280.h:96
void set_temperature_oversampling(BME280Oversampling temperature_over_sampling)
Set the oversampling value for the temperature sensor. Default is 16x.
Definition: bme280.cpp:332
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:27
void set_pressure_oversampling(BME280Oversampling pressure_over_sampling)
Set the oversampling value for the pressure sensor. Default is 16x.
Definition: bme280.cpp:335
BME280Oversampling temperature_oversampling_
Definition: bme280.h:95