Fully featured I2C and SPI driver for CEVA (Hilcrest)'s BNO080 and FSM300 Inertial Measurement Units.

Dependents:   BNO080-Examples BNO080-Examples

BNO080 Driver

by Jamie Smith / USC Rocket Propulsion Lab

After lots of development, we are proud to present our driver for the Hilcrest BNO080 IMU! This driver is inspired by SparkFun and Nathan Seidle's Arduino driver for this chip, but has been substantially rewritten and adapted.

It supports the main features of the chip, such as reading rotation and acceleration data, as well as some of its more esoteric functionality, such as counting steps and detecting whether the device is being hand-held.

Features

  • Support for 15 different data reports from the IMU, from acceleration to rotation to tap detection
  • Support for reading of sensor data, and automatic checking of update rate against allowed values in metadata
  • BNO_DEBUG switch enabling verbose, detailed output about communications with the chip for ease of debugging
  • Ability to tare sensor rotation and set mounting orientation
  • Can operate in several execution modes: polling I2C, polling SPI, and threaded SPI (which handles timing-critical functions in a dedicated thread, and automatically activates when the IMU has data available)
    • Also has experimental support for using asynchronous SPI transactions, allowing other threads to execute while communication with the BNO is occurring. Note that this functionality requires a patch to Mbed OS source code due to Mbed bug #13941
  • Calibration function
  • Reasonable code size for what you get: the library uses about 4K of flash and one instance of the object uses about 1700 bytes of RAM.

Documentation

Full Doxygen documentation is available online here

Example Code

Here's a simple example:

BNO080 Rotation Vector and Acceleration

#include <mbed.h>
#include <BNO080.h>

int main()
{
	Serial pc(USBTX, USBRX);

	// Create IMU, passing in output stream, pins, I2C address, and I2C frequency
	// These pin assignments are specific to my dev setup -- you'll need to change them
	BNO080I2C imu(&pc, p28, p27, p16, p30, 0x4a, 100000); 

	pc.baud(115200);
	pc.printf("============================================================\n");

	// Tell the IMU to report rotation every 100ms and acceleration every 200ms
	imu.enableReport(BNO080::ROTATION, 100);
	imu.enableReport(BNO080::TOTAL_ACCELERATION, 200);

	while (true)
	{
		wait(.001f);
		
		// poll the IMU for new data -- this returns true if any packets were received
		if(imu.updateData())
		{
			// now check for the specific type of data that was received (can be multiple at once)
			if (imu.hasNewData(BNO080::ROTATION))
			{
				// convert quaternion to Euler degrees and print
				pc.printf("IMU Rotation Euler: ");
				TVector3 eulerRadians = imu.rotationVector.euler();
				TVector3 eulerDegrees = eulerRadians * (180.0 / M_PI);
				eulerDegrees.print(pc, true);
				pc.printf("\n");
			}
			if (imu.hasNewData(BNO080::TOTAL_ACCELERATION))
			{
				// print the acceleration vector using its builtin print() method
				pc.printf("IMU Total Acceleration: ");
				imu.totalAcceleration.print(pc, true);
				pc.printf("\n");
			}
		}
	}

}


If you want more, a comprehensive, ready-to-run set of examples is available on my BNO080-Examples repository.

Credits

This driver makes use of a lightweight, public-domain library for vectors and quaternions available here.

Changelog

Version 2.1 (Nov 24 2020)

  • Added BNO080Async, which provides a threaded implementation of the SPI driver. This should help get the best performance and remove annoying timing requirements on the code calling the driver
  • Added experimental USE_ASYNC_SPI option
  • Fixed bug in v2.0 causing calibrations to fail

Version 2.0 (Nov 18 2020)

  • Added SPI support
  • Refactored buffer system so that SPI could be implemented as a subclass. Unfortunately this does substantially increase the memory usage of the driver, but I believe that the benefits are worth it.

