mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 02:02:51 +00:00
[bmi270] Add optional BMM150 magnetometer support via aux interface (#18436)
Co-authored-by: J. Nick Koston <nick@home-assistant.io> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
54fdeb7b1b
commit
81c275777d
@@ -5,12 +5,16 @@
|
||||
|
||||
namespace esphome::bmi270 {
|
||||
|
||||
#ifdef USE_BMI270_BMM150
|
||||
using namespace bmm150; // NOLINT - sole consumer of the BMM150 aux-device definitions
|
||||
#endif
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "bmi270");
|
||||
|
||||
#if defined(USE_ARDUINO) && !defined(USE_ESP32)
|
||||
static const size_t MAX_I2C_BUFFER_SIZE = 32;
|
||||
static constexpr size_t MAX_I2C_BUFFER_SIZE = 32;
|
||||
#else
|
||||
static const size_t MAX_I2C_BUFFER_SIZE = 256;
|
||||
static constexpr size_t MAX_I2C_BUFFER_SIZE = 256;
|
||||
#endif
|
||||
|
||||
// Configuration blob upload
|
||||
@@ -19,12 +23,12 @@ static const size_t MAX_I2C_BUFFER_SIZE = 256;
|
||||
|
||||
bool BMI270Component::load_config_file_() {
|
||||
// 1. Disable advanced power-save so the config port is accessible
|
||||
if (!this->write_byte(BMI270_REG_PWR_CONF, 0x00))
|
||||
if (!this->write_byte(BMI270_REG_PWR_CONF, BMI270_PWR_CONF_ADV_POWER_SAVE_OFF))
|
||||
return false;
|
||||
delay(1);
|
||||
|
||||
// 2. Prepare config load: write 0x00 to INIT_CTRL to start
|
||||
if (!this->write_byte(BMI270_REG_INIT_CTRL, 0x00))
|
||||
if (!this->write_byte(BMI270_REG_INIT_CTRL, BMI270_INIT_CTRL_LOAD_START))
|
||||
return false;
|
||||
|
||||
// 3. Burst-write the config in pages
|
||||
@@ -50,7 +54,7 @@ bool BMI270Component::load_config_file_() {
|
||||
}
|
||||
|
||||
// 4. Signal end of config load
|
||||
if (!this->write_byte(BMI270_REG_INIT_CTRL, 0x01))
|
||||
if (!this->write_byte(BMI270_REG_INIT_CTRL, BMI270_INIT_CTRL_LOAD_DONE))
|
||||
return false;
|
||||
delay(20); // spec: wait ≥20 ms for init to complete
|
||||
|
||||
@@ -58,7 +62,7 @@ bool BMI270Component::load_config_file_() {
|
||||
uint8_t status = 0;
|
||||
if (!this->read_byte(BMI270_REG_INTERNAL_STATUS, &status))
|
||||
return false;
|
||||
if ((status & 0x0F) != 0x01) {
|
||||
if ((status & BMI270_INTERNAL_STATUS_MASK) != BMI270_INTERNAL_STATUS_INIT_OK) {
|
||||
ESP_LOGE(TAG, "Config load failed: INTERNAL_STATUS=0x%02X (expected 0x01)", status);
|
||||
return false;
|
||||
}
|
||||
@@ -84,7 +88,7 @@ void BMI270Component::setup() {
|
||||
ESP_LOGD(TAG, "Chip ID: 0x%02X", chip_id);
|
||||
|
||||
// 2. Soft-reset via CMD register (0x7E = 0xB6)
|
||||
if (!this->write_byte(0x7E, 0xB6)) {
|
||||
if (!this->write_byte(BMI270_REG_CMD, BMI270_CMD_SOFT_RESET)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
@@ -100,7 +104,8 @@ void BMI270Component::setup() {
|
||||
|
||||
// 5. Configure accelerometer
|
||||
// ACC_CONF: ODR | BWP(0x2 = normal avg4) | perf_mode(1)
|
||||
uint8_t acc_conf = (uint8_t) (accel_odr_) | (0x2 << 4) | (1 << 7);
|
||||
uint8_t acc_conf =
|
||||
(uint8_t) (accel_odr_) | (BMI270_CONF_BWP_NORMAL << BMI270_CONF_BWP_SHIFT) | BMI270_ACC_CONF_PERF_MODE;
|
||||
if (!this->write_byte(BMI270_REG_ACC_CONF, acc_conf)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
@@ -112,7 +117,8 @@ void BMI270Component::setup() {
|
||||
|
||||
// 6. Configure gyroscope
|
||||
// GYR_CONF: ODR | BWP(0x2 = normal) | noise_perf(1) | filter_perf(1)
|
||||
uint8_t gyr_conf = (uint8_t) (gyro_odr_) | (0x2 << 4) | (1 << 6) | (1 << 7);
|
||||
uint8_t gyr_conf = (uint8_t) (gyro_odr_) | (BMI270_CONF_BWP_NORMAL << BMI270_CONF_BWP_SHIFT) |
|
||||
BMI270_GYR_CONF_NOISE_PERF | BMI270_GYR_CONF_FILTER_PERF;
|
||||
if (!this->write_byte(BMI270_REG_GYR_CONF, gyr_conf)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
@@ -124,7 +130,7 @@ void BMI270Component::setup() {
|
||||
|
||||
// 7. Enable accelerometer, gyroscope, and temperature sensor
|
||||
// PWR_CTRL bits: temp_en[3] | gyr_en[2] | acc_en[1]
|
||||
if (!this->write_byte(BMI270_REG_PWR_CTRL, 0x0E)) {
|
||||
if (!this->write_byte(BMI270_REG_PWR_CTRL, BMI270_PWR_CTRL_IMU)) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
@@ -132,14 +138,133 @@ void BMI270Component::setup() {
|
||||
|
||||
// 8. Re-enable advanced power save (optional; keeps current low between reads)
|
||||
// Disabled here for simplicity – leave in performance mode
|
||||
if (!this->write_byte(BMI270_REG_PWR_CONF, 0x02)) { // bit1 = fifo_self_wakeup
|
||||
if (!this->write_byte(BMI270_REG_PWR_CONF, BMI270_PWR_CONF_FIFO_SELF_WAKEUP)) { // bit1 = fifo_self_wakeup
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// 9. Optionally bring up whatever is wired to the aux interface.
|
||||
// This is entirely additive: it is off by default, and failure only disables
|
||||
// magnetometer reporting rather than the whole component.
|
||||
#ifdef USE_BMI270_BMM150
|
||||
if (this->aux_device_ == BMI270_AUX_DEVICE_BMM150) {
|
||||
this->magnetometer_ready_ = this->setup_magnetometer_();
|
||||
if (!this->magnetometer_ready_) {
|
||||
ESP_LOGW(TAG, "BMM150 magnetometer setup failed; continuing without it");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_LOGCONFIG(TAG, "BMI270 initialised successfully");
|
||||
}
|
||||
|
||||
#ifdef USE_BMI270_BMM150
|
||||
bool BMI270Component::wait_for_aux_idle_(uint32_t timeout_ms) {
|
||||
// The STATUS aux-busy bit is set while an aux transaction is in flight. Aux transaction
|
||||
// duration depends on the attached device's own timing, so poll with a bounded
|
||||
// timeout instead of guessing a fixed delay.
|
||||
uint32_t start = millis();
|
||||
while (true) {
|
||||
uint8_t status = 0;
|
||||
if (!this->read_byte(BMI270_REG_STATUS, &status))
|
||||
return false;
|
||||
if ((status & BMI270_STATUS_AUX_BUSY) == 0)
|
||||
return true;
|
||||
if (millis() - start >= timeout_ms)
|
||||
return false;
|
||||
delay(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool BMI270Component::write_aux_register_(uint8_t reg, uint8_t value) {
|
||||
if (!this->write_byte(BMI270_REG_AUX_WR_DATA, value))
|
||||
return false;
|
||||
if (!this->write_byte(BMI270_REG_AUX_WR_ADDR, reg))
|
||||
return false;
|
||||
return this->wait_for_aux_idle_(20);
|
||||
}
|
||||
|
||||
bool BMI270Component::read_aux_block_(uint8_t reg, uint8_t *buf) {
|
||||
if (!this->write_byte(BMI270_REG_AUX_RD_ADDR, reg))
|
||||
return false;
|
||||
if (!this->wait_for_aux_idle_(20))
|
||||
return false;
|
||||
return this->read_bytes(BMI270_REG_AUX_X_LSB, buf, BMI270_AUX_DATA_LEN);
|
||||
}
|
||||
|
||||
bool BMI270Component::read_magnetometer_trim_() {
|
||||
uint8_t regs[BMM150_TRIM_BLOCKS * BMM150_TRIM_BLOCK_LEN];
|
||||
for (uint8_t i = 0; i < BMM150_TRIM_BLOCKS; i++) {
|
||||
if (!this->read_aux_block_(BMM150_REG_TRIM_START + i * BMM150_TRIM_BLOCK_LEN, regs + i * BMM150_TRIM_BLOCK_LEN))
|
||||
return false;
|
||||
}
|
||||
this->mag_trim_ = bmm150_parse_trim(regs);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BMI270Component::setup_magnetometer_() {
|
||||
// Sequence adapted from the Bosch BMI270 aux-interface protocol: enable the secondary
|
||||
// I2C master in manual mode, point it at the BMM150's address, power the BMM150 up
|
||||
// and soft-reset it, verify its chip ID, read its factory trim values, put it in
|
||||
// normal/30Hz mode, then switch the aux interface to auto-burst mode so BMI270 keeps
|
||||
// AUX_X/Y/Z/R refreshed from the BMM150's data registers.
|
||||
ESP_LOGD(TAG, "Setting up BMM150 magnetometer via BMI270 aux interface...");
|
||||
|
||||
if (!this->write_byte(BMI270_REG_IF_CONF, BMI270_IF_CONF_AUX_IF_EN))
|
||||
return false;
|
||||
if (!this->write_byte(BMI270_REG_AUX_IF_CONF, BMI270_AUX_IF_CONF_MANUAL_EN | BMI270_AUX_IF_CONF_MAN_RD_BURST_8))
|
||||
return false;
|
||||
if (!this->write_byte(BMI270_REG_AUX_DEV_ID, (uint8_t) (this->aux_device_address_ << 1)))
|
||||
return false;
|
||||
|
||||
// Suspend -> sleep, then a soft reset so the BMM150 starts from a known state even if it
|
||||
// stayed powered across a restart. The chip ID is only readable once the power bit is set.
|
||||
if (!this->write_aux_register_(BMM150_REG_POWER_CONTROL, BMM150_CMD_POWER_ON)) {
|
||||
ESP_LOGW(TAG, "BMM150 power-on write timed out");
|
||||
return false;
|
||||
}
|
||||
delay(BMM150_START_UP_TIME_MS);
|
||||
if (!this->write_aux_register_(BMM150_REG_POWER_CONTROL, BMM150_CMD_SOFT_RESET)) {
|
||||
ESP_LOGW(TAG, "BMM150 soft-reset write timed out");
|
||||
return false;
|
||||
}
|
||||
delay(BMM150_SOFT_RESET_TIME_MS);
|
||||
|
||||
uint8_t chip_id[BMI270_AUX_DATA_LEN];
|
||||
if (!this->read_aux_block_(BMM150_REG_CHIP_ID, chip_id)) {
|
||||
ESP_LOGW(TAG, "BMM150 chip-ID read failed");
|
||||
return false;
|
||||
}
|
||||
if (chip_id[0] != BMM150_CHIP_ID_VALUE) {
|
||||
ESP_LOGW(TAG, "BMM150 not detected (chip ID 0x%02X, expected 0x%02X)", chip_id[0], BMM150_CHIP_ID_VALUE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->read_magnetometer_trim_()) {
|
||||
ESP_LOGW(TAG, "BMM150 trim data read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->write_aux_register_(BMM150_REG_OP_MODE, BMM150_CMD_NORMAL_MODE_ODR_30HZ)) {
|
||||
ESP_LOGW(TAG, "BMM150 mode write timed out");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Switch to auto-burst mode: BMI270 will keep polling 8 bytes from the BMM150's
|
||||
// data registers (0x42) into its own AUX_X/Y/Z/R registers automatically.
|
||||
if (!this->write_byte(BMI270_REG_AUX_IF_CONF, BMI270_AUX_IF_CONF_FCU_WRITE_EN | BMI270_AUX_IF_CONF_MAN_RD_BURST_8 |
|
||||
BMI270_AUX_IF_CONF_RD_BURST_8))
|
||||
return false;
|
||||
if (!this->write_byte(BMI270_REG_AUX_RD_ADDR, BMM150_REG_DATA_X_LSB))
|
||||
return false;
|
||||
if (!this->write_byte(BMI270_REG_PWR_CTRL, BMI270_PWR_CTRL_IMU | BMI270_PWR_CTRL_AUX_EN))
|
||||
return false;
|
||||
|
||||
ESP_LOGD(TAG, "BMM150 magnetometer ready");
|
||||
return true;
|
||||
}
|
||||
#endif // USE_BMI270_BMM150
|
||||
|
||||
void BMI270Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BMI270 IMU:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
@@ -153,6 +278,12 @@ void BMI270Component::dump_config() {
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Accel range : %s", ACCEL_RANGE_STRS[accel_range_]);
|
||||
ESP_LOGCONFIG(TAG, " Gyro range : %s", GYRO_RANGE_STRS[gyro_range_]);
|
||||
#ifdef USE_BMI270_BMM150
|
||||
if (this->aux_device_ == BMI270_AUX_DEVICE_BMM150) {
|
||||
ESP_LOGCONFIG(TAG, " Aux device : BMM150 magnetometer (%s)",
|
||||
this->magnetometer_ready_ ? LOG_STR_LITERAL("ready") : LOG_STR_LITERAL("NOT DETECTED"));
|
||||
}
|
||||
#endif
|
||||
MotionComponent::dump_config();
|
||||
}
|
||||
|
||||
@@ -163,9 +294,22 @@ bool BMI270Component::update_data(motion::MotionData &data) {
|
||||
if (this->is_failed())
|
||||
return false;
|
||||
|
||||
// Accelerometer: registers 0x0C–0x11 (6 bytes: x_lsb, x_msb, y_lsb, y_msb, z_lsb, z_msb)
|
||||
uint8_t raw_data[REG_READ_LEN];
|
||||
if (!this->read_bytes(BMI270_REG_DATA_8, raw_data, REG_READ_LEN)) {
|
||||
// Accelerometer: registers 0x0C–0x11 (6 bytes: x_lsb, x_msb, y_lsb, y_msb, z_lsb, z_msb)
|
||||
// When the magnetometer is active, the 8 aux data bytes (0x04–0x0B) sit directly before
|
||||
// the accel data, so one burst read from AUX_X_LSB fetches both.
|
||||
#ifdef USE_BMI270_BMM150
|
||||
const bool read_mag = this->magnetometer_ready_ && !this->magnetometer_callback_.empty();
|
||||
uint8_t buf[BMI270_AUX_DATA_LEN + REG_READ_LEN];
|
||||
const uint8_t *mag_raw = buf;
|
||||
const uint8_t *raw_data = read_mag ? buf + BMI270_AUX_DATA_LEN : buf;
|
||||
const bool read_ok = read_mag ? this->read_bytes(BMI270_REG_AUX_X_LSB, buf, sizeof(buf))
|
||||
: this->read_bytes(BMI270_REG_DATA_8, buf, REG_READ_LEN);
|
||||
#else
|
||||
uint8_t buf[REG_READ_LEN];
|
||||
const uint8_t *raw_data = buf;
|
||||
const bool read_ok = this->read_bytes(BMI270_REG_DATA_8, buf, REG_READ_LEN);
|
||||
#endif
|
||||
if (!read_ok) {
|
||||
ESP_LOGW(TAG, "Failed to read IMU data");
|
||||
return false;
|
||||
}
|
||||
@@ -195,14 +339,21 @@ bool BMI270Component::update_data(motion::MotionData &data) {
|
||||
data.angular_rate[motion::Y_AXIS] = (int16_t) ((raw_data[GYR_OFFS + 3] << 8) | raw_data[GYR_OFFS + 2]) * scale;
|
||||
data.angular_rate[motion::Z_AXIS] = (int16_t) ((raw_data[GYR_OFFS + 5] << 8) | raw_data[GYR_OFFS + 4]) * scale;
|
||||
|
||||
if (this->temperature_callback_.empty())
|
||||
return true;
|
||||
// Temperature: registers 0x22–0x23
|
||||
// Formula from datasheet: T[°C] = raw / 512 + 23
|
||||
static constexpr uint8_t TEMP_OFFS = BMI270_REG_TEMP_0 - BMI270_REG_DATA_8;
|
||||
int16_t raw_t = (int16_t) ((raw_data[TEMP_OFFS + 1] << 8) | raw_data[TEMP_OFFS + 0]);
|
||||
float temperature = (raw_t / 512.0f) + 23.0f;
|
||||
this->temperature_callback_.call(temperature);
|
||||
if (!this->temperature_callback_.empty()) {
|
||||
// Temperature: registers 0x22–0x23
|
||||
// Formula from datasheet: T[°C] = raw / 512 + 23
|
||||
static constexpr uint8_t TEMP_OFFS = BMI270_REG_TEMP_0 - BMI270_REG_DATA_8;
|
||||
int16_t raw_t = (int16_t) ((raw_data[TEMP_OFFS + 1] << 8) | raw_data[TEMP_OFFS + 0]);
|
||||
float temperature = (raw_t / 512.0f) + 23.0f;
|
||||
this->temperature_callback_.call(temperature);
|
||||
}
|
||||
|
||||
#ifdef USE_BMI270_BMM150
|
||||
if (read_mag) {
|
||||
const BMM150Data mag_data = bmm150_convert(this->mag_trim_, mag_raw);
|
||||
this->magnetometer_callback_.call(mag_data);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,35 +4,81 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_BMI270_BMM150
|
||||
#include "bmm150_aux.h"
|
||||
#endif
|
||||
#include <functional>
|
||||
|
||||
namespace esphome::bmi270 {
|
||||
|
||||
// Register map
|
||||
static const uint8_t BMI270_REG_CHIP_ID = 0x00;
|
||||
static const uint8_t BMI270_REG_ERR_REG = 0x02;
|
||||
static const uint8_t BMI270_REG_STATUS = 0x03;
|
||||
static const uint8_t BMI270_REG_DATA_8 = 0x0C; // ACC_X LSB
|
||||
static const uint8_t BMI270_REG_DATA_14 = 0x12; // GYR_X LSB
|
||||
static const uint8_t BMI270_REG_TEMP_0 = 0x22;
|
||||
static const uint8_t BMI270_REG_TEMP_MSB = 0x23; // temperature (2 bytes big-endian ish)
|
||||
static constexpr uint8_t BMI270_REG_CHIP_ID = 0x00;
|
||||
static constexpr uint8_t BMI270_REG_ERR_REG = 0x02;
|
||||
static constexpr uint8_t BMI270_REG_STATUS = 0x03;
|
||||
static constexpr uint8_t BMI270_REG_DATA_8 = 0x0C; // ACC_X LSB
|
||||
static constexpr uint8_t BMI270_REG_DATA_14 = 0x12; // GYR_X LSB
|
||||
static constexpr uint8_t BMI270_REG_TEMP_0 = 0x22;
|
||||
static constexpr uint8_t BMI270_REG_TEMP_MSB = 0x23; // temperature (2 bytes big-endian ish)
|
||||
|
||||
static constexpr uint8_t REG_READ_LEN =
|
||||
BMI270_REG_TEMP_MSB - BMI270_REG_DATA_8 +
|
||||
1; // 0x23 - 0x0C + 1 = 0x18 bytes total for accel(6) + gyro(6) + temp(2) + padding(4)
|
||||
|
||||
static const uint8_t BMI270_REG_PWR_CONF = 0x7C;
|
||||
static const uint8_t BMI270_REG_PWR_CTRL = 0x7D;
|
||||
static const uint8_t BMI270_REG_INIT_CTRL = 0x59;
|
||||
static const uint8_t BMI270_REG_INIT_DATA = 0x5E;
|
||||
static const uint8_t BMI270_REG_INIT_ADDR_0 = 0x5B;
|
||||
static const uint8_t BMI270_REG_INTERNAL_STATUS = 0x21;
|
||||
static const uint8_t BMI270_REG_ACC_CONF = 0x40;
|
||||
static const uint8_t BMI270_REG_ACC_RANGE = 0x41;
|
||||
static const uint8_t BMI270_REG_GYR_CONF = 0x42;
|
||||
static const uint8_t BMI270_REG_GYR_RANGE = 0x43;
|
||||
static constexpr uint8_t BMI270_REG_PWR_CONF = 0x7C;
|
||||
static constexpr uint8_t BMI270_REG_PWR_CTRL = 0x7D;
|
||||
static constexpr uint8_t BMI270_REG_INIT_CTRL = 0x59;
|
||||
static constexpr uint8_t BMI270_REG_INIT_DATA = 0x5E;
|
||||
static constexpr uint8_t BMI270_REG_INIT_ADDR_0 = 0x5B;
|
||||
static constexpr uint8_t BMI270_REG_INTERNAL_STATUS = 0x21;
|
||||
static constexpr uint8_t BMI270_REG_ACC_CONF = 0x40;
|
||||
static constexpr uint8_t BMI270_REG_ACC_RANGE = 0x41;
|
||||
static constexpr uint8_t BMI270_REG_GYR_CONF = 0x42;
|
||||
static constexpr uint8_t BMI270_REG_GYR_RANGE = 0x43;
|
||||
|
||||
static const uint8_t BMI270_CHIP_ID_VALUE = 0x24;
|
||||
static constexpr uint8_t BMI270_CHIP_ID_VALUE = 0x24;
|
||||
|
||||
// Auxiliary (secondary I2C master) interface registers.
|
||||
// Used to optionally drive a BMM150 magnetometer wired to the BMI270's AUX pins
|
||||
// instead of the main I2C bus - the BMM150 has no address of its own on that bus.
|
||||
static constexpr uint8_t BMI270_REG_AUX_X_LSB = 0x04; // start of aux data: X,Y,Z,R (8 bytes)
|
||||
static constexpr uint8_t BMI270_REG_AUX_DEV_ID = 0x4B;
|
||||
static constexpr uint8_t BMI270_REG_AUX_IF_CONF = 0x4C;
|
||||
static constexpr uint8_t BMI270_REG_AUX_RD_ADDR = 0x4D;
|
||||
static constexpr uint8_t BMI270_REG_AUX_WR_ADDR = 0x4E;
|
||||
static constexpr uint8_t BMI270_REG_AUX_WR_DATA = 0x4F;
|
||||
static constexpr uint8_t BMI270_REG_IF_CONF = 0x6B;
|
||||
static constexpr uint8_t BMI270_REG_CMD = 0x7E;
|
||||
|
||||
// Register values and bitfields
|
||||
static constexpr uint8_t BMI270_CMD_SOFT_RESET = 0xB6;
|
||||
static constexpr uint8_t BMI270_PWR_CONF_ADV_POWER_SAVE_OFF = 0x00;
|
||||
static constexpr uint8_t BMI270_PWR_CONF_FIFO_SELF_WAKEUP = 0x02;
|
||||
static constexpr uint8_t BMI270_INIT_CTRL_LOAD_START = 0x00;
|
||||
static constexpr uint8_t BMI270_INIT_CTRL_LOAD_DONE = 0x01;
|
||||
static constexpr uint8_t BMI270_INTERNAL_STATUS_MASK = 0x0F;
|
||||
static constexpr uint8_t BMI270_INTERNAL_STATUS_INIT_OK = 0x01;
|
||||
static constexpr uint8_t BMI270_CONF_BWP_NORMAL = 0x02; // filter bandwidth, bits 5:4
|
||||
static constexpr uint8_t BMI270_CONF_BWP_SHIFT = 4;
|
||||
static constexpr uint8_t BMI270_ACC_CONF_PERF_MODE = 0x80;
|
||||
static constexpr uint8_t BMI270_GYR_CONF_NOISE_PERF = 0x40;
|
||||
static constexpr uint8_t BMI270_GYR_CONF_FILTER_PERF = 0x80;
|
||||
static constexpr uint8_t BMI270_STATUS_AUX_BUSY = 0x04;
|
||||
static constexpr uint8_t BMI270_IF_CONF_AUX_IF_EN = 0x20;
|
||||
static constexpr uint8_t BMI270_PWR_CTRL_AUX_EN = 0x01;
|
||||
static constexpr uint8_t BMI270_PWR_CTRL_ACC_EN = 0x02;
|
||||
static constexpr uint8_t BMI270_PWR_CTRL_GYR_EN = 0x04;
|
||||
static constexpr uint8_t BMI270_PWR_CTRL_TEMP_EN = 0x08;
|
||||
static constexpr uint8_t BMI270_PWR_CTRL_IMU =
|
||||
BMI270_PWR_CTRL_ACC_EN | BMI270_PWR_CTRL_GYR_EN | BMI270_PWR_CTRL_TEMP_EN;
|
||||
static constexpr uint8_t BMI270_AUX_IF_CONF_MANUAL_EN = 0x80;
|
||||
static constexpr uint8_t BMI270_AUX_IF_CONF_FCU_WRITE_EN = 0x40;
|
||||
static constexpr uint8_t BMI270_AUX_IF_CONF_MAN_RD_BURST_8 = 0x0C; // bits 3:2 = 3
|
||||
static constexpr uint8_t BMI270_AUX_IF_CONF_RD_BURST_8 = 0x03; // bits 1:0 = 3
|
||||
|
||||
// AUX_X_LSB..AUX_R_MSB (8 bytes) sit directly before the accel data, so one burst can read both.
|
||||
static constexpr uint8_t BMI270_AUX_DATA_LEN = 8;
|
||||
static_assert(BMI270_REG_AUX_X_LSB + BMI270_AUX_DATA_LEN == BMI270_REG_DATA_8, "aux data must precede accel data");
|
||||
|
||||
// Accelerometer range options
|
||||
enum BMI270AccelRange : uint8_t {
|
||||
@@ -75,7 +121,14 @@ enum BMI270GyroODR : uint8_t {
|
||||
BMI270_GYRO_ODR_3200 = 0x0D,
|
||||
};
|
||||
|
||||
// ---Data class
|
||||
#ifdef USE_BMI270_BMM150
|
||||
// What is wired to this instance's auxiliary (secondary I2C master) interface; another
|
||||
// instance in the same build may have nothing attached.
|
||||
enum BMI270AuxDevice : uint8_t {
|
||||
BMI270_AUX_DEVICE_NONE = 0,
|
||||
BMI270_AUX_DEVICE_BMM150 = 1,
|
||||
};
|
||||
#endif
|
||||
|
||||
// Main component class
|
||||
class BMI270Component final : public motion::MotionComponent, public i2c::I2CDevice {
|
||||
@@ -91,18 +144,43 @@ class BMI270Component final : public motion::MotionComponent, public i2c::I2CDev
|
||||
void set_gyro_range(BMI270GyroRange r) { this->gyro_range_ = r; }
|
||||
void set_gyro_odr(BMI270GyroODR o) { this->gyro_odr_ = o; }
|
||||
template<typename F> void add_temperature_listener(F &&cb) { this->temperature_callback_.add(std::forward<F>(cb)); }
|
||||
#ifdef USE_BMI270_BMM150
|
||||
void set_aux_device(BMI270AuxDevice device) { this->aux_device_ = device; }
|
||||
void set_aux_device_address(uint8_t address) { this->aux_device_address_ = address; }
|
||||
template<typename F> void add_magnetometer_listener(F &&cb) { this->magnetometer_callback_.add(std::forward<F>(cb)); }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool update_data(motion::MotionData &data) override;
|
||||
bool load_config_file_();
|
||||
#ifdef USE_BMI270_BMM150
|
||||
// Brings up the BMM150 over the BMI270's aux interface. Optional: on failure this only
|
||||
// disables magnetometer reporting, it does not fail the whole component (accel/gyro still work).
|
||||
bool setup_magnetometer_();
|
||||
// Blocking poll of the aux-transaction-busy bit; aux transaction time depends on the
|
||||
// attached device, so this is bounded by retries/timeout rather than a fixed delay.
|
||||
bool wait_for_aux_idle_(uint32_t timeout_ms);
|
||||
// Manual-mode aux read of 8 bytes starting at `reg` on the aux device.
|
||||
bool read_aux_block_(uint8_t reg, uint8_t *buf);
|
||||
// Manual-mode aux write of one byte to `reg` on the aux device.
|
||||
bool write_aux_register_(uint8_t reg, uint8_t value);
|
||||
// Reads the BMM150 factory trim values used to compensate its samples.
|
||||
bool read_magnetometer_trim_();
|
||||
#endif
|
||||
|
||||
// Config
|
||||
BMI270AccelRange accel_range_{BMI270_ACCEL_RANGE_4G};
|
||||
BMI270AccelODR accel_odr_{BMI270_ACCEL_ODR_100};
|
||||
BMI270GyroRange gyro_range_{BMI270_GYRO_RANGE_2000};
|
||||
BMI270GyroODR gyro_odr_{BMI270_GYRO_ODR_200};
|
||||
|
||||
LazyCallbackManager<void(float)> temperature_callback_{};
|
||||
#ifdef USE_BMI270_BMM150
|
||||
LazyCallbackManager<void(const bmm150::BMM150Data &)> magnetometer_callback_{};
|
||||
bmm150::BMM150Trim mag_trim_{};
|
||||
BMI270AuxDevice aux_device_{BMI270_AUX_DEVICE_NONE};
|
||||
uint8_t aux_device_address_{bmm150::BMM150_DEFAULT_I2C_ADDRESS};
|
||||
bool magnetometer_ready_{false};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace esphome::bmi270
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
// BMM150 magnetometer definitions - the chip that can optionally be wired to the BMI270's
|
||||
// auxiliary (secondary I2C master) interface. Lives in its own header/namespace because this
|
||||
// is BMM150-specific driver knowledge, not BMI270 knowledge - it just can't be a standalone
|
||||
// ESPHome component, since the BMM150 has no address of its own on the main I2C bus and is only
|
||||
// reachable by having the BMI270 proxy register reads/writes through its aux interface.
|
||||
namespace esphome::bmi270::bmm150 {
|
||||
|
||||
static constexpr uint8_t BMM150_DEFAULT_I2C_ADDRESS = 0x10;
|
||||
static constexpr uint8_t BMM150_REG_CHIP_ID = 0x40;
|
||||
static constexpr uint8_t BMM150_REG_DATA_X_LSB = 0x42;
|
||||
static constexpr uint8_t BMM150_REG_POWER_CONTROL = 0x4B;
|
||||
static constexpr uint8_t BMM150_REG_OP_MODE = 0x4C;
|
||||
static constexpr uint8_t BMM150_CHIP_ID_VALUE = 0x32;
|
||||
static constexpr uint8_t BMM150_CMD_POWER_ON = 0x01; // power control bit: suspend -> sleep (3 ms start-up)
|
||||
static constexpr uint8_t BMM150_CMD_SOFT_RESET = 0x83; // soft reset bits 7 and 1, power control bit kept set
|
||||
static constexpr uint8_t BMM150_CMD_NORMAL_MODE_ODR_30HZ = 0x38; // data rate 111b (30 Hz), opmode 00b (normal)
|
||||
static constexpr uint32_t BMM150_START_UP_TIME_MS = 3;
|
||||
static constexpr uint32_t BMM150_SOFT_RESET_TIME_MS = 1;
|
||||
|
||||
// Factory trim registers 0x5D-0x71, read once at setup and used to compensate every sample.
|
||||
static constexpr uint8_t BMM150_REG_TRIM_START = 0x5D;
|
||||
static constexpr uint8_t BMM150_TRIM_BLOCK_LEN = 8; // one aux burst
|
||||
static constexpr uint8_t BMM150_TRIM_BLOCKS = 3; // 0x5D, 0x65, 0x6D -> covers 0x5D-0x74
|
||||
static constexpr uint8_t BMM150_TRIM_X1 = 0x5D;
|
||||
static constexpr uint8_t BMM150_TRIM_Y1 = 0x5E;
|
||||
static constexpr uint8_t BMM150_TRIM_Z4 = 0x62; // 16-bit, LSB first
|
||||
static constexpr uint8_t BMM150_TRIM_X2 = 0x64;
|
||||
static constexpr uint8_t BMM150_TRIM_Y2 = 0x65;
|
||||
static constexpr uint8_t BMM150_TRIM_Z2 = 0x68; // 16-bit
|
||||
static constexpr uint8_t BMM150_TRIM_Z1 = 0x6A; // 16-bit
|
||||
static constexpr uint8_t BMM150_TRIM_XYZ1 = 0x6C; // 16-bit, 15 valid bits
|
||||
static constexpr uint8_t BMM150_TRIM_Z3 = 0x6E; // 16-bit
|
||||
static constexpr uint8_t BMM150_TRIM_XY2 = 0x70;
|
||||
static constexpr uint8_t BMM150_TRIM_XY1 = 0x71;
|
||||
static constexpr uint16_t BMM150_TRIM_XYZ1_MASK = 0x7FFF;
|
||||
|
||||
// Raw ADC values that mean "overflow" per the datasheet.
|
||||
static constexpr int16_t BMM150_XY_OVERFLOW_ADCVAL = -4096;
|
||||
static constexpr int16_t BMM150_Z_OVERFLOW_ADCVAL = -16384;
|
||||
|
||||
// Result of a BMM150 magnetometer reading, in µT. An axis is NaN when the sensor reported overflow.
|
||||
struct BMM150Data {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
|
||||
// Factory trim values (BMM150 datasheet, compensation section).
|
||||
struct BMM150Trim {
|
||||
int8_t x1, y1, x2, y2, xy2;
|
||||
uint8_t xy1;
|
||||
int16_t z2, z3, z4;
|
||||
uint16_t z1, xyz1;
|
||||
};
|
||||
|
||||
// A little-endian 16-bit trim word, addressed by its register
|
||||
inline uint16_t bmm150_trim_word(const uint8_t *regs, uint8_t reg) {
|
||||
const uint8_t offset = reg - BMM150_REG_TRIM_START;
|
||||
return encode_uint16(regs[offset + 1], regs[offset]);
|
||||
}
|
||||
|
||||
// Decodes the trim registers; `regs` holds the bytes of registers 0x5D onwards (at least 21).
|
||||
inline BMM150Trim bmm150_parse_trim(const uint8_t *regs) {
|
||||
BMM150Trim trim;
|
||||
trim.x1 = static_cast<int8_t>(regs[BMM150_TRIM_X1 - BMM150_REG_TRIM_START]);
|
||||
trim.y1 = static_cast<int8_t>(regs[BMM150_TRIM_Y1 - BMM150_REG_TRIM_START]);
|
||||
trim.z4 = static_cast<int16_t>(bmm150_trim_word(regs, BMM150_TRIM_Z4));
|
||||
trim.x2 = static_cast<int8_t>(regs[BMM150_TRIM_X2 - BMM150_REG_TRIM_START]);
|
||||
trim.y2 = static_cast<int8_t>(regs[BMM150_TRIM_Y2 - BMM150_REG_TRIM_START]);
|
||||
trim.z2 = static_cast<int16_t>(bmm150_trim_word(regs, BMM150_TRIM_Z2));
|
||||
trim.z1 = bmm150_trim_word(regs, BMM150_TRIM_Z1);
|
||||
trim.xyz1 = bmm150_trim_word(regs, BMM150_TRIM_XYZ1) & BMM150_TRIM_XYZ1_MASK;
|
||||
trim.z3 = static_cast<int16_t>(bmm150_trim_word(regs, BMM150_TRIM_Z3));
|
||||
trim.xy2 = static_cast<int8_t>(regs[BMM150_TRIM_XY2 - BMM150_REG_TRIM_START]);
|
||||
trim.xy1 = regs[BMM150_TRIM_XY1 - BMM150_REG_TRIM_START];
|
||||
return trim;
|
||||
}
|
||||
|
||||
// Compensates an X or Y sample; returns µT, or NaN on overflow. Port of the Bosch BMM150 API (float variant).
|
||||
inline float bmm150_compensate_xy(const BMM150Trim &trim, int8_t trim_a, int8_t trim_b, int16_t raw, uint16_t rhall) {
|
||||
if (raw == BMM150_XY_OVERFLOW_ADCVAL || rhall == 0 || trim.xyz1 == 0)
|
||||
return NAN;
|
||||
float retval = (float) trim.xyz1 * 16384.0f / (float) rhall - 16384.0f;
|
||||
float comp1 = (float) trim.xy2 * (retval * retval / 268435456.0f);
|
||||
float comp2 = comp1 + retval * (float) trim.xy1 / 16384.0f;
|
||||
float comp3 = (float) trim_b + 160.0f;
|
||||
float comp4 = (float) raw * ((comp2 + 256.0f) * comp3);
|
||||
return ((comp4 / 8192.0f) + (float) trim_a * 8.0f) / 16.0f;
|
||||
}
|
||||
|
||||
// Compensates a Z sample; returns µT, or NaN on overflow. Port of the Bosch BMM150 API (float variant).
|
||||
inline float bmm150_compensate_z(const BMM150Trim &trim, int16_t raw, uint16_t rhall) {
|
||||
if (raw == BMM150_Z_OVERFLOW_ADCVAL || trim.z2 == 0 || trim.z1 == 0 || trim.xyz1 == 0 || rhall == 0)
|
||||
return NAN;
|
||||
float comp0 = (float) raw - (float) trim.z4;
|
||||
float comp1 = (float) rhall - (float) trim.xyz1;
|
||||
float comp2 = (float) trim.z3 * comp1;
|
||||
float comp3 = (float) trim.z1 * (float) rhall / 32768.0f;
|
||||
float comp4 = (float) trim.z2 + comp3;
|
||||
float comp5 = comp0 * 131072.0f - comp2;
|
||||
return (comp5 / (comp4 * 4.0f)) / 16.0f;
|
||||
}
|
||||
|
||||
// Converts 8 raw bytes from the BMM150 data registers (X, Y, Z, RHALL; 0x42-0x49) to compensated µT.
|
||||
inline BMM150Data bmm150_convert(const BMM150Trim &trim, const uint8_t *raw) {
|
||||
// X/Y are 13-bit (LSB byte bits 7:3), Z is 15-bit (bits 7:1), RHALL is 14-bit (bits 7:2).
|
||||
int16_t x = (int16_t) (((int16_t) (int8_t) raw[1] * 32) | (raw[0] >> 3));
|
||||
int16_t y = (int16_t) (((int16_t) (int8_t) raw[3] * 32) | (raw[2] >> 3));
|
||||
int16_t z = (int16_t) (((int16_t) (int8_t) raw[5] * 128) | (raw[4] >> 1));
|
||||
uint16_t rhall = (uint16_t) (((uint16_t) raw[7] << 6) | (raw[6] >> 2));
|
||||
return BMM150Data{
|
||||
.x = bmm150_compensate_xy(trim, trim.x1, trim.x2, x, rhall),
|
||||
.y = bmm150_compensate_xy(trim, trim.y1, trim.y2, y, rhall),
|
||||
.z = bmm150_compensate_z(trim, z, rhall),
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace esphome::bmi270::bmm150
|
||||
@@ -14,7 +14,11 @@ from . import BMI270Component, bmi270_ns
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
|
||||
CONF_AUX_ADDRESS = "aux_address"
|
||||
CONF_AUX_DEVICE = "aux_device"
|
||||
|
||||
# Enum proxies (must match the C++ enum values exactly)
|
||||
BMI270AuxDevice = bmi270_ns.enum("BMI270AuxDevice")
|
||||
BMI270AccelRange = bmi270_ns.enum("BMI270AccelRange")
|
||||
ACCEL_RANGE_OPTIONS = {
|
||||
"2G": BMI270AccelRange.BMI270_ACCEL_RANGE_2G,
|
||||
@@ -73,6 +77,13 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(CONF_GYROSCOPE_ODR, default="200HZ"): cv.enum(
|
||||
GYRO_ODR_OPTIONS, upper=True
|
||||
),
|
||||
# Optional device wired to the BMI270's aux interface (not the main I2C bus).
|
||||
# Defaults to NONE so existing configs/boards are unaffected.
|
||||
cv.Optional(CONF_AUX_DEVICE, default="NONE"): cv.one_of(
|
||||
"NONE", "BMM150", upper=True
|
||||
),
|
||||
# Default matches the BMM150's fixed I2C address; irrelevant when aux_device is NONE.
|
||||
cv.Optional(CONF_AUX_ADDRESS, default=0x10): cv.i2c_address,
|
||||
}
|
||||
)
|
||||
.extend(i2c.i2c_device_schema(0x68))
|
||||
@@ -90,3 +101,9 @@ async def to_code(config: ConfigType) -> None:
|
||||
cg.add(var.set_accel_odr(config[CONF_ACCELEROMETER_ODR]))
|
||||
cg.add(var.set_gyro_range(config[CONF_GYROSCOPE_RANGE]))
|
||||
cg.add(var.set_gyro_odr(config[CONF_GYROSCOPE_ODR]))
|
||||
# The aux interface code only compiles when some instance has a BMM150; the
|
||||
# instance still records its own device, as another one may have none
|
||||
if config[CONF_AUX_DEVICE] == "BMM150":
|
||||
cg.add_define("USE_BMI270_BMM150")
|
||||
cg.add(var.set_aux_device(BMI270AuxDevice.BMI270_AUX_DEVICE_BMM150))
|
||||
cg.add(var.set_aux_device_address(config[CONF_AUX_ADDRESS]))
|
||||
|
||||
@@ -6,37 +6,97 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
CONF_TYPE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_MAGNET,
|
||||
ICON_THERMOMETER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_MICROTESLA,
|
||||
)
|
||||
from esphome.cpp_generator import MockObj
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from . import CONF_BMI270_ID, BMI270Component
|
||||
from . import CONF_BMI270_ID, BMI270Component, bmi270_ns
|
||||
from .motion import CONF_AUX_DEVICE
|
||||
|
||||
AUTO_LOAD = ["bmi270"]
|
||||
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
).extend(
|
||||
CONF_MAGNETIC_FIELD_X = "magnetic_field_x"
|
||||
CONF_MAGNETIC_FIELD_Y = "magnetic_field_y"
|
||||
CONF_MAGNETIC_FIELD_Z = "magnetic_field_z"
|
||||
|
||||
BMM150Data = bmi270_ns.namespace("bmm150").class_("BMM150Data")
|
||||
|
||||
_MAGNETIC_FIELDS = (CONF_MAGNETIC_FIELD_X, CONF_MAGNETIC_FIELD_Y, CONF_MAGNETIC_FIELD_Z)
|
||||
|
||||
|
||||
def _parent_schema() -> cv.Schema:
|
||||
return cv.Schema({cv.GenerateID(CONF_BMI270_ID): cv.use_id(BMI270Component)})
|
||||
|
||||
|
||||
def _temperature_sensor_schema() -> cv.Schema:
|
||||
return sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
).extend(_parent_schema())
|
||||
|
||||
|
||||
def _magnetic_field_sensor_schema() -> cv.Schema:
|
||||
return sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_MICROTESLA,
|
||||
icon=ICON_MAGNET,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(_parent_schema())
|
||||
|
||||
|
||||
# `type` defaults to temperature so existing configs that omit it keep working unchanged.
|
||||
CONFIG_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
cv.Optional(CONF_TYPE): cv.one_of(CONF_TEMPERATURE),
|
||||
cv.GenerateID(CONF_BMI270_ID): cv.use_id(BMI270Component),
|
||||
}
|
||||
CONF_TEMPERATURE: _temperature_sensor_schema(),
|
||||
**{x: _magnetic_field_sensor_schema() for x in _MAGNETIC_FIELDS},
|
||||
},
|
||||
default_type=CONF_TEMPERATURE,
|
||||
)
|
||||
|
||||
|
||||
def _final_validate(config: ConfigType) -> None:
|
||||
if config[CONF_TYPE] not in _MAGNETIC_FIELDS:
|
||||
return
|
||||
full_config = fv.full_config.get()
|
||||
bmi270_path = full_config.get_path_for_id(config[CONF_BMI270_ID])[:-1]
|
||||
bmi270_config = full_config.get_config_for_path(bmi270_path)
|
||||
if bmi270_config.get(CONF_AUX_DEVICE) != "BMM150":
|
||||
raise cv.Invalid(
|
||||
"This sensor requires the parent bmi270's 'aux_device' to be set to 'BMM150'",
|
||||
path=[CONF_TYPE],
|
||||
)
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = _final_validate
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
sensor_type = config[CONF_TYPE]
|
||||
var = await sensor.new_sensor(config)
|
||||
parent = await cg.get_variable(config[CONF_BMI270_ID])
|
||||
|
||||
if sensor_type == CONF_TEMPERATURE:
|
||||
data = MockObj("data")
|
||||
value_lambda = await cg.process_lambda(
|
||||
var.publish_state(data),
|
||||
[(cg.float_, str(data))],
|
||||
)
|
||||
cg.add(parent.add_temperature_listener(value_lambda))
|
||||
return
|
||||
|
||||
axis = sensor_type[-1:]
|
||||
data = MockObj("data")
|
||||
value_lambda = await cg.process_lambda(
|
||||
var.publish_state(data),
|
||||
[(cg.float_, str(data))],
|
||||
var.publish_state(getattr(data, axis)),
|
||||
[(BMM150Data.operator("const").operator("ref"), str(data))],
|
||||
)
|
||||
cg.add(parent.add_temperature_listener(value_lambda))
|
||||
cg.add(parent.add_magnetometer_listener(value_lambda))
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#define USE_BINARY_SENSOR_FILTER
|
||||
#define USE_BINARY_SENSOR_MULTI_CLICK_TRIGGER
|
||||
#define USE_BLE_DEVICE_IRK
|
||||
#define USE_BMI270_BMM150
|
||||
#define USE_BUTTON
|
||||
#define USE_CAMERA
|
||||
#define USE_CLIMATE
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
motion:
|
||||
- platform: bmi270
|
||||
id: plain_imu
|
||||
i2c_id: i2c_bus
|
||||
address: 0x69
|
||||
|
||||
sensor:
|
||||
- platform: bmi270
|
||||
bmi270_id: plain_imu
|
||||
type: temperature
|
||||
name: Plain IMU Temperature
|
||||
@@ -1,7 +1,21 @@
|
||||
sensor:
|
||||
- platform: bmi270
|
||||
bmi270_id: bmi270_motion
|
||||
name: "BMI270 Temperature"
|
||||
|
||||
- platform: bmi270
|
||||
bmi270_id: bmi270_motion
|
||||
type: magnetic_field_x
|
||||
name: "BMM150 Magnetic Field X"
|
||||
- platform: bmi270
|
||||
bmi270_id: bmi270_motion
|
||||
type: magnetic_field_y
|
||||
name: "BMM150 Magnetic Field Y"
|
||||
- platform: bmi270
|
||||
bmi270_id: bmi270_motion
|
||||
type: magnetic_field_z
|
||||
name: "BMM150 Magnetic Field Z"
|
||||
|
||||
- platform: motion
|
||||
motion_id: bmi270_motion
|
||||
type: acceleration_x
|
||||
@@ -75,6 +89,8 @@ motion:
|
||||
# Gyroscope output data rate: 25HZ | 50HZ | 100HZ | 200HZ |
|
||||
# 400HZ | 800HZ | 1600HZ | 3200HZ
|
||||
gyroscope_odr: 200HZ
|
||||
aux_device: BMM150
|
||||
aux_address: 0x10
|
||||
axis_map:
|
||||
x: y
|
||||
y: x
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
bmi270: !include common-plain.yaml
|
||||
@@ -1,4 +1,4 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
bmi270: !include common.yaml
|
||||
bmi270_plain: !include common-plain.yaml
|
||||
|
||||
Reference in New Issue
Block a user