Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ jobs:
target: esp32
- path: 'components/bmi270/example'
target: esp32p4
- path: 'components/bno055/example'
target: esp32p4
- path: 'components/bq27220/example'
target: esp32
- path: 'components/button/example'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/upload_components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ jobs:
components/ble_gatt_server
components/bm8563
components/bmi270
components/bno055
components/button
components/byte90
components/canopen
Expand Down
5 changes: 5 additions & 0 deletions components/bno055/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES "base_peripheral"
)
27 changes: 27 additions & 0 deletions components/bno055/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# BNO055 9-Axis Absolute Orientation IMU Component

[![Badge](https://components.espressif.com/components/espp/bno055/badge.svg)](https://components.espressif.com/components/espp/bno055)

The `Bno055` component provides a driver for the Bosch BNO055 9-axis absolute
orientation sensor over I2C. The BNO055 runs sensor fusion on its own
microcontroller, so the driver reads finished orientation data rather than
filtering raw samples on the ESP.

It supports configuring and reading:
* Operating mode (raw-only modes and the IMU, COMPASS, M4G, and NDOF fusion
modes)
* 3-axis acceleration, angular rate, and magnetic field
* Fused orientation as Euler angles and as a quaternion
* Linear acceleration (gravity removed) and the gravity vector
* Die temperature and per-sensor calibration status

All outputs are read in a single 46-byte I2C burst and reported in SI units
(m/s^2, rad/s, rad, uT).

It does not _yet_ support saving and restoring calibration offsets, axis
remapping, or the interrupt pin.

## Example

The [example](./example) shows how to use the `espp::Bno055` component to
initialize a BNO055 and print its outputs.
22 changes: 22 additions & 0 deletions components/bno055/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.20)

set(ENV{IDF_COMPONENT_MANAGER} "0")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# add the component directories that we want to use
set(EXTRA_COMPONENT_DIRS
"../../../components/"
)

set(
COMPONENTS
"main esptool_py i2c bno055 task"
CACHE STRING
"List of components to include"
)

project(bno055_example)

set(CMAKE_CXX_STANDARD 23)
41 changes: 41 additions & 0 deletions components/bno055/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# BNO055 Example

This example shows how to use the `espp::Bno055` component to initialize a
BNO055 9-axis absolute orientation IMU over I2C and print its outputs.

It puts the BNO055 in IMU fusion mode (accelerometer + gyroscope, heading
relative to power-on), reads all outputs at 100 Hz in one I2C burst per sample,
and prints one CSV row per sample: acceleration, angular rate, Euler angles, the
orientation quaternion, temperature, and calibration status. The header line
starts with `%` so the output can be pasted straight into a plotter.

## How to use example

### Hardware Required

This example runs on any ESP32 series device with a BNO055 breakout connected
over I2C. The defaults are SDA=GPIO7 and SCL=GPIO8 on the ESP32-P4 (the
internal I2C bus pins on the espp `esp32-p4-eth` board) and SDA=GPIO21,
SCL=GPIO22 on other targets. Change the pins and the I2C address (0x28 or 0x29)
with `idf.py menuconfig` under `Example Configuration`.

### Build and Flash

Build the project and flash it to the board, then run monitor tool to view
serial output:

```
idf.py -p PORT flash monitor
```

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type ``Ctrl-]``.)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example Output

```
% 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
```
2 changes: 2 additions & 0 deletions components/bno055/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
33 changes: 33 additions & 0 deletions components/bno055/example/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
menu "Example Configuration"

config EXAMPLE_I2C_SDA_GPIO
int "SDA GPIO Num"
range 0 50
default 7 if IDF_TARGET_ESP32P4
default 21
help
GPIO number for I2C Master data line.

config EXAMPLE_I2C_SCL_GPIO
int "SCL GPIO Num"
range 0 50
default 8 if IDF_TARGET_ESP32P4
default 22
help
GPIO number for I2C Master clock line.

config EXAMPLE_I2C_CLOCK_SPEED_HZ
int "I2C Clock Speed"
range 100 400000
default 400000
help
I2C clock speed in Hz. The BNO055 supports up to 400 kHz.

config EXAMPLE_I2C_ADDRESS
hex "BNO055 I2C Address"
range 0x28 0x29
default 0x28
help
I2C address of the BNO055: 0x28 with ADR (COM3) low, 0x29 with it high.

endmenu
102 changes: 102 additions & 0 deletions components/bno055/example/main/bno055_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include <chrono>
#include <sdkconfig.h>

#include "bno055.hpp"
#include "i2c.hpp"
#include "task.hpp"

using namespace std::chrono_literals;

extern "C" void app_main(void) {
espp::Logger logger({.tag = "BNO055 Example", .level = espp::Logger::Verbosity::INFO});
logger.info("Starting example!");

//! [bno055 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);
}
//! [bno055 example]
}
14 changes: 14 additions & 0 deletions components/bno055/example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CONFIG_IDF_TARGET="esp32p4"

CONFIG_FREERTOS_HZ=1000

# set compiler optimization level to -O2 (compile for performance)
CONFIG_COMPILER_OPTIMIZATION_PERF=y

# Common ESP-related
#
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384

# Set esp-timer task stack size to 6KB
CONFIG_ESP_TIMER_TASK_STACK_SIZE=6144
11 changes: 11 additions & 0 deletions components/bno055/example/sdkconfig.defaults.esp32p4
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Applied on top of sdkconfig.defaults when the target is esp32p4 (ESP-IDF
# picks up sdkconfig.defaults.<target> automatically).
#
# Support pre-v3 silicon (e.g. ESP32-P4 v1.x on the ESP32-P4-ETH and
# Function-EV boards).
CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y

# The ESP-IDF 6.1 esp32p4 bootloader (0x6140 bytes) no longer fits in front of
# the default partition-table offset 0x8000 (max 0x6000 bytes there); move the
# table to 0x9000 like the other esp32p4 boards in this repo.
CONFIG_PARTITION_TABLE_OFFSET=0x9000
20 changes: 20 additions & 0 deletions components/bno055/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## IDF Component Manager Manifest File
license: "MIT"
description: "BNO055 9-Axis absolute orientation IMU component for ESP-IDF"
url: "https://github.com/esp-cpp/espp/tree/main/components/bno055"
repository: "git://github.com/esp-cpp/espp.git"
maintainers:
- William Emfinger <waemfinger@gmail.com>
documentation: "https://esp-cpp.github.io/espp/imu/bno055.html"
examples:
- path: example
tags:
- cpp
- Component
- BNO055
- IMU
- Peripheral
dependencies:
idf:
version: '>=5.0'
espp/base_peripheral: '>=1.0'
Loading
Loading