Electronics Software Development

Using The Arduino_LSM6DS3 Library To Access The Arduino Uno WiFi Rev2 IMU

Arduino IMU Graphic
Written by John Woolsey

Skill Level: Intermediate

Table Of Contents

Introduction

This tutorial will teach you how to retrieve and display sensor readings from the on-board inertial measurement unit (IMU) of the Arduino Uno WiFi Rev2 development board. A basic understanding of electronics and programming is expected along with some familiarity with the Arduino platform. If you are new to the Arduino platform or would just like to refresh your knowledge, please see our Blink: Making An LED Blink On An Arduino Uno tutorial before proceeding with this one.

The resources created for this tutorial are available on GitHub for your reference.

What Is Needed

  • Linux, macOS, Or Windows Based Computer With A USB Port
  • Arduino IDE
  • Arduino Uno WiFi Rev2 (available on Arduino and SparkFun) With Compatible USB Cable

Background Information

The Arduino Uno WiFi Rev2 development board has an on-board IMU with a 3-axis accelerometer, a 3-axis gyroscope, and an embedded temperature sensor. This IMU is the LSM6DS3 device provided by ST Microelectronics (datasheet) accessed over the SPI serial bus interface.

I previously wrote the Accessing The IMU On The New Arduino Uno WiFi Rev2 article shortly after that board was released. The article utilized the SparkFun LSM6DS3 Breakout library since there was no official Arduino support available at the time. Arduino recently released its own Arduino_LSM6DS3 library for accessing the IMU that we will be using in this tutorial.

The accelerometer and gyroscope data is accessed a bit differently between the two libraries. In addition, the Arduino_LSM6DS3 library does not include access to the on-chip temperature sensor, but I have requested this enhancement. (See issue on GitHub)

My development system consists of the Arduino Uno WiFi Rev2 development board connected to a macOS based computer running the desktop Arduino IDE. If you are using a different computer setup, the vast majority of this tutorial should still apply, however, some minor changes may be necessary.

If you need assistance with your particular setup, post a question in the comments section below and I, or someone else, can try to help you.

Writing The Software

The first thing we need to do is open the Arduino IDE and install the Arduino_LSM6DS3 library from within the Library Manager (Main Menu > Tools > Manage Libraries…).

Next, create a new sketch named LSM6DS3_Arduino_Simple with the code shown below.

// LSM6DS3_Arduino_Simple - LSM6DS3_Arduino_Simple.ino
//
// Description:
// Retrieves motion data from the on-board LSM6DS3 IMU of the Arduino Uno WiFi
// Rev2 using the Arduino_LSM6DS3 library and displays readings in the Serial
// Monitor.
//
// Created by John Woolsey on 01/28/2020.
// Copyright (c) 2019 Woolsey Workshop.  All rights reserved.


// Includes
#include <Arduino_LSM6DS3.h>


void setup() {
   Serial.begin(9600);  // initialize serial bus (Serial Monitor)
   while (!Serial);  // wait for serial initialization
   Serial.print("LSM6DS3 IMU initialization ");
   if (IMU.begin()) {  // initialize IMU
      Serial.println("completed successfully.");
   } else {
      Serial.println("FAILED.");
      IMU.end();
      while (1);
   }
   Serial.println();
}


void loop() {
   char buffer[8];    // string buffer for use with dtostrf() function
   float ax, ay, az;  // accelerometer values
   float gx, gy, gz;  // gyroscope values

   // Retrieve and print IMU values
   if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()
      && IMU.readAcceleration(ax, ay, az) && IMU.readGyroscope(gx, gy, gz)) {
      Serial.print("ax = ");  Serial.print(dtostrf(ax, 4, 1, buffer));  Serial.print(" g, ");
      Serial.print("ay = ");  Serial.print(dtostrf(ay, 4, 1, buffer));  Serial.print(" g, ");
      Serial.print("az = ");  Serial.print(dtostrf(az, 4, 1, buffer));  Serial.print(" g, ");
      Serial.print("gx = ");  Serial.print(dtostrf(gx, 7, 1, buffer));  Serial.print(" °/s, ");
      Serial.print("gy = ");  Serial.print(dtostrf(gy, 7, 1, buffer));  Serial.print(" °/s, ");
      Serial.print("gz = ");  Serial.print(dtostrf(gz, 7, 1, buffer));  Serial.println(" °/s");
   }
   delay(1000);  // wait one second between readings
}

