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.
Now to get into the Qwiic ecosystem, the key will be one of the following Qwiic shields to match your preference of microcontroller:
You will also need a Qwiic cable to connect the shield to your accelerometer, choose a length that suits your needs.
Suggested Reading
If you aren't familiar with the Qwiic system, we recommend reading here for an overview.
Qwiic Connect System |
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.
Serial Communication
Gyroscope
Connecting Arduino to Processing
Qwiic Shield for Arduino & Photon Hookup Guide
Hardware Overview
Let's look over a few characteristics of the BNO080 sensor so we know a bit more about how it behaves.
Characteristic | Range |
---|---|
Operating Voltage | 1.65V - 3.6V |
Linear Acceleration Accuracy | ±.35m/s2 |
Gyroscope Accuracy | ±.35m/s2 |
I2C Address | 0x4B (S0 Pulled High) or 0x4A (S0 grounded) |
Pins
There are multiple rows of pins on the BNO080, the first row, used for the default I2C interface (configurable up to 400 kHz) is explained in the table below.
Pin Label | Pin Function | Input/Output | Notes |
---|---|---|---|
PS0 | Protocol Selection | Input | Configuration of the communication interface (Default: 0, I2C) |
PS1 | Protocol Selection | Input | Configuration of the communication interface (Default: 0, I2C) |
GND | Ground | Input | 0V/common voltage. |
3V3 | Power Supply | Input | Should be between 1.65 - 3.6V |
SDA | I2C Data Signal | Bi-directional | Bi-directional data line. Voltage should not exceed power supply (e.g. 3.3V). |
SCL | I2C Clock Signal | Input | Clock signal. Voltage should not exceed power supply (e.g. 3.3V). |
RST | Reset Signal | Input | Reset signal, active low, pull low to reset IC |
INT | Interrupt | Output | 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 | Clock | Input | Clock signal to synchronize controller and peripheral. |
SO | CIPO | Output | Controller in, peripheral out. Device sends data to the controller on this line. |
SI | COPI/ADDR | Input | Controller out, peripheral 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 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; if multiple sensors are connected to the bus with the pull-up resistors enabled, the parallel equivalent resistance will create too strong of a pull-up for the bus to operate correctly. As a general rule of thumb, disable all but one pair of pull-up resistors if multiple devices are connected to the bus. If you need to disconnect the pull up resistors they can be removed by removing the solder on the corresponding jumpers highlighted below.
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 0x4B (default) to 0x4A 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:
Let's get started by looking at the functions that set up the IMU.
Setup and Settings
boolean begin(uint8_t deviceAddress = BNO080_DEFAULT_ADDRESS, TwoWire &wirePort = Wire);
--- By default use the default I2C address, and use Wire port, otherwise, pass in a custom I2C address and wire port.void enableDebugging(Stream &debugPort = Serial);
--- Turn on debug printing. If user doesn't specify method of printing then Serial will be used.void enableRotationVector(uint16_t timeBetweenReports);
--- Enables the rotation vector to give a report everytimeBetweenReports
ms.void enableGameRotationVector(uint16_t timeBetweenReports);
--- Enables the game rotation vector to give a report everytimeBetweenReports
ms.void enableAccelerometer(uint16_t timeBetweenReports);
--- Enables the accelerometer withtimeBetweenReports
in ms.void enableGyro(uint16_t timeBetweenReports);
--- Enables the gyroscope withtimeBetweenReports
in ms.- **
void enableMagnetometer(uint16_t timeBetweenReports);
--- Enables the magnetometer withtimeBetweenReports
in ms. void enableStepCounter(uint16_t timeBetweenReports);
--- Enables the step counter withtimeBetweenReports
in ms.void enableStabilityClassifier(uint16_t timeBetweenReports);
--- Enables the stability classifier withtimeBetweenReports
in ms.void enableActivityClassifier(uint16_t timeBetweenReports, uint32_t activitiesToEnable,
uint8_t (&activityConfidences)[9]);
--- Enables the activity classifier with atimeBetweenReports
(in ms), theactivitiesToEnable
(0x1F to enable all activities) and theactivityConfidences[9]
array to store the IMU's confidence that the activity is occurring.void softReset();
--- Try to reset the IMU via softwareuint8_t resetReason();
--- Query the IMU for the reason it last resetfloat qToFloat(int16_t fixedPointValue, uint8_t qPoint);
--- Given a Q value, converts fixed point floating to regular floating point numbervoid setFeatureCommand(uint8_t reportID, uint16_t timeBetweenReports();
--- Used to enable different sensors and functions (features) of the IMU with to report withtimeBetweenReports
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 withtimeBetweenReports
ms between reports.void sendCommand(uint8_t command);
--- Sends a command to the sensor.void sendCalibrateCommand(uint8_t thingToCalibrate);
--- Sends calibrations commands to the sensor of choice. Possible arguments are listed below.CALIBRATE_ACCEL
CALIBRATE_GYRO
CALIBRATE_MAG
CALIBRATE_PLANAR_ACCEL
CALIBRATE_ACCEL_GYRO_MAG
--- Calibrates all sensors.CALIBRATE_STOP
--- Stops all calibration.
Communication and Data Handling
boolean waitForI2C();
--- Delay based polling for I2C trafficboolean receivePacket(void);
--- Receives an I2C packet from the IMU.boolean getData(uint16_t bytesRemaining);
--- Given a number of bytes, send the requests inI2C_BUFFER_LENGTH
chunksboolean sendPacket(uint8_t channelNumber, uint8_t dataLength);
--- Sends a packet of lengthdataLength
tochannelNumber
.void printPacket(void);
--- Prints the current shtp header and data packetsbool dataAvailable(void);
--- Checks if new data is available from the IMU.void parseInputReport(void);
--- Takes the data from the IMu and places it into the proper variable so they can be properly read.
Getting Values
float getQuatI();
--- Retrieves the i-axis quaternion from the IMU.float getQuatJ();
--- Retrieves the j-axis quaternion from the IMU.float getQuatK();
--- Retrieves the k-axis quaternion from the IMU.float getQuatReal();
--- Retrieves the real component of the quaternion from the IMU.float getQuatRadianAccuracy();
--- Retrieves the accuracy of the quaternion from the IMU in radians.uint8_t getQuatAccuracy();
--- Retrieves the accuracy of the quaternion from the IMU.float getAccelX();
--- Retrieves the x-axis acceleration.float getAccelY();
--- Retrieves the y-axis acceleration.float getAccelZ();
--- Retrieves the z-axis acceleration.uint8_t getAccelAccuracy();
--- Retrieves the accuracy of the accelerometer readings.float getGyroX();
--- Retrieves the x-axis gyroscope reading.float getGyroY();
--- Retrieves the y-axis gyroscope reading.float getGyroZ();
--- Retrieves the z-axis gyroscope reading.uint8_t getGyroAccuracy();
--- Retrieves the accuracy of the gyroscope reading.float getMagX();
--- Retrieves the x-axis magnetometer reading.float getMagY();
--- Retrieves the y-axis magnetometer reading.float getMagZ();
--- Retrieves the z-axis magnetometer reading.uint8_t getMagAccuracy();
--- Retrieves the accuracy of the magnetometer reading.
Sensor Calibration
Check the calibration procedure in order to calibrate the IMU with the functions listed below.
void calibrateAccelerometer();
--- Begins the calibration function for the IMU's accelerometer.void calibrateGyro();
--- Begins the calibration function for the IMU's gyroscope.void calibrateMagnetometer();
--- Begins the calibration function for the IMU's magnetometer.void calibratePlanarAccelerometer();
--- Begins the planar calibration function for the IMU's accelerometer.void calibrateAll();
--- Begins all calibration functions.void endCalibration();
--- Ends the active calibration functions.void saveCalibration();
--- Saves data from current calibration.
Special Functions
uint16_t getStepCount();
--- Gets the number of steps from the BNO080's onboard pedometer.uint8_t getStabilityClassifier();
--- Retrieves the stability classification, a number between 0 and 6.- 0 --- Unknown classification
- 1 --- On table
- 2 --- Stationary
- 3 --- Stable
- 4 --- Motion
- 5 --- Reserved
uint8_t getActivityClassifier();
--- Retrieves the Activity classification, a number between 0 and 8, based on a comparison of the confidence levels in each activity.- 0 --- Unknown
- 1 --- In Vehicle
- 2 --- On Bicycle
- 3 --- On Foot
- 4 --- Still
- 5 --- Tilting
- 6 --- Walking
- 7 --- Running
- 8 --- On stairs
Metadata Handling Functions
int16_t getQ1(uint16_t recordID);
--- Given a record ID, read the Q1 value from the metaData record in the Flash Record System (FRS). Q1 is used for all sensor data calculations.int16_t getQ2(uint16_t recordID);
--- Given a record ID, read the Q2 value from the metaData record in the FRS. Q2 is used in sensor bias.int16_t getQ3(uint16_t recordID);
--- Given a record ID, read the Q3 value from the metaData record in the FRS. Q3 is used in sensor change sensitivity.float getResolution(uint16_t recordID);
--- Given a record ID, reads the resolution value from the sensors metadatafloat getRange(uint16_t recordID);
--- Given a record ID, read the range value from the metaData record in the FRS for a given sensor- ****
uint32_t readFRSword(uint16_t recordID, uint8_t wordNumber);
** --- Given a record ID and a word number, find the word data. Used ingetQ1()
,getResolution()
, etc.. void frsReadRequest(uint16_t recordID, uint16_t readOffset, uint16_t blockSize);
--- Ask the sensor for data from the Flash Record System.bool readFRSdata(uint16_t recordID, uint8_t startLocation, uint8_t wordsToRead);
--- Reads data from the Flash Record System.
Global Variables
uint8_t shtpHeader[4];
--- In Hillcrest's Sensor Hubtransfer Protocol, each packet has a header of 4 bytesuint8_t shtpData[MAX_PACKET_SIZE];
--- Creates an array for SHTP data to be stored.uint8_t sequenceNumber[6] = {0, 0, 0, 0, 0, 0};
--- There are 6 com channels. Each channel has its own sequence number.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.
language:c
Serial.begin(9600); //Don't forget to enable Serial to talk to your microcontroller
Serial.println();
Serial.println("BNO080 Read Example");
Wire.begin(); //Begin the I2C bus
Wire.setClock(400000); //Increase I2C data rate to 400kHz
myIMU.begin();
myIMU.enableRotationVector(50); //Send data update every 50ms
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.
language:c
float quatI = myIMU.getQuatI();
float quatJ = myIMU.getQuatJ();
float quatK = myIMU.getQuatK();
float quatReal = myIMU.getQuatReal();
float quatRadianAccuracy = myIMU.getQuatRadianAccuracy();
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.
language:c
float x = myIMU.getAccelX();
float y = myIMU.getAccelY();
float z = myIMU.getAccelZ();
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.
language:c
float x = myIMU.getGyroX();
float y = myIMU.getGyroY();
float z = myIMU.getGyroZ();
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.
language:c
float x = myIMU.getMagX();
float y = myIMU.getMagY();
float z = myIMU.getMagZ();
byte accuracy = myIMU.getMagAccuracy();
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.
language:c
#define FRS_RECORDID_ACCELEROMETER 0xE302
#define FRS_RECORDID_GYROSCOPE_CALIBRATED 0xE306
#define FRS_RECORDID_MAGNETIC_FIELD_CALIBRATED 0xE309
#define FRS_RECORDID_ROTATION_VECTOR 0xE30B
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.
language:c
if (myIMU.dataAvailable() == true)
{
byte classification = myIMU.getStabilityClassifier();
if(classification == 0) Serial.print(F("Unknown classification"));
else if(classification == 1) Serial.print(F("On table"));
else if(classification == 2) Serial.print(F("Stationary"));
else if(classification == 3) Serial.print(F("Stable"));
else if(classification == 4) Serial.print(F("Motion"));
else if(classification == 5) Serial.print(F("[Reserved]"));
Serial.println();
}
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.
language:c
void loop()
{
//Look for reports from the IMU
if (myIMU.dataAvailable() == true)
{
//getActivityClassifier will modify our activityConfidences array
//It will return the most likely activity as well.
byte mostLikelyActivity = myIMU.getActivityClassifier();
Serial.print("Most likely activity: ");
printActivityName(mostLikelyActivity);
Serial.println();
Serial.println("Confidence levels:");
for(int x = 0 ; x < 9 ; x++)
{
printActivityName(x);
Serial.print(F(") "));
Serial.print(activityConfidences[x]);
Serial.print(F("%"));
Serial.println();
}
Serial.println();
}
}
//Given a number between 0 and 8, print the name of the activity
//See page 73 of reference manual for activity list
void printActivityName(byte activityNumber)
{
if(activityNumber == 0) Serial.print("Unknown");
else if(activityNumber == 1) Serial.print("In vehicle");
else if(activityNumber == 2) Serial.print("On bicycle");
else if(activityNumber == 3) Serial.print("On foot");
else if(activityNumber == 4) Serial.print("Still");
else if(activityNumber == 5) Serial.print("Tilting");
else if(activityNumber == 6) Serial.print("Walking");
else if(activityNumber == 7) Serial.print("Running");
else if(activityNumber == 8) Serial.print("On stairs");
}
The output of this code should look something like the below image.
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.
language:c
if(incoming == 's')
{
myIMU.saveCalibration(); //Saves the current dynamic calibration data (DCD) to memory
myIMU.endCalibration(); //Turns off all calibration
Serial.println("Calibration ended");
}
}
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.
language:c
void loop()
{
//Look for reports from the IMU
if (myIMU.receivePacket() == true)
{
myIMU.printPacket();
}
}
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.
This extra example isn't included in the library as it requires Processing. To grab it, go ahead and download or clone the BNO080 Github Repo.
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:
- Schematic (PDF) -- PDF schematic of the Qwiic VR IMU (BNO080).
- Eagle Files (ZIP) -- PCB design files for the Qwiic VR IMU (BNO080).
- BNO080 Datasheet (PDF) -- Loads of information about the Qwiic VR IMU (BNO080) electrical characteristics, registers, communication specifications, and more.
- Reference Manual
- Sensor Hub Transport Protocol
- Sensor Calibration Procedure
- Qwiic Landing Page
- GitHub Repos
- Product Repo -- Design files and example code all related to the Qwiic VR IMU (BNO080).
- Library GitHub Repo
- Arduino Library - Arduino library for the Qwiic VR IMU (BNO080).
Need some inspiration for your next project? Check out some of these related tutorials:
Dungeons and Dragons Dice Gauntlet
Blynk Board Washer/Dryer Alarm
9DoF Razor IMU M0 Hookup Guide
Raspberry Pi Zero Helmet Impact Force Monitor
Or check out this blog post for ideas: