Qwiic VR IMU (BNO080) Hookup Guide

Introduction

Bosch’s BNO080 is a combination triple axis accelerometer/gyro/magnetometer packaged with an ARM Cortex M0+ running powerful algorithms. The BNO080 Inertial Measurement Unit (IMU) produces accurate rotation vector headings, excellently suited for VR and other heading applications with a static rotation error of 2 degrees or less. It’s what we’ve been waiting for: all the sensor data is combined and drift corrected into meaningful, accurate IMU information. It’s perfect for any project that needs to sense orientation or motion. We’ve taken this IMU and stuck it on a Qwiic enabled breakout board, in order to make interfacing with the tiny, QFN package a bit easier to connect.

In this hookup guide, we’ll connect our sensor up to our microcontroller of choice and separately read the rotation vectors (which is what we will mainly want), acceleration vectors, gyro values, and magnetometer vectors. We’ll check out how to implement the step counter on the BNO080 in order to use it as a pedometer. We’ll also read Q values and various other metadata from the sensor. Knowing what activity you’re performing is important so we’ll learn how to classify what activity the IMU is performing (i.e. Sitting still, moving, biking, walking, running, etc…) and how confident the IMU is that each activity is being performed. The examples will also show how to calibrate our hardware to give us the most accurate readings possible. Printing out raw packets will also be examined for debugging purposes. Finally, we’ll examine how to configure the sensor on different I2C ports and addresses. A bonus example is provided in Processing to show us how to use quaternion data to orient a cube.

Required Materials

To get started, you’ll need a microcontroller to, well, control everything.

Suggested Reading

If you aren’t familiar with our new Qwiic system, we recommend reading here for an overview. We would also recommend taking a look at the following tutorials if you aren’t familiar with them. We also delve into Processing in this tutorial, if you aren’t familiar, check out the below tutorial on Processing.

Interrupt, active low, pulls low when the BNO080 is ready for communication.

Also broken out on the board is a Serial Peripheral Interface (SPI) which can run data up to 3MHz. The pins for this interface are outlined below. On any pin, the voltage should not exceed that supplied on the 3V3 pin.

Pin Label

Pin Function

Input/Output

Notes

GND

Ground

Input

0V/Common Voltage

3V3

Power

Input

Should be between 1.65 - 3.6V

SCK

Master Clock

Input

Clock signal to synchronize master and slave.

SO

MISO

Output

Master in , slave out. Device sends data to the master on this line.

SI

MOSI/ADDR

Input

Master out, slave in. Device receives data from the microcontroller on this line. Tie to 3.3V to change I2C address from 0x4A to 0x4B

CS

Chip Select

Input

Chip select, active low, used as chip select/slave select on SPI

WAK

Wake

Input

Active low, Used to wake the processor from a sleep mode.

RST

Reset Signal

Input

Reset signal, active low, pull low to reset IC

You can also use the UART interface at up to 3 Mbps or a simplified UART called UART-RVC (Used for robotic vacuum cleaners) which can run at a data rate of 115200 kbps. The UART interface is in the middle of the board, with the black and green pins labeled on the back of the board as shown below. These serial pins have been arranged to work with our Serial Basic board to make interfacing to a computer simple and fast. The GRN and BLK labels help align the serial connection properly.

Also note the BOOT pin next to the Qwiic connector, which is necessary for configuration of the communication mode. If the BOOT pin is low upon reset or power up, the chip will go into bootloader mode to allow for programming of new firmware.

Optional Features

Pull-Up Resistor Jumper

The Qwiic VR IMU has onboard I2C pull up resistors, which can be removed by removing the solder from the jumper highlighted below. Only remove this solder if you are using your own pull-ups on the I2C lines.

Protocol Selection Jumpers

You can use the PS0 and PS1 jumpers to change the communication protocol that the BNO080 is using. The jumpers are left open (0) by default, and the following configurations will allow for their corresponding communications protocols.

PS0

PS1

Interface

0

0

I2C

1

0

UART-RVC

0

1

UART

1

1

SPI

The jumpers themselves are located on the back of the board, shown below

I2C Jumper

You can also change the address of the BNO080 from 0x4A (default) to 0x4B by connecting the I2C ADR jumper. The jumper itself is shown in the below image.

Axis Reference

Also, be sure to check out the labeling on the front of the board that indicates the orientation of the positive X, Y, and Z axes so you know which way your data is pointing.

Hardware Assembly

If you haven’t yet assembled your Qwiic Shield, now would be the time to head on over to that tutorial.
With the shield assembled, SparkFun’s new Qwiic environment means that connecting the sensor could not be easier. Just plug one end of the Qwiic cable into the BNO080 breakout, the other into the Qwiic Shield of your choice and you’ll be ready to upload a sketch and figure out how you’re moving that board. It seems like it’s too easy too use, but that’s why we made it that way!