The setup() function initializes the serial port (for display to the Serial Monitor) and the IMU. The loop() function retrieves and displays the IMU values in the Serial Monitor with a delay of one second between readings. I am using the dtostrf() function here to convert floating point numbers to nicely formatted strings for display.

Verify and save your sketch when you are done.

Running And Testing The System

Open the Serial Monitor (Main Menu > Tools > Serial Monitor) window and upload your sketch to the Arduino board. You should see something like the following being displayed.

LSM6DS3 IMU initialization completed successfully.

ax =  0.0 g, ay = -0.0 g, az =  1.0 g, gx =     0.9 °/s, gy =    -4.3 °/s, gz =    -3.1 °/s
ax =  0.0 g, ay = -0.0 g, az =  1.0 g, gx =     0.9 °/s, gy =    -4.5 °/s, gz =    -3.1 °/s
ax =  0.0 g, ay =  0.0 g, az =  1.0 g, gx =     1.0 °/s, gy =    -4.4 °/s, gz =    -3.1 °/s
ax =  0.0 g, ay =  0.0 g, az =  1.0 g, gx =     1.0 °/s, gy =    -4.5 °/s, gz =    -3.1 °/s
ax =  0.0 g, ay =  0.0 g, az =  1.0 g, gx =     1.2 °/s, gy =    -4.3 °/s, gz =    -3.0 °/s

Congratulations, you are now able to access the IMU and retrieve motion sensor readings from your Arduino Uno WiFi Rev2. These readings are showing you the acceleration, in g, that your device is experiencing along with how quickly, in degrees per second, the device is rotating around particular axes in 3-dimensional space. If it is sitting still on your desk, all readings should be close to 0, except for az, which should be close to 1 due to the effect of earth’s gravity.

Pick up your Arduino and move, tilt, and shake it to see how the readings change.

Displaying Rotation Angles

We could have stopped here and called it a success, but it is often nice to see our data in a different form using rotation angles, e.g. roll, pitch, and yaw. Sometimes people refer to these as inclination or Euler angles. Let’s make some changes to our sketch to add that capability. For additional information on roll, pitch, and yaw, see Aircraft principal axes in Wikipedia.

Duplicate (Save As…) your original LSM6DS3_Arduino_Simple sketch and name the new one as LSM6DS3_Arduino_Rotation. This new sketch is the one we will be modifying.

There is already a library available that will calculate the rotation angles for us. Search for and install the Madgwick library provided by Arduino from the Arduino Library Manager. Once installed, include the library in your sketch at the end of the Includes section.

#include <MadgwickAHRS.h>

Next we need to define a sample rate, in Hz, specifying how often the measurements will be read from the IMU and the rotation angles will be calculated. Add the following statement after the Includes section.

#define SAMPLE_RATE 10

Now we need to instantiate the Madgwick filter class that will perform our rotation angle calculations. Add the following line just before the setup() function.

Madgwick filter;

At the very bottom of the setup() function, add a line to initialize the Madgwick filter with the specified sample rate.

filter.begin(SAMPLE_RATE);

Change the name of the loop() function to printValues() and remove the last delay() line. This probably seems a bit strange, but it is the easiest way to retain our previous work.

Now add the loop() function back, just before the printValues() function, with the following contents.

void loop() {
   static unsigned long previousTime = millis();
   unsigned long currentTime = millis();
   if (currentTime - previousTime >= 1000/SAMPLE_RATE) {
      // printValues();
      printRotationAngles();
      previousTime = millis();
   }
}

