diff --git a/.github/workflows/ros2.yml b/.github/workflows/ros2.yml
new file mode 100644
index 0000000..61fcc05
--- /dev/null
+++ b/.github/workflows/ros2.yml
@@ -0,0 +1,51 @@
+name: ros2
+
+on:
+ push:
+ paths:
+ - ".github/workflows/ros2.yml"
+ - "include/**"
+ - "launch/**"
+ - "src/**"
+ - "CMakeLists.txt"
+ - "package.xml"
+ pull_request:
+ paths:
+ - ".github/workflows/ros2.yml"
+ - "include/**"
+ - "launch/**"
+ - "src/**"
+ - "CMakeLists.txt"
+ - "package.xml"
+
+env:
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ name: Build on ros2 ${{ matrix.ros_distro }}
+ runs-on: ubuntu-22.04
+ strategy:
+ matrix:
+ ros_distro: [ humble ]
+
+ steps:
+ - uses: ros-tooling/setup-ros@v0.7
+ with:
+ required-ros-distributions: ${{ matrix.ros_distro }}
+
+ - name: Setup ros2 workspace
+ run: |
+ mkdir -p ${{github.workspace}}/ros2_ws/src
+
+ - uses: actions/checkout@v4
+ with:
+ path: 'ros2_ws/src/pika-spark-bno085-driver'
+ submodules: true
+ fetch-depth: 1
+
+ - name: colcon build
+ run: |
+ source /opt/ros/${{ matrix.ros_distro }}/setup.bash
+ cd ${{github.workspace}}/ros2_ws
+ colcon build --event-handlers console_direct+
diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml
deleted file mode 100644
index 0025ac4..0000000
--- a/.github/workflows/smoke-test.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Build
-
-# See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows
-on:
- push:
- pull_request:
- schedule:
- # Run every Tuesday at 8 AM UTC
- - cron: "0 8 * * TUE"
- workflow_dispatch:
- repository_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- smoke-test:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- submodules: true
- fetch-depth: 1
-
- - name: Install CMake
- run: sudo apt-get install cmake
-
- - name: Create build directory, run CMake and Make
- run: mkdir build && cd build && cmake .. && make
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b14106a..3ea4772 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,16 @@
##########################################################################
cmake_minimum_required(VERSION 3.15)
##########################################################################
-project("pika-spark-bno085-driver")
+project("pika_spark_bno085_driver")
+set(PIKA_SPARK_BNO085_TARGET ${PROJECT_NAME}_node)
+##########################################################################
+find_package(ament_cmake REQUIRED)
+find_package(rclcpp REQUIRED)
+find_package(sensor_msgs REQUIRED)
##########################################################################
add_subdirectory(sh2)
##########################################################################
-add_executable(${PROJECT_NAME}
+add_executable(${PIKA_SPARK_BNO085_TARGET}
main.cpp
bno085.cpp
bno085_hal.cpp
@@ -14,10 +19,17 @@ add_executable(${PROJECT_NAME}
)
##########################################################################
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Werror -Wextra -Wpedantic)
+ target_compile_options(${PIKA_SPARK_BNO085_TARGET} PRIVATE -Wall -Werror -Wextra -Wpedantic)
endif()
##########################################################################
-target_link_libraries(${PROJECT_NAME} pika-spark-sh2)
+target_link_libraries(${PIKA_SPARK_BNO085_TARGET} pika-spark-sh2)
+##########################################################################
+target_compile_features(${PIKA_SPARK_BNO085_TARGET} PUBLIC cxx_std_17)
+##########################################################################
+ament_target_dependencies(${PIKA_SPARK_BNO085_TARGET} rclcpp sensor_msgs)
+##########################################################################
+install(TARGETS ${PIKA_SPARK_BNO085_TARGET} DESTINATION lib/${PROJECT_NAME})
+install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
##########################################################################
-target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
+ament_package()
##########################################################################
diff --git a/README.md b/README.md
index 599440e..019fdc5 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,35 @@
:sparkles: `pika-spark-bno085-driver`
=====================================
-[![Smoke test status](https://github.com/pika-spark/pika-spark-bno085-driver/actions/workflows/smoke-test.yml/badge.svg)](https://github.com/pika-spark/pika-spark-bno085-driver/actions/workflows/smoke-test.yml)
+[![Build Status](https://github.com/pika-spark/pika-spark-bno085-driver/actions/workflows/ros2.yml/badge.svg)](https://github.com/pika-spark/pika-spark-bno085-driver/actions/workflows/ros2.yml)
[![Spell Check status](https://github.com/pika-spark/pika-spark-bno085-driver/actions/workflows/spell-check.yml/badge.svg)](https://github.com/pika-spark/pika-spark-bno085-driver/actions/workflows/spell-check.yml)
-Linux user space driver for the [BNO085](https://www.ceva-dsp.com/wp-content/uploads/2019/10/BNO080_085-Datasheet.pdf) 9-DoF IMU driver.
+Linux user space ROS driver for the [BNO085](https://www.ceva-dsp.com/wp-content/uploads/2019/10/BNO080_085-Datasheet.pdf) 9-DoF IMU.
+
+**Note**: In order to run the BNO085 ROS driver on [Pika Spark](https://pika-spark.io/) take a look at ready-to-use build/run scripts at [pika-spark-container/ros-imu-bno085](https://github.com/pika-spark/pika-spark-containers/tree/main/ros-imu-bno085).
-### How-to-build/run
+### How-to-build
+```bash
+cd $COLCON_WS/src
+git clone --recursive https://github.com/pika-spark/pika-spark-bno085-driver
+cd $COLCON_WS
+source /opt/ros/humble/setup.bash
+colcon build --packages-select pika_spark_bno085_driver
+```
+
+#### How-to-run
+```bash
+cd $COLCON_WS
+. install/setup.bash
+ros2 launch pika_spark_bno085_driver imu.py
+```
+
+#### How-to-visualize your IMU data
```bash
-git clone https://github.com/pika-spark/pika-spark-bno085-driver && cd pika-spark-bno085-driver/docker
-./docker-build.sh
-sudo ./docker-run.sh
+sudo apt-get install ros-humble-imu-tools
+ros2 run rviz2 rviz2
```
diff --git a/bno085.cpp b/bno085.cpp
index 1c946ec..72558b7 100644
--- a/bno085.cpp
+++ b/bno085.cpp
@@ -19,9 +19,13 @@
BNO085::BNO085(std::shared_ptr const spi,
std::shared_ptr const nirq,
- StabilizedRotationVectorWAccuracyCallbackFunc const sensor_func)
+ LinearAccelerationCallbackFunc const acc_callback,
+ CalibratedGyroscopeCallbackFunc const gyro_callback,
+ StabilizedRotationVectorWAccuracyCallbackFunc const attitude_callback)
: BNO085_HAL{spi, nirq}
-, _sensor_func{sensor_func}
+, _acc_callback{acc_callback}
+, _gyro_callback{gyro_callback}
+, _attitude_callback{attitude_callback}
{
}
@@ -30,7 +34,43 @@ BNO085::BNO085(std::shared_ptr const spi,
* PUBLIC MEMBER FUNCTIONS
**************************************************************************************/
-int BNO085::config()
+int BNO085::enableAccelerometer()
+{
+ sh2_SensorConfig_t const bno085_config =
+ {
+ /* .changeSensitivityEnabled = */ false,
+ /* .changeSensitivityRelative = */ false,
+ /* .wakeupEnabled = */ false,
+ /* .alwaysOnEnabled = */ false,
+ /* .sniffEnabled = */ false,
+ /* .changeSensitivity = */ 0,
+ /* .reportInterval_us = */ 5000, /* 200 Hz */
+ /* .batchInterval_us = */ 0,
+ /* .sensorSpecific = */ 0
+ };
+
+ return sh2_setSensorConfig(SH2_LINEAR_ACCELERATION, &bno085_config);
+}
+
+int BNO085::enableGyroscope()
+{
+ sh2_SensorConfig_t const bno085_config =
+ {
+ /* .changeSensitivityEnabled = */ false,
+ /* .changeSensitivityRelative = */ false,
+ /* .wakeupEnabled = */ false,
+ /* .alwaysOnEnabled = */ false,
+ /* .sniffEnabled = */ false,
+ /* .changeSensitivity = */ 0,
+ /* .reportInterval_us = */ 5000, /* 200 Hz */
+ /* .batchInterval_us = */ 0,
+ /* .sensorSpecific = */ 0
+ };
+
+ return sh2_setSensorConfig(SH2_GYROSCOPE_CALIBRATED, &bno085_config);
+}
+
+int BNO085::enableAttitude()
{
sh2_SensorConfig_t const bno085_config =
{
@@ -64,10 +104,12 @@ void BNO085::internal_sh2_sensor_callback(sh2_SensorEvent_t * event)
if (auto const rc = sh2_decodeSensorEvent(&sensor_value, event); rc != SH2_OK)
throw BNO085_Exception("error decoding sensor event, rc = %d", rc);
- if (sensor_value.sensorId == SH2_ARVR_STABILIZED_RV) {
- if (_sensor_func)
- _sensor_func(sensor_value.un.arvrStabilizedRV);
- }
+ if (sensor_value.sensorId == SH2_LINEAR_ACCELERATION)
+ _acc_callback(sensor_value.un.linearAcceleration);
+ else if (sensor_value.sensorId == SH2_GYROSCOPE_CALIBRATED)
+ _gyro_callback(sensor_value.un.gyroscope);
+ else if (sensor_value.sensorId == SH2_ARVR_STABILIZED_RV)
+ _attitude_callback(sensor_value.un.arvrStabilizedRV);
else
throw BNO085_Exception("unhandled sensor event decoded, sensorId = %d", sensor_value.sensorId);
}
diff --git a/bno085.h b/bno085.h
index 4d13492..4d81b9f 100644
--- a/bno085.h
+++ b/bno085.h
@@ -24,15 +24,21 @@
class BNO085 : private BNO085_HAL
{
public:
+ typedef std::function LinearAccelerationCallbackFunc;
+ typedef std::function CalibratedGyroscopeCallbackFunc;
typedef std::function StabilizedRotationVectorWAccuracyCallbackFunc;
BNO085(std::shared_ptr const spi,
std::shared_ptr const nirq,
- StabilizedRotationVectorWAccuracyCallbackFunc const sensor_func);
+ LinearAccelerationCallbackFunc const acc_callback,
+ CalibratedGyroscopeCallbackFunc const gyro_callback,
+ StabilizedRotationVectorWAccuracyCallbackFunc const attitude_callback);
virtual ~BNO085() { }
- int config();
+ int enableAccelerometer();
+ int enableGyroscope();
+ int enableAttitude();
void spinOnce();
@@ -41,5 +47,7 @@ class BNO085 : private BNO085_HAL
private:
- StabilizedRotationVectorWAccuracyCallbackFunc const _sensor_func;
+ LinearAccelerationCallbackFunc const _acc_callback;
+ CalibratedGyroscopeCallbackFunc const _gyro_callback;
+ StabilizedRotationVectorWAccuracyCallbackFunc const _attitude_callback;
};
diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index 6990496..0000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright Alexander Entinger, MSc / LXRobotics GmbH
-# This docker file allows building and running of 10BASE-T1S user space driver for the onsemi NCN26010 SPI-MAC-PHY.
-FROM alpine:3.18
-
-RUN apk add git g++ make cmake linux-headers
-
-RUN cd /tmp && \
- git clone --recursive https://github.com/pika-spark/pika-spark-bno085-driver && \
- cd pika-spark-bno085-driver && \
- mkdir build && \
- cd build && \
- cmake .. && \
- make
-
-COPY start.sh /
-RUN chmod ugo+x /start.sh
-ENTRYPOINT ["/start.sh"]
diff --git a/docker/docker-build.sh b/docker/docker-build.sh
deleted file mode 100755
index 4cc6b66..0000000
--- a/docker/docker-build.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-IFS=$'\n\t'
-docker build --pull --no-cache --tag pika_spark_bno085_driver .
diff --git a/docker/docker-run.sh b/docker/docker-run.sh
deleted file mode 100755
index 894b577..0000000
--- a/docker/docker-run.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-IFS=$'\n\t'
-
-if [ "$(id -u)" != "0" ]; then
- echo "This script must be run as root."
- exit 1
-fi
-
-# SYSFS_GPIO_NUMBER = ((GPIO_PORT - 1) * 32) + GPIO_PIN
-# BNO085_nIRQ = MX8MM_IOMUXC_SPDIF_TX_GPIO5_IO3
-# BNO085_nRST = MX8MM_IOMUXC_SAI5_RXD1_GPIO3_IO22
-# BNO085_nBOOT = MX8MM_IOMUXC_SAI5_RXD2_GPIO3_IO23
-GPIO_NIRQ_NUM=131
-GPIO_NRST_NUM=86
-GPIO_NBOOT_NUM=87
-
-function finish {
- echo $GPIO_NIRQ_NUM > /sys/class/gpio/unexport
- echo $GPIO_NRST_NUM > /sys/class/gpio/unexport
- echo $GPIO_NBOOT_NUM > /sys/class/gpio/unexport
-}
-trap finish EXIT
-
-echo $GPIO_NIRQ_NUM > /sys/class/gpio/export
-echo $GPIO_NRST_NUM > /sys/class/gpio/export
-echo $GPIO_NBOOT_NUM > /sys/class/gpio/export
-
-modprobe spidev
-chmod ugo+rw /dev/spidev0.0
-
-sudo -u fio docker run -it --ulimit nofile=1024:1024 --rm -u 0 --privileged --device /dev/spidev0.0 -v /sys/class/gpio:/sys/class/gpio pika_spark_bno085_driver
diff --git a/docker/start.sh b/docker/start.sh
deleted file mode 100755
index aa7e26f..0000000
--- a/docker/start.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-export PATH="$PATH:/tmp/pika-spark-bno085-driver/build"
-echo "Starting pika-spark-bno085-driver ..."
-pika-spark-bno085-driver
diff --git a/launch/imu.py b/launch/imu.py
new file mode 100644
index 0000000..5f350c8
--- /dev/null
+++ b/launch/imu.py
@@ -0,0 +1,20 @@
+import os
+from launch import LaunchDescription
+from launch_ros.actions import Node
+
+def generate_launch_description():
+ os.environ['RCUTILS_CONSOLE_OUTPUT_FORMAT'] = '[{severity}] [{time}]: {message}'
+
+ return LaunchDescription([
+ Node(
+ package='pika_spark_bno085_driver',
+ executable='pika_spark_bno085_driver_node',
+ name='pika_spark_bno085_driver',
+ namespace='',
+ output='screen',
+ emulate_tty=True,
+ parameters=[
+ {'imu_topic': 'imu'},
+ ]
+ )
+ ])
diff --git a/main.cpp b/main.cpp
index f4538c3..8c65ebf 100644
--- a/main.cpp
+++ b/main.cpp
@@ -17,6 +17,11 @@
#include
#include
+#include
+#include
+
+#include
+
#include "spi.h"
#include "gpio-sysfs.h"
@@ -35,8 +40,26 @@ static int constexpr nIRQ_PIN = 131;
* MAIN
**************************************************************************************/
-int main(int /* argc */, char ** /* argv */) try
+int main(int argc, char ** argv) try
{
+ rclcpp::init(argc, argv);
+
+ auto const node = rclcpp::Node::make_shared("pika_spark_bno085_driver_node");
+
+ rclcpp::QoS imu_qos_profile(rclcpp::KeepLast(1), rmw_qos_profile_sensor_data);
+
+ node->declare_parameter("imu_topic", "joy");
+ auto const imu_topic = node->get_parameter("imu_topic").as_string();
+ auto const imu_topic_deadline = std::chrono::milliseconds(100);
+ auto const imu_topic_liveliness_lease_duration = std::chrono::milliseconds(1000);
+
+ imu_qos_profile.deadline(imu_topic_deadline);
+ imu_qos_profile.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC);
+ imu_qos_profile.liveliness_lease_duration(imu_topic_liveliness_lease_duration);
+
+ sensor_msgs::msg::Imu imu_msg;
+ auto const imu_pub = node->create_publisher(imu_topic, imu_qos_profile);
+
auto const gpio_nboot = std::make_shared(nBOOT_PIN);
gpio_nboot->gpio_set_dir(true);
gpio_nboot->gpio_set_value(1); /* Note: setting it to '0' activates bootloader mode. */
@@ -53,43 +76,74 @@ int main(int /* argc */, char ** /* argv */) try
gpio_nirq->gpio_set_dir(false);
gpio_nirq->gpio_set_edge("falling");
- auto arvrStabilizedRV_callback_last = std::chrono::steady_clock::now();
- auto const arvrStabilizedRV_callback = [&arvrStabilizedRV_callback_last](sh2_RotationVectorWAcc_t const & data)
+ auto const acc_callback = [node, &imu_msg](sh2_Accelerometer_t const & data)
+ {
+ RCLCPP_DEBUG_THROTTLE(node->get_logger(), *node->get_clock(), 1000UL,
+ "Acceleration [x, y, z,] = [%0.3f, %0.3f, %0.3f] m/s²",
+ data.x, data.y, data.z);
+
+ imu_msg.linear_acceleration.x = data.x;
+ imu_msg.linear_acceleration.y = data.y;
+ imu_msg.linear_acceleration.z = data.z;
+ };
+
+ auto const gyro_callback = [node, &imu_msg](sh2_Gyroscope_t const & data)
{
- auto const now = std::chrono::steady_clock::now();
- auto const diff_time = (now - arvrStabilizedRV_callback_last);
- auto const diff_time_us = std::chrono::duration_cast(diff_time).count();
- arvrStabilizedRV_callback_last = now;
-
- char msg[128] = {0};
- snprintf(msg,
- sizeof(msg),
- "[%4ld] [i, j, k, real, accuracy] = [%0.3f, %0.3f, %0.3f, %0.3f, %0.3f]",
- diff_time_us,
- data.i,
- data.j,
- data.k,
- data.real,
- data.accuracy);
- std::cout << msg << std::endl;
+ RCLCPP_DEBUG_THROTTLE(node->get_logger(), *node->get_clock(), 1000UL,
+ "Gyroscope [x, y, z,] = [%0.3f, %0.3f, %0.3f] rad/s",
+ data.x, data.y, data.z);
+
+ imu_msg.angular_velocity.x = data.x;
+ imu_msg.angular_velocity.y = data.y;
+ imu_msg.angular_velocity.z = data.z;
+ };
+
+ auto const attitude_callback = [node, imu_pub, &imu_msg](sh2_RotationVectorWAcc_t const & data)
+ {
+ RCLCPP_INFO_THROTTLE(node->get_logger(), *node->get_clock(), 250UL,
+ "Attitude [i, j, k, real, accuracy] = [%0.3f, %0.3f, %0.3f, %0.3f, %0.3f]",
+ data.i, data.j, data.k, data.real, data.accuracy);
+
+ imu_msg.header.stamp = node->now();
+ imu_msg.orientation.x = data.i;
+ imu_msg.orientation.y = data.j;
+ imu_msg.orientation.z = data.k;
+ imu_msg.orientation.w = data.real;
+
+ imu_pub->publish(imu_msg);
};
auto spi = std::make_shared("/dev/spidev0.0", SPI_MODE_3, 8, 3*1000*1000UL);
- auto bno085 = std::make_shared(spi, gpio_nirq, arvrStabilizedRV_callback);
+ auto bno085 = std::make_shared(spi, gpio_nirq, acc_callback, gyro_callback, attitude_callback);
/* Configure sensor for obtaining current orientation
* as a quaternion with accuracy estimation.
*/
- if (auto const rc = bno085->config(); rc != SH2_OK) {
- std::cerr << "config failed with error code " << rc << std::endl;
+ if (auto const rc = bno085->enableAccelerometer(); rc != SH2_OK) {
+ std::cerr << "\"enableAccelerometer()\" failed with error code " << rc << std::endl;
+ return EXIT_FAILURE;
+ }
+ if (auto const rc = bno085->enableGyroscope(); rc != SH2_OK) {
+ std::cerr << "\"enableGyroscope()\" failed with error code " << rc << std::endl;
+ return EXIT_FAILURE;
+ }
+ if (auto const rc = bno085->enableAttitude(); rc != SH2_OK) {
+ std::cerr << "\"enableAttitude()\" failed with error code " << rc << std::endl;
return EXIT_FAILURE;
}
- /* Run until killed by Ctrl+C to service
- * the sensor hub.
+ /* Run until killed by Ctrl+C to service the sensor hub.
*/
- for (;;)
+ RCLCPP_INFO(node->get_logger(), "%s init complete.", node->get_name());
+
+ while (rclcpp::ok())
{
+ /* Let ROS do its things. */
+ rclcpp::spin_some(node);
+
+ /* Check if an event has been signalled via the
+ * BNO085 nIRQ pin.
+ */
auto const gpio_nirq_fd = gpio_nirq->gpio_fd_open();
auto const rc_poll = gpio_nirq->gpio_poll(gpio_nirq_fd, 1000);
gpio_nirq->gpio_fd_close(gpio_nirq_fd);
@@ -102,6 +156,9 @@ int main(int /* argc */, char ** /* argv */) try
bno085->spinOnce();
}
+ RCLCPP_INFO(node->get_logger(), "%s shut down.", node->get_name());
+
+ rclcpp::shutdown();
return EXIT_SUCCESS;
}
catch (BNO085_Exception const & err)
diff --git a/package.xml b/package.xml
new file mode 100644
index 0000000..789686c
--- /dev/null
+++ b/package.xml
@@ -0,0 +1,24 @@
+
+
+
+ pika_spark_bno085_driver
+ 1.0.0
+ This package implements a ROS driver for the BNO085 9-DoF IMU.
+
+ Alexander Entinger
+
+ MIT
+
+ ament_cmake
+
+ l4xz
+ rclcpp
+ sensor_msgs
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+