Version 1.3 (Jul 21 2020)

  • Fix deprecation warnings and compile errors in Mbed 6
  • Fix compile errors in Arm Compiler (why doesn't it have M_PI????)

Version 1.2 (Jan 30 2020)

  • Removed accidental IRQ change
  • Fixed hard iron offset reading incorrectly due to missing cast

Version 1.1 (Jun 14 2019)

  • Added support for changing permanent orientation
  • Add FRS writing functions
  • Removed some errant printfs

Version 1.0 (Dec 29 2018)

  • Initial Mbed OS release
Revision:
3:197ad972fb7c
Parent:
2:2269b723d16a
Child:
6:5ba996be5312
--- a/BNO080.h	Sat Dec 29 04:09:34 2018 -0800
+++ b/BNO080.h	Fri Jun 14 20:31:37 2019 -0700
@@ -27,7 +27,7 @@
 #include "BNO080Constants.h"
 
 // useful define when working with orientation quaternions
-#define SQRT_2 1.414213562
+#define SQRT_2 1.414213562f
 
 /**
   Class to drive the BNO080 9-axis IMU.
@@ -63,8 +63,8 @@
 
 #define SHTP_HEADER_SIZE 4
 
-// Arbitrarily chosen, but should hopefully be large enough for all packets we need.
-// If you enable lots of sensor reports and get an error, you might need to increase this.
+	// Arbitrarily chosen, but should hopefully be large enough for all packets we need.
+	// If you enable lots of sensor reports and get an error, you might need to increase this.
 #define STORED_PACKET_SIZE 128 
 
 	/// Each SHTP packet has a header of 4 uint8_ts
@@ -397,7 +397,7 @@
 		   PinName user_INTPin, 
 		   PinName user_RSTPin,
 		   uint8_t i2cAddress=0x4a, 
-		   int i2cPortSpeed=400000);
+		   int i2cPortSpeed=100000);
 
 	/**
 	 * Resets and connects to the IMU.  Verifies that it's connected, and reads out its version
@@ -460,13 +460,25 @@
 	 * NOTE: this driver provides the macro SQRT_2 to help with entering values from that table.
 	 *
 	 * NOTE 2: this setting does not persist and will have to be re-applied every time the chip is reset.
-	 * It is possible to set an FRS record and cause it to persist, but that method is not currently
-	 * supported by this driver,
+	 * Use setPermanentOrientation() for that.
 	 *
 	 * @param orientation quaternion mapping from IMU space to world space.
 	 */
 	void setSensorOrientation(Quaternion orientation);
 
+	/**
+	 * Sets the orientation quaternion, telling the sensor how it's mounted
+	 * in relation to world space. See page 40 of the BNO080 datasheet.
+	 *
+	 * Unlike setSensorOrientation(), this setting will persist across sensor restarts.
+	 * However, it will also take a few hundred milliseconds to write.
+	 *
+	 * @param orientation quaternion mapping from IMU space to world space.
+	 *
+	 * @return true if the operation succeeded, false if it failed.
+ 	*/
+	bool setPermanentOrientation(Quaternion orientation);
+    
 	// Report functions
 	//-----------------------------------------------------------------------------------------------------------------
 
@@ -654,6 +666,18 @@
 	int16_t floatToQ(float qFloat, uint8_t qPoint);
 
 	/**
+	 * Given a floating point value and a Q point, convert to Q
+	 * See https://en.wikipedia.org/wiki/Q_(number_format)
+	 *
+	 * This version is used for the signed 32-bit values in metadata records.
+     *
+	 * @param qFloat
+	 * @param qPoint
+	 * @return
+	 */
+	int32_t floatToQ_dword(float qFloat, uint16_t qPoint);
+
+	/**
 	 * 	Tell the sensor to do a command.
 	 * 	See SH-2 Reference Manual section 6.3.8 page 42, Command request
 	 * 	The caller is expected to set shtpData 3 though 11 prior to calling
@@ -684,6 +708,20 @@
 	bool readFRSRecord(uint16_t recordID, uint32_t* readBuffer, uint16_t readLength);
 
 	/**
+	 * Write a record to the FRS (Flash Record System) on the IMU.  FRS records are composed of 32-bit words,
+	 * with the size of each record determined by the record type.
+	 *
+	 * Will block until the entire record has been written.
+	 * @param recordID Record ID to write.  See SH-2 figures 28 and 29 for a list of these.  Sometimes also called
+	 * the "FRS Type" by the datasheet (???).
+	 * @param buffer Buffer to write data into.
+	 * @param length Amount of words to write to the record.  Must be <= the length of the record.
+	 *
+	 * @return whether the request succeeded
+	 */
+	bool writeFRSRecord(uint16_t recordID, uint32_t* buffer, uint16_t length);
+
+	/**
 	 * Reads a packet from the IMU and stores it in the class variables.
 	 *
 	 * @param timeout how long to wait for there to be a packet