The new loop() function calculates the correct times, based on the defined sample rate, to call one of the functions. The commented out printValues() function is our original method and the printRotationAngles() function will be our new method. Note, the 10 Hz sample rate also means that the appropriate values will be printed to the Serial Monitor 10 times per second.

Let’s now add that new printRotationAngles() function to the end of our sketch.

void printRotationAngles() {
   char buffer[5];
   float ax, ay, az;
   float gx, gy, gz;

   if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()
      && IMU.readAcceleration(ax, ay, az) && IMU.readGyroscope(gx, gy, gz)) {
      filter.updateIMU(gx, gy, gz, ax, ay, az);
      Serial.print("Roll = ");  Serial.print(dtostrf(filter.getRoll(), 4, 0, buffer)); Serial.print(" °, ");
      Serial.print("Pitch = ");  Serial.print(dtostrf(filter.getPitch(), 4, 0, buffer)); Serial.print(" °, ");
      Serial.print("Yaw = ");  Serial.print(dtostrf(filter.getYaw(), 4, 0, buffer)); Serial.println(" °");
   }
}

There really isn’t much difference between the new printRotationAngles() function and the original printValues() function. The filter.updateIMU() call was added to calculate the rotation angles and printing was adjusted to properly display the angles.

Please note that the yaw angle calculated by this method will be relative to the initial starting position of the IMU. In addition, the value will drift over time. To obtain an absolute, or true, yaw angle (heading), an additional data source, such as a magnometer, would be required.

Compile the new sketch to verify everything was entered correctly. Your updated sketch should now look similar to the following.

// LSM6DS3_Arduino_Rotation - LSM6DS3_Arduino_Rotation.ino
//
// Description:
// Retrieves motion data from the on-board LSM6DS3 IMU of the Arduino Uno WiFi
// Rev2 using the Arduino_LSM6DS3 library and displays rotation angles (roll,
// pitch, and yaw) in the Serial Monitor.
//
// Created by John Woolsey on 01/28/2020.
// Copyright (c) 2019 Woolsey Workshop.  All rights reserved.


// Includes
#include <Arduino_LSM6DS3.h>
#include <MadgwickAHRS.h>


// Defines
#define SAMPLE_RATE 10  // in Hz


// Constructors
Madgwick filter;  // Madgwick algorithm for roll, pitch, and yaw calculations


void setup() {
   Serial.begin(9600);  // initialize serial bus (Serial Monitor)
   while (!Serial);  // wait for serial initialization
   Serial.print("LSM6DS3 IMU initialization ");
   if (IMU.begin()) {  // initialize IMU
      Serial.println("completed successfully.");
   } else {
      Serial.println("FAILED.");
      IMU.end();
      while (1);
   }
   Serial.println();
   filter.begin(SAMPLE_RATE);  // initialize Madgwick filter
}


void loop() {
   static unsigned long previousTime = millis();
   unsigned long currentTime = millis();
   if (currentTime - previousTime >= 1000/SAMPLE_RATE) {
      // printValues();
      printRotationAngles();
      previousTime = millis();
   }
}


// Prints IMU values.
void printValues() {
   char buffer[8];    // string buffer for use with dtostrf() function
   float ax, ay, az;  // accelerometer values
   float gx, gy, gz;  // gyroscope values

   // Retrieve and print IMU values
   if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()
      && IMU.readAcceleration(ax, ay, az) && IMU.readGyroscope(gx, gy, gz)) {
      Serial.print("ax = ");  Serial.print(dtostrf(ax, 4, 1, buffer));  Serial.print(" g, ");
      Serial.print("ay = ");  Serial.print(dtostrf(ay, 4, 1, buffer));  Serial.print(" g, ");
      Serial.print("az = ");  Serial.print(dtostrf(az, 4, 1, buffer));  Serial.print(" g, ");
      Serial.print("gx = ");  Serial.print(dtostrf(gx, 7, 1, buffer));  Serial.print(" °/s, ");
      Serial.print("gy = ");  Serial.print(dtostrf(gy, 7, 1, buffer));  Serial.print(" °/s, ");
      Serial.print("gz = ");  Serial.print(dtostrf(gz, 7, 1, buffer));  Serial.println(" °/s");
   }
}


