ESPHome  2024.3.2
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 
5 namespace esphome {
6 namespace bmi160 {
7 
8 static const char *const TAG = "bmi160";
9 
10 const uint8_t BMI160_REGISTER_CHIPID = 0x00;
11 
12 const uint8_t BMI160_REGISTER_CMD = 0x7E;
13 enum class Cmd : uint8_t {
14  START_FOC = 0x03,
15  ACCL_SET_PMU_MODE = 0b00010000, // last 2 bits are mode
16  GYRO_SET_PMU_MODE = 0b00010100, // last 2 bits are mode
17  MAG_SET_PMU_MODE = 0b00011000, // last 2 bits are mode
18  PROG_NVM = 0xA0,
19  FIFO_FLUSH = 0xB0,
20  INT_RESET = 0xB1,
21  SOFT_RESET = 0xB6,
22  STEP_CNT_CLR = 0xB2,
23 };
24 enum class GyroPmuMode : uint8_t {
25  SUSPEND = 0b00,
26  NORMAL = 0b01,
27  LOW_POWER = 0b10,
28 };
29 enum class AcclPmuMode : uint8_t {
30  SUSPEND = 0b00,
31  NORMAL = 0b01,
32  FAST_STARTUP = 0b11,
33 };
34 enum class MagPmuMode : uint8_t {
35  SUSPEND = 0b00,
36  NORMAL = 0b01,
37  LOW_POWER = 0b10,
38 };
39 
40 const uint8_t BMI160_REGISTER_ACCEL_CONFIG = 0x40;
41 enum class AcclFilterMode : uint8_t {
42  POWER_SAVING = 0b00000000,
43  PERF = 0b10000000,
44 };
45 enum class AcclBandwidth : uint8_t {
46  OSR4_AVG1 = 0b00000000,
47  OSR2_AVG2 = 0b00010000,
48  NORMAL_AVG4 = 0b00100000,
49  RES_AVG8 = 0b00110000,
50  RES_AVG16 = 0b01000000,
51  RES_AVG32 = 0b01010000,
52  RES_AVG64 = 0b01100000,
53  RES_AVG128 = 0b01110000,
54 };
55 enum class AccelOutputDataRate : uint8_t {
56  HZ_25_32 = 0b0001, // 25/32 Hz
57  HZ_25_16 = 0b0010, // 25/16 Hz
58  HZ_25_8 = 0b0011, // 25/8 Hz
59  HZ_25_4 = 0b0100, // 25/4 Hz
60  HZ_25_2 = 0b0101, // 25/2 Hz
61  HZ_25 = 0b0110, // 25 Hz
62  HZ_50 = 0b0111, // 50 Hz
63  HZ_100 = 0b1000, // 100 Hz
64  HZ_200 = 0b1001, // 200 Hz
65  HZ_400 = 0b1010, // 400 Hz
66  HZ_800 = 0b1011, // 800 Hz
67  HZ_1600 = 0b1100, // 1600 Hz
68 };
69 const uint8_t BMI160_REGISTER_ACCEL_RANGE = 0x41;
70 enum class AccelRange : uint8_t {
71  RANGE_2G = 0b0011,
72  RANGE_4G = 0b0101,
73  RANGE_8G = 0b1000,
74  RANGE_16G = 0b1100,
75 };
76 
77 const uint8_t BMI160_REGISTER_GYRO_CONFIG = 0x42;
78 enum class GyroBandwidth : uint8_t {
79  OSR4 = 0x00,
80  OSR2 = 0x10,
81  NORMAL = 0x20,
82 };
83 enum class GyroOuputDataRate : uint8_t {
84  HZ_25 = 0x06,
85  HZ_50 = 0x07,
86  HZ_100 = 0x08,
87  HZ_200 = 0x09,
88  HZ_400 = 0x0A,
89  HZ_800 = 0x0B,
90  HZ_1600 = 0x0C,
91  HZ_3200 = 0x0D,
92 };
93 const uint8_t BMI160_REGISTER_GYRO_RANGE = 0x43;
94 enum class GyroRange : uint8_t {
95  RANGE_2000_DPS = 0x0, // ±2000 °/s
96  RANGE_1000_DPS = 0x1,
97  RANGE_500_DPS = 0x2,
98  RANGE_250_DPS = 0x3,
99  RANGE_125_DPS = 0x4,
100 };
101 
102 const uint8_t BMI160_REGISTER_DATA_GYRO_X_LSB = 0x0C;
103 const uint8_t BMI160_REGISTER_DATA_GYRO_X_MSB = 0x0D;
104 const uint8_t BMI160_REGISTER_DATA_GYRO_Y_LSB = 0x0E;
105 const uint8_t BMI160_REGISTER_DATA_GYRO_Y_MSB = 0x0F;
106 const uint8_t BMI160_REGISTER_DATA_GYRO_Z_LSB = 0x10;
107 const uint8_t BMI160_REGISTER_DATA_GYRO_Z_MSB = 0x11;
108 const uint8_t BMI160_REGISTER_DATA_ACCEL_X_LSB = 0x12;
109 const uint8_t BMI160_REGISTER_DATA_ACCEL_X_MSB = 0x13;
110 const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_LSB = 0x14;
111 const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_MSB = 0x15;
112 const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_LSB = 0x16;
113 const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_MSB = 0x17;
114 const uint8_t BMI160_REGISTER_DATA_TEMP_LSB = 0x20;
115 const uint8_t BMI160_REGISTER_DATA_TEMP_MSB = 0x21;
116 
117 const float GRAVITY_EARTH = 9.80665f;
118 
120  switch (stage) {
121  case 0:
122  ESP_LOGCONFIG(TAG, "Setting up BMI160...");
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(10, [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 
187 void BMI160Component::setup() { this->internal_setup_(0); }
189  ESP_LOGCONFIG(TAG, "BMI160:");
190  LOG_I2C_DEVICE(this);
191  if (this->is_failed()) {
192  ESP_LOGE(TAG, "Communication with BMI160 failed!");
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 
204 i2c::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, true);
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 }
268 
269 } // namespace bmi160
270 } // namespace esphome
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:19
float get_setup_priority() const override
Definition: bmi160.cpp:267
const uint8_t BMI160_REGISTER_ACCEL_CONFIG
Definition: bmi160.cpp:40
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_MSB
Definition: bmi160.cpp:113
const uint8_t BMI160_REGISTER_CMD
Definition: bmi160.cpp:12
const uint8_t BMI160_REGISTER_DATA_TEMP_MSB
Definition: bmi160.cpp:115
const uint8_t BMI160_REGISTER_CHIPID
Definition: bmi160.cpp:10
float temperature
Definition: qmp6988.h:71
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_LSB
Definition: bmi160.cpp:108
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_LSB
Definition: bmi160.cpp:104
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_LSB
Definition: bmi160.cpp:106
const uint8_t BMI160_REGISTER_DATA_ACCEL_Z_LSB
Definition: bmi160.cpp:112
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
const uint8_t BMI160_REGISTER_DATA_GYRO_Z_MSB
Definition: bmi160.cpp:107
No error found during execution of method.
Definition: i2c_bus.h:13
const uint8_t BMI160_REGISTER_DATA_ACCEL_X_MSB
Definition: bmi160.cpp:109
const uint8_t BMI160_REGISTER_GYRO_CONFIG
Definition: bmi160.cpp:77
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_LSB
Definition: bmi160.cpp:110
const uint8_t BMI160_REGISTER_DATA_GYRO_X_MSB
Definition: bmi160.cpp:103
const uint8_t BMI160_REGISTER_DATA_TEMP_LSB
Definition: bmi160.cpp:114
const uint8_t BMI160_REGISTER_DATA_GYRO_Y_MSB
Definition: bmi160.cpp:105
std::string size_t len
Definition: helpers.h:292
const uint8_t BMI160_REGISTER_DATA_ACCEL_Y_MSB
Definition: bmi160.cpp:111
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void internal_setup_(int stage)
Definition: bmi160.cpp:119
const float GRAVITY_EARTH
Definition: bmi160.cpp:117
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition: i2c_bus.h:11
const uint8_t BMI160_REGISTER_ACCEL_RANGE
Definition: bmi160.cpp:69
const uint8_t BMI160_REGISTER_DATA_GYRO_X_LSB
Definition: bmi160.cpp:102
const uint8_t BMI160_REGISTER_GYRO_RANGE
Definition: bmi160.cpp:93