Library Overview

Note: This example assumes you are using the latest version of the Arduino IDE on your desktop. If this is your first time using Arduino, please review our tutorial on installing the Arduino IDE. If you have not previously installed an Arduino library, please check out our installation guide.

Before we get into programming our IMU, let’s download and check out the available functions in our library. SparkFun has written a library to control the Qwiic VR IMU. You can obtain these libraries through the Arduino Library Manager. Search for SparkFun BNO080 Cortex Based IMU and you should be able to install the latest version. If you prefer manually downloading the libraries from the GitHub repository, you can grab them here:

void enableActivityClassifier(uint16_t timeBetweenReports, uint32_t activitiesToEnable,uint8_t (&activityConfidences)[9]); — Enables the activity classifier with a timeBetweenReports (in ms), the activitiesToEnable (0x1F to enable all activities) and the activityConfidences[9] array to store the IMU’s confidence that the activity is occurring.

void softReset(); — Try to reset the IMU via software

uint8_t resetReason(); — Query the IMU for the reason it last reset

float qToFloat(int16_t fixedPointValue, uint8_t qPoint); — Given a Q value, converts fixed point floating to regular floating point number

void setFeatureCommand(uint8_t reportID, uint16_t timeBetweenReports(); — Used to enable different sensors and functions (features) of the IMU with to report with timeBetweenReports ms between reports.

void setFeatureCommand(uint8_t reportID, uint16_t timeBetweenReports, uint32_t specificConfig); — Used to enable different sensors and functions (features) of the IMU with to report with timeBetweenReports ms between reports.

uint8_t commandSequenceNumber = 0; — Commands have a sequence number as well. These are inside command packet, the header uses its own sequence number for each channel.

uint32_t metaData[MAX_METADATA_SIZE]; — There is more than 10 words in a metadata record but we’ll stop at Q point 3

Arduino Example Code

Now that we have our library installed, we can get started playing around with our examples to learn more about how the IMU behaves. From there we’ll be able to build our own custom code to integrate the sensor into a project.

Example 1 - Rotation Vector

This first example gets us started taking a reading of our complex valued rotation vector, or quaternion, which tells us how we are oriented. To get started, open up Example1-RotationVector under File > Examples > SparkFun BNO080 Cortex Based IMU > Example1-RotationVector. To access all of the functions in the BNO080 library we’ll have to include the library and create an IMU object, this is accomplished in the following lines of code.

language:c
#include "SparkFun_BNO080_Arduino_Library.h"
BNO080 myIMU;

In our setup(), we’ll have to initialize the sensor and enable the parts of the sensor (gyro, accelerometer, magnetometer) that we want to obtain readings from. We’ll also tell the IMU how often we want a reading from our sensor of choice by passing this value into our enable function in ms. The code outlining this sensor setup is shown below. Pay attention to this as we’ll be performing a similar setup in many of the remaining examples.

Now that our sensor is setup, we can look at our void loop() to see how we obtain and print data. When the loop executes, it begins by checking to see if the sensor has new data with myIMU.dataAvailable(), which returns true when new data is available. We then proceed to get the i, j, k, and real quaternion values along with the accuracy in radians of our measurement using the following lines of code.

Printing the rotation vector is then as easy as using a few Serial.print(quatI, 2) statements. Uploading the code and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Example 2 - Accelerometer

Examples 2 deals with pulling the accelerometer values from our sensor to figure out how it is moving. To get started, open up Example2-Accelerometer under File > Examples > SparkFun BNO080 Cortex Based IMU > Example2-Accelerometer. At first glance, you’ll notice that the way we set up our IMU is nearly identical to the first example. The one difference being in our setup(), where we call myIMU.enableAccelerometer(50); instead of myIMU.enableRotationVector(50); which has the accelerometer report a value every 50 ms. We once again obtain and output our data in our void loop() by waiting for data using myIMU.dataAvailable(). Once data is available we use the following lines of code to get our x, y, and z acceleration values.

We can then print these values to get our acceleration vector, uploading the code, and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Example 3 - Gyro

In example 3, we’ll pull values from the IMU’s gyroscope to get a vector for our angular velocity. To get started, open up Example3-Gyro under File > Examples > SparkFun BNO080 Cortex Based IMU > Example3-Gyro. The differences between this example and example 1 are very similar to the differences between examples 1 & 2. We initialize the sensor the exact same way as example 1 only calling myIMU.enableGyro(50); instead of myIMU.enableRotationVector(50); to have the gyro report it’s value every 50 ms. We once again obtain and output our data in our void loop() by waiting for data using myIMU.dataAvailable(). Once data is available, we use the following lines of code to get our x, y, and z gyroscope values.

We can then print these values to get our gyroscope angular velocity vector, uploading the code, and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Also, check out the values in a graph by opening the Serial Plotter to the same baud rate to see the readings from each gyroscope channel plotted against each other. Rotate the IMU and see how the values respond, I got the following output just letting the IMU swing on its cable.

Example 4 - Magnetometer

The following example will get us reading a vector for the magnetic field. To get started, open up Example4-Magnetometer under File > Examples > SparkFun BNO080 Cortex Based IMU > Example4-Magnetometer. The differences between this example and example 1 are very similar to the differences between examples 1 & 2, are you starting to see a pattern here? We initialize the sensor the exact same way as example 1 only calling myIMU.enableMagnetometer(50); instead of myIMU.enableRotationVector(50); to have the magnetometer report it’s value every 50 ms. We once again obtain and output our data in our void loop() by waiting for data using myIMU.dataAvailable(). Once data is available we use the following lines of code to get our x, y, and z magnetometer values. We also obtain the uncertainty in the magnetometer.

We can then print these values to get our magnetometer vector, uploading the code, and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Example 5 - Step Counter

The BNO080 has some really neat built in features due to its built in Cortex. One of these is a built in step counter. To get started with this pedometer, open up Example5-StepCounter under File > Examples > SparkFun BNO080 Cortex Based IMU > Example5-StepCounter. The differences between this example and example 1 are very similar to the differences in the previous examples. We initialize our step counter function with a lower sample rate than our previous functions as we don’t expect steps to happen at as high of a rate. Due to this, we call myIMU.enableStepCounter(500) to allow for a half a second in between reports. We once again obtain and output our data in our void loop() by waiting for data using myIMU.dataAvailable(). Once data is available, we pull the amount of steps from the IMU using unsigned int steps = myIMU.getStepCount() to initialize and populate an unsigned int with the step count. We then print this value to our serial monitor. Uploading the code and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Example 6 - Metadata

This example shows us how to retrieve the static metadata from the different sensors in the IMU. To get started, open up Example6-Metadata under File > Examples > SparkFun BNO080 Cortex Based IMU > Example6-Metadata. Notice how this example has an empty void loop()? Everything just happens once in our setup() loop. To get the metadata for a sensor, we simply pass its corresponding FRS_RECORD_ID into getRange(), getResolution(), getQ1(), getQ2(), and getQ3() to retrieve the metadata for a sensor. The different FRS_RECORD_ID variables are shown below.

These are then used like so to print the different parts of each sensors metadata. For a little bit more on metadata, check out page 29 of the Reference Manual. Uploading the code and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Example 7 - Stability Classifier

This example sketch allows us to use the built in stability classifier to figure out how stable the IMU is. To get started, open up Example7-StabilityClassifier under File > Examples > SparkFun BNO080 Cortex Based IMU > Example7-StabilityClassifier. This example is very similar to our first few examples in that we must call myIMU.enableStabilityClassifier(50); in our setup() function to have the stability classifier report its data every 50 ms. However, we also need to call myIMU.calibrateGyro() in our setup() function to enable all of our stability classification outputs. We then check if data is available using myIMU.dataAvailable(). If it is, we pull the stability classification (a number from 0-5 using myIMU.getStabilityClassifier() ) and output the text corresponding to the classification. The code that does this in the void loop() is shown below. Also, take note of which number corresponds to which activity.

Uploading the code and opening the Serial Monitor to a baud rate of 9600 should yield an output similar to the below image.

Example 8 - Activity Classifier

The activity classifier is somewhat similar to the stability classifier in that it uses the on-board cortex to determine what activity the IMU is doing. To get started, open up Example8-ActivityClassifier under File > Examples > SparkFun BNO080 Cortex Based IMU > Example8-ActivityClassifier. To set up the activity classifier, we need to tell the IMU which activities to look for. We do this using a 32-bit word. There are only 8 possible activities at the moment, so we set our word enableActivities = 0x1F to enable everything. The activity classifier also gives a confidence level in each activity. To store these confidences, we create a variable byte activityConfidences[9] above our setup() function. Then, we can set up the activity classifier in our setup() function by calling myIMU.enableActivityClassifier(50, enableActivities, activityConfidences);. Using this function enables the activity classifier with 50 ms between reports, activities specified by enableActivities, and their confidences are stored in activityConfidences. We then check if data is available using myIMU.dataAvailable(). If it is, we pull the activity classification (a number from 0-9 using myIMU.getActivityClassifier()) and output the text corresponding to the classification. The code that does this is shown below. Take note of which number corresponds to which activity.

Example 9 - Calibrate

When moving between different magnetic environments (different rooms, indoors, outdoors, etc…), it might be necessary to recalibrate your IMU to obtain the best readings. In order to do this, we’ll run a calibration function,. To get started, open up Example9-Calibrate under File > Examples > SparkFun BNO080 Cortex Based IMU > Example9-Calibrate. In our setup function, we call the function myIMU.calibrateAll() to begin calibration of our sensor. We also need to make sure the we enable our game rotation vector and magnetometer as these are necessary for calculating the calibration of the magnetometer. Go ahead and upload the code to the IMU and open the serial monitor to 9600 baud. Take a look at your output and look for the calibration status. This should probably say Unreliable. Make sure you’re in a clean magnetic environment and go through the calibration steps listed in the calibration procedure. Once your sensor is calibrated, your accuracy should change from Unreliable to Medium or High. After calibration, send an s to your microcontroller over the serial monitor to run the following code and save the calibration.

Your Serial Monitor should look something like the following image when the sensor is being calibrated. Remember, when your confidence levels are satisfactory, send an s to save the calibration.

Example 10 - Print Packet

Sometimes it’s easier to look at the raw data coming from the sensor for debugging purposes. This example shows you how to do just that. To get started, open up Example10-PrintPacket under File > Examples > SparkFun BNO080 Cortex Based IMU > Example10-PrintPacket. In our setup, we set up the sensors that we would like to use (in this case, we’ll set up the magnetometer and accelerometer with 1000 ms sample rates). Since we’re most likely debugging in this mode, we’ll also call myIMU.enableDebugging(Serial). Our void loop() then simply listens for and prints packets using the below code.

Your Serial Monitor should look something like the following image with this example code uploaded. Make sure to change the baud rate to 115200 as opposed to 9600 like the previous examples.

Example 11 - Advanced Configuration

The final example simply shows us how to configure the sensor on different addresses and I2C buses. To get started, open up Example11-AdvancedConfig under File > Examples > SparkFun BNO080 Cortex Based IMU > Example11-AdvancedConfig. This is simply a matter of setting up the sensor differently. Instead of calling myIMU.begin() with no arguments, we call it as myIMU.begin(0x4A, Wire1). If we’ve pulled the SI pin high, the address will be 0x4B. We can also set up the sensor on a different I2C bus if by passing in Wire2 in place of Wire1.

Bonus Example - Serial Cube Visualizer

Note: Processing is a software that enables visual representation of data, among other things. If you've never dealt with Processing before, we recommend you also check out the Arduino to Processing tutorial. Follow the below button to go ahead and download and install Processing.

Processing listens for serial data, so we’ll need to get our Arduino producing serial data that makes sense to Processing. The required Arduino sketch is located in Qwiic_IMU_BNO080 > Software > Serial_Cube_Rotate > Serial_Cube_Rotate.ino. This sketch simply prints a list of our quaternions separated by a comma over serial for Processing to listen to.

Once this sketch is uploaded, we need to tell Processing how to turn this data into a visualization. The Processing sketch to do this is located one folder above the Arduino sketch, in Qwiic_IMU_BNO080 > Software > Serial_Cube_Rotate.pde. Open the Serial_Cube_Rotate file in Processing. Before running the sketch, we’ll need to download ToxicLibs, a library used for computational design. To do this, go to Sketch > Import Library… > Add Library…. Then search for and download ToxicLibs. Attempting to run the Processing sketch will show us available serial ports in the debug window from this line of code.

language:c
myPort = new Serial(this, Serial.list()[0], 115200);

Identify which serial port your Arduino is on. For instance, my RedBoard is on COM6, which corresponds to [1] in the image below, so I will need to change 0 to 1 in the following line to ensure Processing is listening to the correct serial port.

Once we’ve done this, we should be able to run the Processing sketch and it will give us a nice visualization of how our IMU is oriented in 3D space as a cube. Try rotating the IMU to see how it responds. You should get a neat little output like the one in the below GIF.

Resources and Going Further

Thanks for reading! We’re excited to see what you build with the Qwiic VR IMU. If you’re left needing more BNO080-related documentation, check out some of these resources:

In 2003, CU student Nate Seidle fried a power supply in his dorm room and, in lieu of a way to order easy replacements, decided to start his own company. Since then, SparkFun has been committed to sustainably helping our world achieve electronics literacy from our headquarters in Boulder, Colorado.

No matter your vision, SparkFun's products and resources are designed to make the world of electronics more accessible. In addition to over 2,000 open source components and widgets, SparkFun offers curriculum, training and online tutorials designed to help demystify the wonderful world of embedded electronics. We're here to help you start something.