ESPHome 2026.6.2
Loading...
Searching...
No Matches
bmi270.cpp
Go to the documentation of this file.
1#include "bmi270.h"
2#include "bmi270_config.h"
3#include "esphome/core/log.h"
4#include "esphome/core/hal.h"
5
6namespace esphome::bmi270 {
7
8static const char *const TAG = "bmi270";
9
10#if defined(USE_ARDUINO) && !defined(USE_ESP32)
11static const size_t MAX_I2C_BUFFER_SIZE = 32;
12#else
13static const size_t MAX_I2C_BUFFER_SIZE = 256;
14#endif
15
16// Configuration blob upload
17// The BMI270 requires a firmware config blob to be written to its internal
18// memory after every power-on before sensors can be used.
19
21 // 1. Disable advanced power-save so the config port is accessible
22 if (!this->write_byte(BMI270_REG_PWR_CONF, 0x00))
23 return false;
24 delay(1);
25
26 // 2. Prepare config load: write 0x00 to INIT_CTRL to start
27 if (!this->write_byte(BMI270_REG_INIT_CTRL, 0x00))
28 return false;
29
30 // 3. Burst-write the config in pages
31 const uint8_t *cfg = BMI270_CONFIG_FILE;
32 constexpr size_t cfg_len = sizeof(BMI270_CONFIG_FILE);
33 size_t index = 0;
34
35 while (index != cfg_len) {
36 // Set the page address in INIT_ADDR registers
37 uint8_t addr_lsb = (uint8_t) ((index / 2) & 0x0F);
38 uint8_t addr_msb = (uint8_t) ((index / 2) >> 4);
39 if (!this->write_byte(BMI270_REG_INIT_ADDR_0, addr_lsb))
40 return false;
41 if (!this->write_byte(BMI270_REG_INIT_ADDR_0 + 1, addr_msb))
42 return false;
43
44 // Write a burst of up to the maximum allowed size
45 size_t burst = clamp_at_most(cfg_len - index, MAX_I2C_BUFFER_SIZE);
46 if (this->write_register(BMI270_REG_INIT_DATA, cfg + index, burst) != i2c::ERROR_OK)
47 return false;
48
49 index += burst;
50 }
51
52 // 4. Signal end of config load
53 if (!this->write_byte(BMI270_REG_INIT_CTRL, 0x01))
54 return false;
55 delay(20); // spec: wait ≥20 ms for init to complete
56
57 // 5. Check INTERNAL_STATUS: bit[0:3] should be 0x01 ("initialisation OK")
58 uint8_t status = 0;
59 if (!this->read_byte(BMI270_REG_INTERNAL_STATUS, &status))
60 return false;
61 if ((status & 0x0F) != 0x01) {
62 ESP_LOGE(TAG, "Config load failed: INTERNAL_STATUS=0x%02X (expected 0x01)", status);
63 return false;
64 }
65 return true;
66}
67
68// setup() ─
69
71 MotionComponent::setup();
72 // 1. Verify chip ID
73 uint8_t chip_id = 0;
74 if (!this->read_byte(BMI270_REG_CHIP_ID, &chip_id)) {
75 ESP_LOGE(TAG, "Failed to read chip ID – check wiring / address");
76 this->mark_failed();
77 return;
78 }
79 if (chip_id != BMI270_CHIP_ID_VALUE) {
80 ESP_LOGE(TAG, "Wrong chip ID: 0x%02X (expected 0x%02X)", chip_id, BMI270_CHIP_ID_VALUE);
81 this->mark_failed();
82 return;
83 }
84 ESP_LOGD(TAG, "Chip ID: 0x%02X", chip_id);
85
86 // 2. Soft-reset via CMD register (0x7E = 0xB6)
87 if (!this->write_byte(0x7E, 0xB6)) {
88 this->mark_failed();
89 return;
90 }
91 delay(20);
92
93 // 4. Upload the configuration blob
94 if (!load_config_file_()) {
95 ESP_LOGE(TAG, "Config file upload failed");
96 this->mark_failed();
97 return;
98 }
99 ESP_LOGD(TAG, "Config blob uploaded ✓");
100
101 // 5. Configure accelerometer
102 // ACC_CONF: ODR | BWP(0x2 = normal avg4) | perf_mode(1)
103 uint8_t acc_conf = (uint8_t) (accel_odr_) | (0x2 << 4) | (1 << 7);
104 if (!this->write_byte(BMI270_REG_ACC_CONF, acc_conf)) {
105 this->mark_failed();
106 return;
107 }
108 if (!this->write_byte(BMI270_REG_ACC_RANGE, (uint8_t) accel_range_)) {
109 this->mark_failed();
110 return;
111 }
112
113 // 6. Configure gyroscope
114 // GYR_CONF: ODR | BWP(0x2 = normal) | noise_perf(1) | filter_perf(1)
115 uint8_t gyr_conf = (uint8_t) (gyro_odr_) | (0x2 << 4) | (1 << 6) | (1 << 7);
116 if (!this->write_byte(BMI270_REG_GYR_CONF, gyr_conf)) {
117 this->mark_failed();
118 return;
119 }
120 if (!this->write_byte(BMI270_REG_GYR_RANGE, (uint8_t) gyro_range_)) {
121 this->mark_failed();
122 return;
123 }
124
125 // 7. Enable accelerometer, gyroscope, and temperature sensor
126 // PWR_CTRL bits: temp_en[3] | gyr_en[2] | acc_en[1]
127 if (!this->write_byte(BMI270_REG_PWR_CTRL, 0x0E)) {
128 this->mark_failed();
129 return;
130 }
131 delay(5);
132
133 // 8. Re-enable advanced power save (optional; keeps current low between reads)
134 // Disabled here for simplicity – leave in performance mode
135 if (!this->write_byte(BMI270_REG_PWR_CONF, 0x02)) { // bit1 = fifo_self_wakeup
136 this->mark_failed();
137 return;
138 }
139
140 ESP_LOGCONFIG(TAG, "BMI270 initialised successfully");
141}
142
144 ESP_LOGCONFIG(TAG, "BMI270 IMU:");
145 LOG_I2C_DEVICE(this);
146 if (this->is_failed()) {
147 ESP_LOGE(TAG, " Communication failed!");
148 return;
149 }
150
151 static constexpr const char *const ACCEL_RANGE_STRS[] = {"±2g", "±4g", "±8g", "±16g"};
152 static constexpr const char *const GYRO_RANGE_STRS[] = {"±2000°/s", "±1000°/s", "±500°/s", "±250°/s", "±125°/s"};
153
154 ESP_LOGCONFIG(TAG, " Accel range : %s", ACCEL_RANGE_STRS[accel_range_]);
155 ESP_LOGCONFIG(TAG, " Gyro range : %s", GYRO_RANGE_STRS[gyro_range_]);
156 MotionComponent::dump_config();
157}
158
159// update() ─
160// Reads all 6 axes + temperature in one block
161
163 if (this->is_failed())
164 return false;
165
166 // Accelerometer: registers 0x0C–0x11 (6 bytes: x_lsb, x_msb, y_lsb, y_msb, z_lsb, z_msb)
167 uint8_t raw_data[REG_READ_LEN];
168 if (!this->read_bytes(BMI270_REG_DATA_8, raw_data, REG_READ_LEN)) {
169 ESP_LOGW(TAG, "Failed to read IMU data");
170 return false;
171 }
172 // Scale factor: LSB/g depends on range
173 // raw is a signed 16-bit value; full-scale = range_g * 2^15 lsb
174 static constexpr float ACCEL_SCALE[] = {
175 2.0f / 32768.0f,
176 4.0f / 32768.0f,
177 8.0f / 32768.0f,
178 16.0f / 32768.0f,
179 };
180 float scale = ACCEL_SCALE[this->accel_range_];
181
182 data.acceleration[motion::X_AXIS] = (int16_t) ((raw_data[1] << 8) | raw_data[0]) * scale;
183 data.acceleration[motion::Y_AXIS] = (int16_t) ((raw_data[3] << 8) | raw_data[2]) * scale;
184 data.acceleration[motion::Z_AXIS] = (int16_t) ((raw_data[5] << 8) | raw_data[4]) * scale;
185
186 // Gyroscope: registers 0x12–0x17 (6 bytes)
187 // Scale: full-scale range / 2^15
188 static constexpr float GYRO_SCALE[] = {
189 2000.0f / 32768.0f, 1000.0f / 32768.0f, 500.0f / 32768.0f, 250.0f / 32768.0f, 125.0f / 32768.0f,
190 };
191 static constexpr uint8_t GYR_OFFS = BMI270_REG_DATA_14 - BMI270_REG_DATA_8;
192 scale = GYRO_SCALE[this->gyro_range_];
193
194 data.angular_rate[motion::X_AXIS] = (int16_t) ((raw_data[GYR_OFFS + 1] << 8) | raw_data[GYR_OFFS + 0]) * scale;
195 data.angular_rate[motion::Y_AXIS] = (int16_t) ((raw_data[GYR_OFFS + 3] << 8) | raw_data[GYR_OFFS + 2]) * scale;
196 data.angular_rate[motion::Z_AXIS] = (int16_t) ((raw_data[GYR_OFFS + 5] << 8) | raw_data[GYR_OFFS + 4]) * scale;
197
198 if (this->temperature_callback_.empty())
199 return true;
200 // Temperature: registers 0x22–0x23
201 // Formula from datasheet: T[°C] = raw / 512 + 23
202 static constexpr uint8_t TEMP_OFFS = BMI270_REG_TEMP_0 - BMI270_REG_DATA_8;
203 int16_t raw_t = (int16_t) ((raw_data[TEMP_OFFS + 1] << 8) | raw_data[TEMP_OFFS + 0]);
204 float temperature = (raw_t / 512.0f) + 23.0f;
205 this->temperature_callback_.call(temperature);
206 return true;
207}
208
209} // namespace esphome::bmi270
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:272
LazyCallbackManager< void(float)> temperature_callback_
Definition bmi270.h:105
bool update_data(motion::MotionData &data) override
Definition bmi270.cpp:162
BMI270AccelRange accel_range_
Definition bmi270.h:100
BMI270GyroRange gyro_range_
Definition bmi270.h:102
ErrorCode write_register(uint8_t a_register, const uint8_t *data, size_t len) const
writes an array of bytes to a specific register in the I²C device
Definition i2c.cpp:34
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
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:217
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
T clamp_at_most(T value, U max)
Definition helpers.h:2191
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint16_t temperature
Definition sun_gtil2.cpp:12