BNO055 9-Axis Absolute Orientation IMU
The Bno055 component provides a driver for the Bosch BNO055 9-axis absolute orientation sensor. The BNO055 runs sensor fusion on its own microcontroller and reports orientation, linear acceleration, and gravity alongside the raw accelerometer, gyroscope, and magnetometer data.
API Reference
Header File
Classes
-
class Bno055 : public espp::BasePeripheral<uint8_t>
Driver for the Bosch BNO055 9-axis absolute orientation sensor over I2C.
The BNO055 combines a 3-axis accelerometer, 3-axis gyroscope, and 3-axis magnetometer with an on-chip Cortex-M0 that runs Bosch’s sensor fusion and outputs orientation (Euler angles and a quaternion), linear acceleration, and gravity.
This driver configures the sensor into one of its operating modes and reads all sensor and fusion outputs in a single 46-byte burst. Units are set to SI: m/s^2 for acceleration, rad/s for angular rate, rad for Euler angles, and uT for the magnetic field.
The default mode is IMU (accelerometer + gyroscope fusion, heading relative to power-on). NDOF adds the magnetometer for an absolute heading, but is sensitive to nearby motors and ferrous metal.
Datasheet:
Example
// make the i2c we'll use to communicate static constexpr auto i2c_port = I2C_NUM_0; static constexpr auto i2c_clock_speed = CONFIG_EXAMPLE_I2C_CLOCK_SPEED_HZ; static constexpr gpio_num_t i2c_sda = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO; static constexpr gpio_num_t i2c_scl = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO; logger.info("Creating I2C on port {} with SDA {} and SCL {}", i2c_port, i2c_sda, i2c_scl); espp::I2c i2c({.port = i2c_port, .sda_io_num = i2c_sda, .scl_io_num = i2c_scl, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .clk_speed = i2c_clock_speed}); static constexpr uint8_t address = CONFIG_EXAMPLE_I2C_ADDRESS; std::error_code ec; auto imu_device = i2c.add_device<uint8_t>({.device_address = address, .timeout_ms = static_cast<int>(i2c.config().timeout_ms), .scl_speed_hz = i2c.config().clk_speed, .log_level = espp::Logger::Verbosity::WARN}, ec); if (!imu_device) { logger.error("Failed to create BNO055 I2C device: {}", ec.message()); return; } // create the IMU; auto_init resets it and enters IMU (accel + gyro) fusion // mode, which takes about 700 ms espp::Bno055 imu({.device_address = address, .probe = espp::make_i2c_addressed_probe(imu_device), .write = espp::make_i2c_addressed_write(imu_device), .write_then_read = espp::make_i2c_addressed_write_then_read(imu_device), .mode = espp::Bno055::OperatingMode::IMU, .use_external_crystal = true, .auto_init = true, .log_level = espp::Logger::Verbosity::INFO}); if (!imu.initialized()) { logger.error("BNO055 not found at address {:#04x}", address); return; } // print the header for the IMU data (for plotting) fmt::print("% Time (s), " "Accel X (m/s^2), Accel Y (m/s^2), Accel Z (m/s^2), " "Gyro X (rad/s), Gyro Y (rad/s), Gyro Z (rad/s), " "Heading (rad), Roll (rad), Pitch (rad), " "Quat W, Quat X, Quat Y, Quat Z, " "Temp (C), Calib Sys, Calib Gyro, Calib Accel, Calib Mag\n"); // read the IMU at 100 Hz and print one CSV row per sample auto task_fn = [&](std::mutex &m, std::condition_variable &cv) { auto start = std::chrono::high_resolution_clock::now(); std::error_code ec; if (!imu.update(ec)) { logger.error("Failed to update BNO055: {}", ec.message()); } else { auto data = imu.data(); std::string text = fmt::format("{:.3f},", esp_timer_get_time() / 1'000'000.0f); text += fmt::format("{:.3f},{:.3f},{:.3f},", data.accel_mps2.x, data.accel_mps2.y, data.accel_mps2.z); text += fmt::format("{:.4f},{:.4f},{:.4f},", data.gyro_radps.x, data.gyro_radps.y, data.gyro_radps.z); text += fmt::format("{:.4f},{:.4f},{:.4f},", data.euler.heading, data.euler.roll, data.euler.pitch); text += fmt::format("{:.4f},{:.4f},{:.4f},{:.4f},", data.orientation.w, data.orientation.x, data.orientation.y, data.orientation.z); text += fmt::format("{},{},{},{},{}", data.temperature_c, data.calibration.system, data.calibration.gyro, data.calibration.accel, data.calibration.mag); fmt::print("{}\n", text); } std::unique_lock<std::mutex> lk(m); cv.wait_until(lk, start + 10ms); return false; }; espp::Task task({.callback = task_fn, .task_config = {.name = "BNO055 Task", .stack_size_bytes = 6 * 1024}, .log_level = espp::Logger::Verbosity::WARN}); task.start(); // loop forever while (true) { std::this_thread::sleep_for(1s); }
Note
The BNO055 stretches the I2C clock; keep the I2C timeout at or above the espp default (10 ms).
Public Types
-
enum class OperatingMode : uint8_t
Sensor operating mode (OPR_MODE register).
Values:
-
enumerator CONFIG
Configuration mode; no outputs are updated.
-
enumerator ACC_ONLY
Accelerometer only.
-
enumerator ACC_GYRO
Accelerometer and gyroscope, no fusion.
-
enumerator AMG
All three sensors, no fusion.
-
enumerator IMU
Accel + gyro fusion; heading is relative to power-on.
-
enumerator COMPASS
Accel + mag fusion.
-
enumerator M4G
Accel + mag fusion, mag used in place of gyro.
-
enumerator NDOF_FMC_OFF
Full 9-DoF fusion without fast mag calibration.
-
enumerator NDOF
Full 9-DoF fusion; heading is absolute (magnetic north).
-
enumerator CONFIG
-
typedef std::function<bool(uint8_t)> probe_fn
Function to probe the peripheral
- Param address:
The address to probe
- Return:
True if the peripheral is found at the given address
Public Functions
-
explicit Bno055(const Config &config)
Construct the driver.
- Parameters:
config – Bus access functions and sensor settings.
-
bool init(std::error_code &ec)
Reset the sensor, verify its chip ID, set SI units, and enter the configured mode.
Note
Blocks for roughly 700 ms while the sensor resets.
- Parameters:
ec – Set on any bus error or if the chip ID does not match.
- Returns:
True if the sensor is running in the configured mode.
-
bool update(std::error_code &ec)
Read all sensor and fusion outputs in one burst and cache them.
- Parameters:
ec – Set on a bus error.
- Returns:
True if the read succeeded.
-
Data data() const
Get the most recent outputs read by update().
- Returns:
A copy of the cached data.
-
inline bool initialized() const
Check whether init() completed successfully.
- Returns:
True if the sensor is initialized.
-
void read_system_status(uint8_t &status, uint8_t &error, std::error_code &ec)
Read the SYS_STATUS and SYS_ERR registers.
- Parameters:
status – Set to SYS_STATUS (5 = fusion running, 1 = system error).
error – Set to SYS_ERR (0 = no error).
ec – Set on a bus error.
-
inline bool probe(std::error_code &ec) const
Probe the peripheral
Note
This function is thread safe
Note
If the probe function is not set, this function will return false and set the error code to operation_not_supported
Note
This function is only available if UseAddress is true
- Parameters:
ec – The error code to set if there is an error
- Returns:
True if the peripheral is found
-
inline void set_address(uint8_t address)
Set the address of the peripheral
Note
This function is thread safe
Note
This function is only available if UseAddress is true
- Parameters:
address – The address of the peripheral
-
inline void set_probe(const probe_fn &probe)
Set the probe function
Note
This function is thread safe
Note
This should rarely be used, as the probe function is usually set in the constructor. If you need to change the probe function, consider using the set_config function instead.
Note
This function is only available if UseAddress is true
- Parameters:
probe – The probe function
-
inline void set_write(const write_fn &write)
Set the write function
Note
This function is thread safe
Note
This should rarely be used, as the write function is usually set in the constructor. If you need to change the write function, consider using the set_config function instead.
- Parameters:
write – The write function
-
inline void set_read(const read_fn &read)
Set the read function
Note
This function is thread safe
Note
This should rarely be used, as the read function is usually set in the constructor. If you need to change the read function, consider using the set_config function instead.
- Parameters:
read – The read function
-
inline void set_read_register(const read_register_fn &read_register)
Set the read register function
Note
This function is thread safe
Note
This should rarely be used, as the read register function is usually set in the constructor. If you need to change the read register function, consider using the set_config function instead.
- Parameters:
read_register – The read register function
-
inline void set_write_then_read(const write_then_read_fn &write_then_read)
Set the write then read function
Note
This function is thread safe
Note
This should rarely be used, as the write then read function is usually set in the constructor. If you need to change the write then
- Parameters:
write_then_read – The write then read function
-
inline void set_separate_write_then_read_delay(const std::chrono::milliseconds &delay)
Set the delay between the write and read operations in write_then_read
Note
This function is thread safe
Note
This should rarely be used, as the delay is usually set in the constructor. If you need to change the delay, consider using the set_config function instead.
Note
This delay is only used if the write_then_read function is not set to a custom function and the write and read functions are separate functions.
- Parameters:
delay – The delay between the write and read operations in write_then_read
-
inline void set_config(const Config &config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline void set_config(Config &&config)
Set the configuration for the peripheral
Note
This function is thread safe
Note
The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.
- Parameters:
config – The configuration for the peripheral
-
inline const Config &config() const
Get the configuration for the peripheral
- Returns:
The configuration for the peripheral
-
inline uint8_t address() const
Get the address of the peripheral
- Returns:
The address of the peripheral
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
Public Static Attributes
-
static constexpr uint8_t DEFAULT_ADDRESS = 0x28
Address with ADR (COM3) pulled low.
-
static constexpr uint8_t ALTERNATE_ADDRESS = 0x29
Address with ADR (COM3) pulled high.
-
static constexpr uint8_t CHIP_ID = 0xA0
Expected value of the CHIP_ID register.
-
struct CalibrationStatus
Calibration status of each sensor, 0 (uncalibrated) to 3 (fully calibrated).
-
struct Config
Driver configuration.
Public Members
-
uint8_t device_address = {DEFAULT_ADDRESS}
I2C address of the sensor.
-
BasePeripheral::probe_fn probe = {nullptr}
Probe the I2C address.
-
BasePeripheral::write_fn write = {nullptr}
Write bytes to the sensor.
-
BasePeripheral::write_then_read_fn write_then_read = {nullptr}
Write then read.
-
OperatingMode mode = {OperatingMode::IMU}
Fusion mode to run in.
-
bool use_external_crystal = {true}
Use the board’s 32 kHz crystal.
-
uint8_t device_address = {DEFAULT_ADDRESS}
-
struct Data
One complete set of sensor and fusion outputs.
Public Members
-
Quaternion orientation
Fused orientation as a quaternion.
-
int8_t temperature_c = {0}
Die temperature, in degrees Celsius.
-
CalibrationStatus calibration
Per-sensor calibration status.
-
Quaternion orientation
-
struct Quaternion
Unit quaternion.
-
struct Vector3
Three-axis vector.
-
enum class OperatingMode : uint8_t