// Prints rotation angles (roll, pitch, and yaw) calculated using the
// Madgwick algorithm.
// Note: Yaw is relative, not absolute, based on initial starting position.
// Calculating a true yaw (heading) angle requires an additional data source,
// such as a magnometer.
void printRotationAngles() {
   char buffer[5];    // string buffer for use with dtostrf() function
   float ax, ay, az;  // accelerometer values
   float gx, gy, gz;  // gyroscope values

   if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()
      && IMU.readAcceleration(ax, ay, az) && IMU.readGyroscope(gx, gy, gz)) {
      filter.updateIMU(gx, gy, gz, ax, ay, az);  // update roll, pitch, and yaw values

      // Print rotation angles
      Serial.print("Roll = ");  Serial.print(dtostrf(filter.getRoll(), 4, 0, buffer)); Serial.print(" °, ");
      Serial.print("Pitch = ");  Serial.print(dtostrf(filter.getPitch(), 4, 0, buffer)); Serial.print(" °, ");
      Serial.print("Yaw = ");  Serial.print(dtostrf(filter.getYaw(), 4, 0, buffer)); Serial.println(" °");
   }
}

Once you are happy with your sketch, upload it to the Arduino Uno WiFi Rev2 board. Make sure the Serial Monitor is still open so that you can see the output which should look like the following.

LSM6DS3 IMU initialization completed successfully.

Roll =    0 °, Pitch =   -2 °, Yaw =  180 °
Roll =    0 °, Pitch =   -3 °, Yaw =  179 °
Roll =    0 °, Pitch =   -2 °, Yaw =  179 °
Roll =    0 °, Pitch =   -2 °, Yaw =  179 °
Roll =   -1 °, Pitch =   -2 °, Yaw =  179 °

Pick up your Arduino and tilt and rotate it in multiple directions to see how the roll, pitch, and yaw rotation angles change. Pretend you are an airplane taking off or turning to a new vector.

You can get back to printing your standard (original) sensor readings once per second with this new sketch by:

  • uncommenting the printValues() function call (within the loop() function),
  • commenting out the printRotationAngles() function,
  • and changing the SAMPLE_RATE define from 10 to 1.

Summary

In this tutorial, we learned how to retrieve motion data from the on-board IMU of the Arduino Uno WiFi Rev2 development board. We created a sketch utilizing the official Arduino_LSM6DS3 library and were able to see acceleration and rotation sensor readings being displayed within the Serial Monitor.

We then created a new sketch, adding the Madgwick library, to display rotation angles, e.g. roll, pitch, and yaw, calculated using the accelerometer and gyroscope values.

The final source code used for this tutorial is available on GitHub.

Thank you for joining me in this journey and I hope you enjoyed the experience. Please feel free to share your thoughts in the comments section below.

About the author

John Woolsey

John is an electrical engineer who loves science, math, and technology and teaching it to others even more.
 
He knew he wanted to work with electronics from an early age, building his first robot when he was in 8th grade. His first computer was a Timex/Sinclair 2068 followed by the Tandy 1000 TL (aka really old stuff).
 
He put himself through college (The University of Texas at Austin) by working at Motorola where he worked for many years afterward in the Semiconductor Products Sector in Research and Development.
 
John started developing mobile app software in 2010 for himself and for other companies. He has also taught programming to kids for summer school and enjoyed years of judging kids science projects at the Austin Energy Regional Science Festival.
 
Electronics, software, and teaching all culminate in his new venture to learn, make, and teach others via the Woolsey Workshop website.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.