ESPHome  2022.8.0
bme680.cpp
Go to the documentation of this file.
1 #include "bme680.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/hal.h"
4 
5 namespace esphome {
6 namespace bme680 {
7 
8 static const char *const TAG = "bme680.sensor";
9 
10 static const uint8_t BME680_REGISTER_COEFF1 = 0x89;
11 static const uint8_t BME680_REGISTER_COEFF2 = 0xE1;
12 
13 static const uint8_t BME680_REGISTER_CONFIG = 0x75;
14 static const uint8_t BME680_REGISTER_CONTROL_MEAS = 0x74;
15 static const uint8_t BME680_REGISTER_CONTROL_HUMIDITY = 0x72;
16 static const uint8_t BME680_REGISTER_CONTROL_GAS1 = 0x71;
17 static const uint8_t BME680_REGISTER_CONTROL_GAS0 = 0x70;
18 static const uint8_t BME680_REGISTER_HEATER_HEAT0 = 0x5A;
19 static const uint8_t BME680_REGISTER_HEATER_WAIT0 = 0x64;
20 
21 static const uint8_t BME680_REGISTER_CHIPID = 0xD0;
22 
23 static const uint8_t BME680_REGISTER_FIELD0 = 0x1D;
24 
25 const float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, -0.8,
26  0.0, 0.0, -0.2, -0.5, 0.0, -1.0, 0.0, 0.0};
27 
28 const float BME680_GAS_LOOKUP_TABLE_2[16] PROGMEM = {0.0, 0.0, 0.0, 0.0, 0.1, 0.7, 0.0, -0.8,
29  -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
30 
31 static const char *oversampling_to_str(BME680Oversampling oversampling) {
32  switch (oversampling) {
34  return "None";
36  return "1x";
38  return "2x";
40  return "4x";
42  return "8x";
44  return "16x";
45  default:
46  return "UNKNOWN";
47  }
48 }
49 
50 static const char *iir_filter_to_str(BME680IIRFilter filter) {
51  switch (filter) {
53  return "OFF";
55  return "1x";
57  return "3x";
59  return "7x";
61  return "15x";
63  return "31x";
65  return "63x";
67  return "127x";
68  default:
69  return "UNKNOWN";
70  }
71 }
72 
74  ESP_LOGCONFIG(TAG, "Setting up BME680...");
75  uint8_t chip_id;
76  if (!this->read_byte(BME680_REGISTER_CHIPID, &chip_id) || chip_id != 0x61) {
77  this->mark_failed();
78  return;
79  }
80 
81  // Read calibration
82  uint8_t cal1[25];
83  if (!this->read_bytes(BME680_REGISTER_COEFF1, cal1, 25)) {
84  this->mark_failed();
85  return;
86  }
87  uint8_t cal2[16];
88  if (!this->read_bytes(BME680_REGISTER_COEFF2, cal2, 16)) {
89  this->mark_failed();
90  return;
91  }
92 
93  this->calibration_.t1 = cal2[9] << 8 | cal2[8];
94  this->calibration_.t2 = cal1[2] << 8 | cal1[1];
95  this->calibration_.t3 = cal1[3];
96 
97  this->calibration_.h1 = cal2[2] << 4 | (cal2[1] & 0x0F);
98  this->calibration_.h2 = cal2[0] << 4 | cal2[1] >> 4;
99  this->calibration_.h3 = cal2[3];
100  this->calibration_.h4 = cal2[4];
101  this->calibration_.h5 = cal2[5];
102  this->calibration_.h6 = cal2[6];
103  this->calibration_.h7 = cal2[7];
104 
105  this->calibration_.p1 = cal1[6] << 8 | cal1[5];
106  this->calibration_.p2 = cal1[8] << 8 | cal1[7];
107  this->calibration_.p3 = cal1[9];
108  this->calibration_.p4 = cal1[12] << 8 | cal1[11];
109  this->calibration_.p5 = cal1[14] << 8 | cal1[13];
110  this->calibration_.p6 = cal1[16];
111  this->calibration_.p7 = cal1[15];
112  this->calibration_.p8 = cal1[20] << 8 | cal1[19];
113  this->calibration_.p9 = cal1[22] << 8 | cal1[21];
114  this->calibration_.p10 = cal1[23];
115 
116  this->calibration_.gh1 = cal2[14];
117  this->calibration_.gh2 = cal2[12] << 8 | cal2[13];
118  this->calibration_.gh3 = cal2[15];
119 
120  if (!this->read_byte(0x02, &this->calibration_.res_heat_range)) {
121  this->mark_failed();
122  return;
123  }
124  if (!this->read_byte(0x00, &this->calibration_.res_heat_val)) {
125  this->mark_failed();
126  return;
127  }
128  if (!this->read_byte(0x04, &this->calibration_.range_sw_err)) {
129  this->mark_failed();
130  return;
131  }
132 
133  this->calibration_.ambient_temperature = 25; // prime ambient temperature
134 
135  // Config register
136  uint8_t config_register;
137  if (!this->read_byte(BME680_REGISTER_CONFIG, &config_register)) {
138  this->mark_failed();
139  return;
140  }
141  config_register &= ~0b00011100;
142  config_register |= (this->iir_filter_ & 0b111) << 2;
143  if (!this->write_byte(BME680_REGISTER_CONFIG, config_register)) {
144  this->mark_failed();
145  return;
146  }
147 
148  // Humidity control register
149  uint8_t hum_control;
150  if (!this->read_byte(BME680_REGISTER_CONTROL_HUMIDITY, &hum_control)) {
151  this->mark_failed();
152  return;
153  }
154  hum_control &= ~0b00000111;
155  hum_control |= this->humidity_oversampling_ & 0b111;
156  if (!this->write_byte(BME680_REGISTER_CONTROL_HUMIDITY, hum_control)) {
157  this->mark_failed();
158  return;
159  }
160 
161  // Gas 1 control register
162  uint8_t gas1_control;
163  if (!this->read_byte(BME680_REGISTER_CONTROL_GAS1, &gas1_control)) {
164  this->mark_failed();
165  return;
166  }
167  gas1_control &= ~0b00011111;
168  gas1_control |= 1 << 4;
169  gas1_control |= 0; // profile 0
170  if (!this->write_byte(BME680_REGISTER_CONTROL_GAS1, gas1_control)) {
171  this->mark_failed();
172  return;
173  }
174 
175  const bool heat_off = this->heater_temperature_ == 0 || this->heater_duration_ == 0;
176 
177  // Gas 0 control register
178  uint8_t gas0_control;
179  if (!this->read_byte(BME680_REGISTER_CONTROL_GAS0, &gas0_control)) {
180  this->mark_failed();
181  return;
182  }
183  gas0_control &= ~0b00001000;
184  gas0_control |= heat_off ? 0b100 : 0b000;
185  if (!this->write_byte(BME680_REGISTER_CONTROL_GAS0, gas0_control)) {
186  this->mark_failed();
187  return;
188  }
189 
190  if (!heat_off) {
191  // Gas Heater Temperature
193  if (!this->write_byte(BME680_REGISTER_HEATER_HEAT0, temperature)) {
194  this->mark_failed();
195  return;
196  }
197 
198  // Gas Heater Duration
199  uint8_t duration = this->calc_heater_duration_(this->heater_duration_);
200 
201  if (!this->write_byte(BME680_REGISTER_HEATER_WAIT0, duration)) {
202  this->mark_failed();
203  return;
204  }
205  }
206 }
207 
209  ESP_LOGCONFIG(TAG, "BME680:");
210  LOG_I2C_DEVICE(this);
211  if (this->is_failed()) {
212  ESP_LOGE(TAG, "Communication with BME680 failed!");
213  }
214  ESP_LOGCONFIG(TAG, " IIR Filter: %s", iir_filter_to_str(this->iir_filter_));
215  LOG_UPDATE_INTERVAL(this);
216 
217  LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
218  ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->temperature_oversampling_));
219  LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
220  ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->pressure_oversampling_));
221  LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
222  ESP_LOGCONFIG(TAG, " Oversampling: %s", oversampling_to_str(this->humidity_oversampling_));
223  LOG_SENSOR(" ", "Gas Resistance", this->gas_resistance_sensor_);
224  if (this->heater_duration_ == 0 || this->heater_temperature_ == 0) {
225  ESP_LOGCONFIG(TAG, " Heater OFF");
226  } else {
227  ESP_LOGCONFIG(TAG, " Heater temperature=%u°C duration=%ums", this->heater_temperature_, this->heater_duration_);
228  }
229 }
230 
232 
234  uint8_t meas_control = 0; // No need to fetch, we're setting all fields
235  meas_control |= (this->temperature_oversampling_ & 0b111) << 5;
236  meas_control |= (this->pressure_oversampling_ & 0b111) << 2;
237  meas_control |= 0b01; // forced mode
238  if (!this->write_byte(BME680_REGISTER_CONTROL_MEAS, meas_control)) {
239  this->status_set_warning();
240  return;
241  }
242 
243  this->set_timeout("data", this->calc_meas_duration_(), [this]() { this->read_data_(); });
244 }
245 
247  if (temperature < 200)
248  temperature = 200;
249  if (temperature > 400)
250  temperature = 400;
251 
252  const uint8_t ambient_temperature = this->calibration_.ambient_temperature;
253  const int8_t gh1 = this->calibration_.gh1;
254  const int16_t gh2 = this->calibration_.gh2;
255  const int8_t gh3 = this->calibration_.gh3;
256  const uint8_t res_heat_range = this->calibration_.res_heat_range;
257  const uint8_t res_heat_val = this->calibration_.res_heat_val;
258 
259  uint8_t heatr_res;
260  int32_t var1;
261  int32_t var2;
262  int32_t var3;
263  int32_t var4;
264  int32_t var5;
265  int32_t heatr_res_x100;
266 
267  var1 = (((int32_t) ambient_temperature * gh3) / 1000) * 256;
268  var2 = (gh1 + 784) * (((((gh2 + 154009) * temperature * 5) / 100) + 3276800) / 10);
269  var3 = var1 + (var2 / 2);
270  var4 = (var3 / (res_heat_range + 4));
271  var5 = (131 * res_heat_val) + 65536;
272  heatr_res_x100 = (int32_t)(((var4 / var5) - 250) * 34);
273  heatr_res = (uint8_t)((heatr_res_x100 + 50) / 100);
274 
275  return heatr_res;
276 }
277 uint8_t BME680Component::calc_heater_duration_(uint16_t duration) {
278  uint8_t factor = 0;
279  uint8_t duration_value;
280 
281  if (duration >= 0xfc0) {
282  duration_value = 0xff;
283  } else {
284  while (duration > 0x3F) {
285  duration /= 4;
286  factor += 1;
287  }
288  duration_value = duration + (factor * 64);
289  }
290 
291  return duration_value;
292 }
294  uint8_t data[15];
295  if (!this->read_bytes(BME680_REGISTER_FIELD0, data, 15)) {
296  this->status_set_warning();
297  return;
298  }
299 
300  uint32_t raw_temperature = (uint32_t(data[5]) << 12) | (uint32_t(data[6]) << 4) | (uint32_t(data[7]) >> 4);
301  uint32_t raw_pressure = (uint32_t(data[2]) << 12) | (uint32_t(data[3]) << 4) | (uint32_t(data[4]) >> 4);
302  uint32_t raw_humidity = (uint32_t(data[8]) << 8) | uint32_t(data[9]);
303  uint16_t raw_gas = (uint16_t(data[13]) << 2) | (uint16_t(14) >> 6);
304  uint8_t gas_range = data[14] & 0x0F;
305 
306  float temperature = this->calc_temperature_(raw_temperature);
307  float pressure = this->calc_pressure_(raw_pressure);
308  float humidity = this->calc_humidity_(raw_humidity);
309  float gas_resistance = NAN;
310  if (data[14] & 0x20) {
311  gas_resistance = this->calc_gas_resistance_(raw_gas, gas_range);
312  }
313 
314  ESP_LOGD(TAG, "Got temperature=%.1f°C pressure=%.1fhPa humidity=%.1f%% gas_resistance=%.1fΩ", temperature, pressure,
315  humidity, gas_resistance);
316  if (this->temperature_sensor_ != nullptr)
317  this->temperature_sensor_->publish_state(temperature);
318  if (this->pressure_sensor_ != nullptr)
319  this->pressure_sensor_->publish_state(pressure);
320  if (this->humidity_sensor_ != nullptr)
321  this->humidity_sensor_->publish_state(humidity);
322  if (this->gas_resistance_sensor_ != nullptr)
323  this->gas_resistance_sensor_->publish_state(gas_resistance);
324  this->status_clear_warning();
325 }
326 
327 float BME680Component::calc_temperature_(uint32_t raw_temperature) {
328  float var1 = 0;
329  float var2 = 0;
330  float var3 = 0;
331  float calc_temp = 0;
332  float temp_adc = raw_temperature;
333 
334  const float t1 = this->calibration_.t1;
335  const float t2 = this->calibration_.t2;
336  const float t3 = this->calibration_.t3;
337 
338  /* calculate var1 data */
339  var1 = ((temp_adc / 16384.0f) - (t1 / 1024.0f)) * t2;
340 
341  /* calculate var2 data */
342  var3 = (temp_adc / 131072.0f) - (t1 / 8192.0f);
343  var2 = var3 * var3 * t3 * 16.0f;
344 
345  /* t_fine value*/
346  this->calibration_.tfine = (var1 + var2);
347 
348  /* compensated temperature data*/
349  calc_temp = ((this->calibration_.tfine) / 5120.0f);
350 
351  return calc_temp;
352 }
353 float BME680Component::calc_pressure_(uint32_t raw_pressure) {
354  const float tfine = this->calibration_.tfine;
355  const float p1 = this->calibration_.p1;
356  const float p2 = this->calibration_.p2;
357  const float p3 = this->calibration_.p3;
358  const float p4 = this->calibration_.p4;
359  const float p5 = this->calibration_.p5;
360  const float p6 = this->calibration_.p6;
361  const float p7 = this->calibration_.p7;
362  const float p8 = this->calibration_.p8;
363  const float p9 = this->calibration_.p9;
364  const float p10 = this->calibration_.p10;
365 
366  float var1 = 0;
367  float var2 = 0;
368  float var3 = 0;
369  float var4 = 0;
370  float calc_pres = 0;
371 
372  var1 = (tfine / 2.0f) - 64000.0f;
373  var2 = var1 * var1 * (p6 / 131072.0f);
374  var2 = var2 + var1 * p5 * 2.0f;
375  var2 = (var2 / 4.0f) + (p4 * 65536.0f);
376  var1 = (((p3 * var1 * var1) / 16384.0f) + (p2 * var1)) / 524288.0f;
377  var1 = (1.0f + (var1 / 32768.0f)) * p1;
378  calc_pres = 1048576.0f - float(raw_pressure);
379 
380  /* Avoid exception caused by division by zero */
381  if (int(var1) != 0) {
382  calc_pres = ((calc_pres - (var2 / 4096.0f)) * 6250.0f) / var1;
383  var1 = (p9 * calc_pres * calc_pres) / 2147483648.0f;
384  var2 = calc_pres * (p8 / 32768.0f);
385  var4 = calc_pres / 256.0f;
386  var3 = var4 * var4 * var4 * (p10 / 131072.0f);
387  calc_pres = calc_pres + (var1 + var2 + var3 + (p7 * 128.0f)) / 16.0f;
388  } else {
389  calc_pres = 0;
390  }
391 
392  return calc_pres / 100.0f;
393 }
394 
395 float BME680Component::calc_humidity_(uint16_t raw_humidity) {
396  const float tfine = this->calibration_.tfine;
397  const float h1 = this->calibration_.h1;
398  const float h2 = this->calibration_.h2;
399  const float h3 = this->calibration_.h3;
400  const float h4 = this->calibration_.h4;
401  const float h5 = this->calibration_.h5;
402  const float h6 = this->calibration_.h6;
403  const float h7 = this->calibration_.h7;
404 
405  float calc_hum = 0;
406  float var1 = 0;
407  float var2 = 0;
408  float var3 = 0;
409  float var4 = 0;
410  float temp_comp;
411 
412  /* compensated temperature data*/
413  temp_comp = tfine / 5120.0f;
414 
415  var1 = float(raw_humidity) - (h1 * 16.0f + ((h3 / 2.0f) * temp_comp));
416  var2 = var1 *
417  (((h2 / 262144.0f) * (1.0f + ((h4 / 16384.0f) * temp_comp) + ((h5 / 1048576.0f) * temp_comp * temp_comp))));
418  var3 = h6 / 16384.0f;
419  var4 = h7 / 2097152.0f;
420 
421  calc_hum = var2 + (var3 + var4 * temp_comp) * var2 * var2;
422 
423  if (calc_hum > 100.0f) {
424  calc_hum = 100.0f;
425  } else if (calc_hum < 0.0f) {
426  calc_hum = 0.0f;
427  }
428 
429  return calc_hum;
430 }
431 uint32_t BME680Component::calc_gas_resistance_(uint16_t raw_gas, uint8_t range) {
432  float calc_gas_res;
433  float var1 = 0;
434  float var2 = 0;
435  float var3 = 0;
436  const float range_sw_err = this->calibration_.range_sw_err;
437 
438  var1 = 1340.0f + (5.0f * range_sw_err);
439  var2 = var1 * (1.0f + BME680_GAS_LOOKUP_TABLE_1[range] / 100.0f);
440  var3 = 1.0f + (BME680_GAS_LOOKUP_TABLE_2[range] / 100.0f);
441 
442  calc_gas_res = 1.0f / (var3 * 0.000000125f * float(1 << range) * (((float(raw_gas) - 512.0f) / var2) + 1.0f));
443 
444  return static_cast<uint32_t>(calc_gas_res);
445 }
447  uint32_t tph_dur; // Calculate in us
448  uint32_t meas_cycles;
449  const uint8_t os_to_meas_cycles[6] = {0, 1, 2, 4, 8, 16};
450 
451  meas_cycles = os_to_meas_cycles[this->temperature_oversampling_];
452  meas_cycles += os_to_meas_cycles[this->pressure_oversampling_];
453  meas_cycles += os_to_meas_cycles[this->humidity_oversampling_];
454 
455  /* TPH measurement duration */
456  tph_dur = meas_cycles * 1963u;
457  tph_dur += 477 * 4; // TPH switching duration
458  tph_dur += 477 * 5; // Gas measurement duration
459  tph_dur += 500; // Get it to the closest whole number.
460  tph_dur /= 1000; // Convert to ms
461 
462  tph_dur += 1; // Wake up duration of 1ms
463 
464  /* The remaining time should be used for heating */
465  tph_dur += this->heater_duration_;
466 
467  return tph_dur;
468 }
470  this->temperature_oversampling_ = temperature_oversampling;
471 }
473  this->pressure_oversampling_ = pressure_oversampling;
474 }
476  this->humidity_oversampling_ = humidity_oversampling;
477 }
478 void BME680Component::set_iir_filter(BME680IIRFilter iir_filter) { this->iir_filter_ = iir_filter; }
479 void BME680Component::set_heater(uint16_t heater_temperature, uint16_t heater_duration) {
480  this->heater_temperature_ = heater_temperature;
481  this->heater_duration_ = heater_duration;
482 }
483 
484 } // namespace bme680
485 } // namespace esphome
float pressure
Definition: qmp6988.h:72
BME680Oversampling temperature_oversampling_
Definition: bme680.h:125
bool read_byte(uint8_t a_register, uint8_t *data, bool stop=true)
Definition: i2c.h:96
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
BME680CalibrationData calibration_
Definition: bme680.h:124
float get_setup_priority() const override
Definition: bme680.cpp:231
BME680IIRFilter
Enum listing all IIR Filter options for the BME680.
Definition: bme680.h:11
uint8_t calc_heater_duration_(uint16_t duration)
Calculate the heater duration value to send to the BME680 register.
Definition: bme680.cpp:277
void read_data_()
Read data from the BME680 and publish results.
Definition: bme680.cpp:293
void set_pressure_oversampling(BME680Oversampling pressure_oversampling)
Set the pressure oversampling value. Defaults to 16X.
Definition: bme680.cpp:472
float calc_humidity_(uint16_t raw_humidity)
Calculate the relative humidity in % using the provided raw ADC value.
Definition: bme680.cpp:395
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:67
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Definition: i2c.h:68
float temperature
Definition: qmp6988.h:71
BME680IIRFilter iir_filter_
Definition: bme680.h:128
uint32_t calc_gas_resistance_(uint16_t raw_gas, uint8_t range)
Calculate the gas resistance in Ω using the provided raw ADC value.
Definition: bme680.cpp:431
void set_humidity_oversampling(BME680Oversampling humidity_oversampling)
Set the humidity oversampling value. Defaults to 16X.
Definition: bme680.cpp:475
void set_heater(uint16_t heater_temperature, uint16_t heater_duration)
Set how the internal heater should operate.
Definition: bme680.cpp:479
sensor::Sensor * gas_resistance_sensor_
Definition: bme680.h:135
BME680Oversampling humidity_oversampling_
Definition: bme680.h:127
sensor::Sensor * pressure_sensor_
Definition: bme680.h:133
const float BME680_GAS_LOOKUP_TABLE_1 [16] PROGMEM
Definition: bme680.cpp:25
void status_clear_warning()
Definition: component.cpp:148
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:72
uint32_t calc_meas_duration_()
Calculate how long the sensor will take until we can retrieve data.
Definition: bme680.cpp:446
void status_set_warning()
Definition: component.cpp:140
float calc_pressure_(uint32_t raw_pressure)
Calculate the pressure in hPa using the provided raw ADC value.
Definition: bme680.cpp:353
sensor::Sensor * temperature_sensor_
Definition: bme680.h:132
BME680Oversampling
Enum listing all oversampling options for the BME680.
Definition: bme680.h:23
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:111
void set_iir_filter(BME680IIRFilter iir_filter)
Set the IIR Filter value. Defaults to no IIR Filter.
Definition: bme680.cpp:478
Definition: a4988.cpp:4
uint8_t calc_heater_resistance_(uint16_t temperature)
Calculate the heater resistance value to send to the BME680 register.
Definition: bme680.cpp:246
sensor::Sensor * humidity_sensor_
Definition: bme680.h:134
BME680Oversampling pressure_oversampling_
Definition: bme680.h:126
void set_temperature_oversampling(BME680Oversampling temperature_oversampling)
Set the temperature oversampling value. Defaults to 16X.
Definition: bme680.cpp:469
float calc_temperature_(uint32_t raw_temperature)
Calculate the temperature in °C using the provided raw ADC value.
Definition: bme680.cpp:327