ESPHome 2026.3.2
Loading...
Searching...
No Matches
bmi160.cpp
Go to the documentation of this file.
1#include "bmi160.h"
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
4
5namespace esphome {
6namespace bmi160 {
7
8static const char *const TAG = "bmi160";
9static constexpr uint32_t GYRO_WAKEUP_TIMEOUT_MS = 100;
10
11const uint8_t BMI160_REGISTER_CHIPID = 0x00;
12
13const uint8_t BMI160_REGISTER_CMD = 0x7E;
14enum class Cmd : uint8_t {
15 START_FOC = 0x03,
16 ACCL_SET_PMU_MODE = 0b00010000, // last 2 bits are mode
17 GYRO_SET_PMU_MODE = 0b00010100, // last 2 bits are mode
18 MAG_SET_PMU_MODE = 0b00011000, // last 2 bits are mode
19 PROG_NVM = 0xA0,
20 FIFO_FLUSH = 0xB0,
21 INT_RESET = 0xB1,
22 SOFT_RESET = 0xB6,
23 STEP_CNT_CLR = 0xB2,
24};
25enum class GyroPmuMode : uint8_t {
26 SUSPEND = 0b00,
27 NORMAL = 0b01,
28 LOW_POWER = 0b10,
29};
30enum class AcclPmuMode : uint8_t {
31 SUSPEND = 0b00,
32 NORMAL = 0b01,
33 FAST_STARTUP = 0b11,
34};
35enum class MagPmuMode : uint8_t {
36 SUSPEND = 0b00,
37 NORMAL = 0b01,
38 LOW_POWER = 0b10,
39};
40
41const uint8_t BMI160_REGISTER_ACCEL_CONFIG = 0x40;
42enum class AcclFilterMode : uint8_t {
43 POWER_SAVING = 0b00000000,
44 PERF = 0b10000000,
45};
46enum class AcclBandwidth : uint8_t {
47 OSR4_AVG1 = 0b00000000,
48 OSR2_AVG2 = 0b00010000,
49 NORMAL_AVG4 = 0b00100000,
50 RES_AVG8 = 0b00110000,
51 RES_AVG16 = 0b01000000,
52 RES_AVG32 = 0b01010000,
53 RES_AVG64 = 0b01100000,
54 RES_AVG128 = 0b01110000,
55};
56enum class AccelOutputDataRate : uint8_t {
57 HZ_25_32 = 0b0001, // 25/32 Hz
58 HZ_25_16 = 0b0010, // 25/16 Hz
59 HZ_25_8 = 0b0011, // 25/8 Hz
60 HZ_25_4 = 0b0100, // 25/4 Hz
61 HZ_25_2 = 0b0101, // 25/2 Hz
62 HZ_25 = 0b0110, // 25 Hz
63 HZ_50 = 0b0111, // 50 Hz
64 HZ_100 = 0b1000, // 100 Hz
65 HZ_200 = 0b1001, // 200 Hz
66 HZ_400 = 0b1010, // 400 Hz
67 HZ_800 = 0b1011, // 800 Hz
68 HZ_1600 = 0b1100, // 1600 Hz
69};
70const uint8_t BMI160_REGISTER_ACCEL_RANGE = 0x41;
71enum class AccelRange : uint8_t {
72 RANGE_2G = 0b0011,
73 RANGE_4G = 0b0101,
74 RANGE_8G = 0b1000,
75 RANGE_16G = 0b1100,
76};
77
78const uint8_t BMI160_REGISTER_GYRO_CONFIG = 0x42;
79enum class GyroBandwidth : uint8_t {
80 OSR4 = 0x00,
81 OSR2 = 0x10,
82 NORMAL = 0x20,
83};
84enum class GyroOuputDataRate : uint8_t {
85 HZ_25 = 0x06,
86 HZ_50 = 0x07,
87 HZ_100 = 0x08,
88 HZ_200 = 0x09,
89 HZ_400 = 0x0A,
90 HZ_800 = 0x0B,
91 HZ_1600 = 0x0C,
92 HZ_3200 = 0x0D,
93};
94const uint8_t BMI160_REGISTER_GYRO_RANGE = 0x43;
95enum class GyroRange : uint8_t {
96 RANGE_2000_DPS = 0x0, // ±2000 °/s
97 RANGE_1000_DPS = 0x1,
98 RANGE_500_DPS = 0x2,
99 RANGE_250_DPS = 0x3,
100 RANGE_125_DPS = 0x4,
101};
102
115const uint8_t BMI160_REGISTER_DATA_TEMP_LSB = 0x20;
116const uint8_t BMI160_REGISTER_DATA_TEMP_MSB = 0x21;
117
118const float GRAVITY_EARTH = 9.80665f;
119
121 switch (stage) {
122 case 0:
123 uint8_t chipid;
124 if (!this->read_byte(BMI160_REGISTER_CHIPID, &chipid) || (chipid != 0b11010001)) {
125 this->mark_failed();
126 return;
127 }
128
129 ESP_LOGV(TAG, " Bringing accelerometer out of sleep");
130 if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::ACCL_SET_PMU_MODE | (uint8_t) AcclPmuMode::NORMAL)) {
131 this->mark_failed();
132 return;
133 }
134 ESP_LOGV(TAG, " Waiting for accelerometer to wake up");
135 // need to wait (max delay in datasheet) because we can't send commands while another is in progress
136 // min 5ms, 10ms
137 this->set_timeout(10, [this]() { this->internal_setup_(1); });
138 break;
139
140 case 1:
141 ESP_LOGV(TAG, " Bringing gyroscope out of sleep");
142 if (!this->write_byte(BMI160_REGISTER_CMD, (uint8_t) Cmd::GYRO_SET_PMU_MODE | (uint8_t) GyroPmuMode::NORMAL)) {
143 this->mark_failed();
144 return;
145 }
146 ESP_LOGV(TAG, " Waiting for gyroscope to wake up");
147 // wait between 51 & 81ms, doing 100 to be safe
148 this->set_timeout(GYRO_WAKEUP_TIMEOUT_MS, [this]() { this->internal_setup_(2); });
149 break;
150
151 case 2:
152 ESP_LOGV(TAG, " Setting up Gyro Config");
153 uint8_t gyro_config = (uint8_t) GyroBandwidth::OSR4 | (uint8_t) GyroOuputDataRate::HZ_25;
154 ESP_LOGV(TAG, " Output gyro_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_config));
155 if (!this->write_byte(BMI160_REGISTER_GYRO_CONFIG, gyro_config)) {
156 this->mark_failed();
157 return;
158 }
159 ESP_LOGV(TAG, " Setting up Gyro Range");
160 uint8_t gyro_range = (uint8_t) GyroRange::RANGE_2000_DPS;
161 ESP_LOGV(TAG, " Output gyro_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(gyro_range));
162 if (!this->write_byte(BMI160_REGISTER_GYRO_RANGE, gyro_range)) {
163 this->mark_failed();
164 return;
165 }
166
167 ESP_LOGV(TAG, " Setting up Accel Config");
168 uint8_t accel_config =
170 ESP_LOGV(TAG, " Output accel_config: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_config));
171 if (!this->write_byte(BMI160_REGISTER_ACCEL_CONFIG, accel_config)) {
172 this->mark_failed();
173 return;
174 }
175 ESP_LOGV(TAG, " Setting up Accel Range");
176 uint8_t accel_range = (uint8_t) AccelRange::RANGE_16G;
177 ESP_LOGV(TAG, " Output accel_range: 0b" BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(accel_range));
178 if (!this->write_byte(BMI160_REGISTER_ACCEL_RANGE, accel_range)) {
179 this->mark_failed();
180 return;
181 }
182
183 this->setup_complete_ = true;
184 }
185}
186
189 ESP_LOGCONFIG(TAG, "BMI160:");
190 LOG_I2C_DEVICE(this);
191 if (this->is_failed()) {
192 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
193 }
194 LOG_UPDATE_INTERVAL(this);
195 LOG_SENSOR(" ", "Acceleration X", this->accel_x_sensor_);
196 LOG_SENSOR(" ", "Acceleration Y", this->accel_y_sensor_);
197 LOG_SENSOR(" ", "Acceleration Z", this->accel_z_sensor_);
198 LOG_SENSOR(" ", "Gyro X", this->gyro_x_sensor_);
199 LOG_SENSOR(" ", "Gyro Y", this->gyro_y_sensor_);
200 LOG_SENSOR(" ", "Gyro Z", this->gyro_z_sensor_);
201 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
202}
203
204i2c::ErrorCode BMI160Component::read_le_int16_(uint8_t reg, int16_t *value, uint8_t len) {
205 uint8_t raw_data[len * 2];
206 // read using read_register because we have little-endian data, and read_bytes_16 will swap it
207 i2c::ErrorCode err = this->read_register(reg, raw_data, len * 2);
208 if (err != i2c::ERROR_OK) {
209 return err;
210 }
211 for (int i = 0; i < len; i++) {
212 value[i] = (int16_t) ((uint16_t) raw_data[i * 2] | ((uint16_t) raw_data[i * 2 + 1] << 8));
213 }
214 return err;
215}
216
218 if (!this->setup_complete_) {
219 return;
220 }
221
222 ESP_LOGV(TAG, " Updating BMI160");
223 int16_t data[6];
224 if (this->read_le_int16_(BMI160_REGISTER_DATA_GYRO_X_LSB, data, 6) != i2c::ERROR_OK) {
225 this->status_set_warning();
226 return;
227 }
228
229 float gyro_x = (float) data[0] / (float) INT16_MAX * 2000.f;
230 float gyro_y = (float) data[1] / (float) INT16_MAX * 2000.f;
231 float gyro_z = (float) data[2] / (float) INT16_MAX * 2000.f;
232 float accel_x = (float) data[3] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
233 float accel_y = (float) data[4] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
234 float accel_z = (float) data[5] / (float) INT16_MAX * 16 * GRAVITY_EARTH;
235
236 int16_t raw_temperature;
237 if (this->read_le_int16_(BMI160_REGISTER_DATA_TEMP_LSB, &raw_temperature, 1) != i2c::ERROR_OK) {
238 this->status_set_warning();
239 return;
240 }
241 float temperature = (float) raw_temperature / (float) INT16_MAX * 64.5f + 23.f;
242
243 ESP_LOGD(TAG,
244 "Got accel={x=%.3f m/s², y=%.3f m/s², z=%.3f m/s²}, "
245 "gyro={x=%.3f °/s, y=%.3f °/s, z=%.3f °/s}, temp=%.3f°C",
246 accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z, temperature);
247
248 if (this->accel_x_sensor_ != nullptr)
249 this->accel_x_sensor_->publish_state(accel_x);
250 if (this->accel_y_sensor_ != nullptr)
251 this->accel_y_sensor_->publish_state(accel_y);
252 if (this->accel_z_sensor_ != nullptr)
253 this->accel_z_sensor_->publish_state(accel_z);
254
255 if (this->temperature_sensor_ != nullptr)
256 this->temperature_sensor_->publish_state(temperature);
257
258 if (this->gyro_x_sensor_ != nullptr)
259 this->gyro_x_sensor_->publish_state(gyro_x);
260 if (this->gyro_y_sensor_ != nullptr)
261 this->gyro_y_sensor_->publish_state(gyro_y);
262 if (this->gyro_z_sensor_ != nullptr)
263 this->gyro_z_sensor_->publish_state(gyro_z);
264
265 this->status_clear_warning();
266}
267
268} // namespace bmi160
269} // namespace esphome
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:233
void status_set_warning(const char *message=nullptr)
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:451
void status_clear_warning()
Definition component.h:254
sensor::Sensor * accel_z_sensor_
Definition bmi160.h:28
i2c::ErrorCode read_le_int16_(uint8_t reg, int16_t *value, uint8_t len)
reads len 16-bit little-endian integers from the given i2c register
Definition bmi160.cpp:204
sensor::Sensor * gyro_y_sensor_
Definition bmi160.h:31
sensor::Sensor * gyro_x_sensor_
Definition bmi160.h:30
sensor::Sensor * accel_x_sensor_
Definition bmi160.h:26
sensor::Sensor * gyro_z_sensor_
Definition bmi160.h:32
void internal_setup_(int stage)
Definition bmi160.cpp:120
sensor::Sensor * temperature_sensor_
Definition bmi160.h:29
sensor::Sensor * accel_y_sensor_
Definition bmi160.h:27
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
ErrorCode read_register(uint8_t a_register, uint8_t *data, size_t len)
reads an array of bytes from a specific register in the I²C device
Definition i2c.cpp:25
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_LSB
Definition bmi160.cpp:109
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_MSB
Definition bmi160.cpp:114
const uint8_t BMI160_REGISTER_DATA_TEMP_MSB
Definition bmi160.cpp:116
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_MSB
Definition bmi160.cpp:110
const uint8_t BMI160_REGISTER_GYRO_RANGE
Definition bmi160.cpp:94
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_LSB
Definition bmi160.cpp:113
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_LSB
Definition bmi160.cpp:105
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_LSB
Definition bmi160.cpp:107
const uint8_t BMI160_REGISTER_ACCEL_RANGE
Definition bmi160.cpp:70
const uint8_t BMI160_REGISTER_DATA_TEMP_LSB
Definition bmi160.cpp:115
const uint8_t BMI160_REGISTER_DATA_GYRO_X_LSB
Definition bmi160.cpp:103
const uint8_t BMI160_REGISTER_GYRO_CONFIG
Definition bmi160.cpp:78
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_MSB
Definition bmi160.cpp:108
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_LSB
Definition bmi160.cpp:111
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_MSB
Definition bmi160.cpp:106
const uint8_t BMI160_REGISTER_CMD
Definition bmi160.cpp:13
const uint8_t BMI160_REGISTER_CHIPID
Definition bmi160.cpp:11
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_MSB
Definition bmi160.cpp:112
const uint8_t BMI160_REGISTER_DATA_GYRO_X_MSB
Definition bmi160.cpp:104
const float GRAVITY_EARTH
Definition bmi160.cpp:118
const uint8_t BMI160_REGISTER_ACCEL_CONFIG
Definition bmi160.cpp:41
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:12
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:14
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:892
static void uint32_t
uint16_t temperature
Definition sun_gtil2.cpp:12