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