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.

12 Comments

  • Hi,

    You mentioned using a magnometer to get a true heading/yaw value, wondered if you’d had any luck in doing this? I’m seeing things similar but using other boards, is there a way of better integrating that with this board?

    Thanks,
    Oscar

  • Hi, I ran your program, but weirdly the Yaw number seems to simply crawl down from 360 to zero over and over. Pitch and Roll vary a little too, and this is with it sitting on the desk!

    Also, supposedly the LSM6DS3 device has a temperature sensor but I am unable to find any way to access it. If you know of one I’d be curious.

    Great site you created! I look at it often.

    • Yes, the yaw numbers will do that without an additional external source, such as a magnometer. I would expect the pitch and roll to move around a little, but not nearly as much as the yaw.

      I submitted a request for the library to include access to the temperature sensor, but it has not yet been added. You could use the Sparkfun library, which does have temperature access, covered in the Accessing The IMU On The New Arduino Uno WiFi Rev2 tutorial to access the on-board temperature sensor. You can also review the temperature access code inside the Sparkfun library or the pull request associated with my issue request to learn how to do it yourself if you are comfortable with that.

      Glad you are enjoying the site. Thanks for letting me know.

    • It appears that the dtostrf() function is only available in the avr toolchain. I see people using other boards recommending adding the following to include it.

      #include <avr/dtostrf.h>

  • This is brilliant. Thank you very much for the amazing knowledge you provide. Now i tapped into the Temperature sensor by integrating the sparkfunLSM6DS3 library in the code you provided here, Oddly enough it seems to work without using the command ” myIMU.begin(); ” .. i don’t konw if this modification to the code is reasonable or not, since i don’t know much about writing code. please take a look…

    #include
    #include “SparkFunLSM6DS3.h”

    LSM6DS3 myIMU(SPI_MODE, SPIIMU_SS); // SPI Chip Select

    void setup() {
    Serial.begin(9600); // initialize serial bus (Serial Monitor)
    while (!Serial); // wait for serial initialization
    Serial.print(“IMU initialization “);
    if (IMU.begin() ) { // initialize IMU
    Serial.println(“IS ROCK ‘N ROLL”);
    } else {
    Serial.println(“SUCKED”);
    IMU.end();
    while (1);
    }
    Serial.println();
    Serial.print(“Accelerometer sample rate = “); Serial.print(IMU.accelerationSampleRate()); Serial.println(” Hz”);
    Serial.println();
    }
    void loop() {
    char buffer[8]; // string buffer for use with dtostrf(float_value, min_width, num_digits_after_decimal, where_to_store_string)
    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(” ay=”); Serial.print(dtostrf(ay, -4, 1, buffer));
    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(” gy=”); Serial.print(dtostrf(gy, -7, 1, buffer));
    Serial.print(” gz=”); Serial.print(dtostrf(gz, -7, 1, buffer)); Serial.print(” (°/s)”);
    Serial.print(” C = “); Serial.print(myIMU.readTempC(), 4); Serial.print(” F = “); Serial.println(myIMU.readTempF(), 4);
    }
    delay(1000);
    }

    • Glad to hear you got it working.

      Your modifications look reasonable, however, I believe you still need to include the Arduino library and remove the empty #include statement.
      #include <SparkFunLSM6DS3.h>
      #include <Arduino_LSM6DS3.h>

      It appears that myIMU.begin() is not necessary since you are not using the SparkFun library to load accelerometer and gyroscope values.

  • Hi John,
    I get this kind of rolling,drifting Yaw [1st column]

    Orientation: 78.82 -1.90 0.49
    Orientation: 78.41 -2.46 -0.53
    Orientation: 77.94 -2.13 0.22
    Orientation: 77.52 -2.16 -0.64
    Orientation: 77.03 -2.00 0.29
    Orientation: 76.62 -2.54 -0.73
    Orientation: 76.14 -2.20 0.01
    Orientation: 75.71 -1.82 -0.43
    Orientation: 75.22 -1.84 0.68
    Orientation: 74.80 -2.65 -0.33
    Orientation: 74.34 -2.15 -0.03
    Orientation: 73.94 -2.10 -0.87
    Orientation: 73.44 -2.37 0.36
    Orientation: 73.02 -2.31 -0.45
    Orientation: 72.56 -1.81 -0.10
    Orientation: 72.15 -3.15 -0.87
    Orientation: 71.69 -2.71 -0.31
    Orientation: 71.21 -2.31 0.34
    Orientation: 70.80 -2.50 -0.60
    Orientation: 70.32 -2.08 0.05
    Orientation: 69.85 -1.55 0.16
    Orientation: 69.41 -3.23 -0.17
    Orientation: 68.96 -2.70 -0.06
    Orientation: 68.52 -2.19 -0.07
    Orientation: 68.10 -2.04 -0.79
    Orientation: 67.60 -2.44 0.46
    Orientation: 67.17 -2.25 -0.24
    Orientation: 66.68 -3.04 1.01
    Orientation: 66.26 -2.93 0.25
    Orientation: 65.79 -2.40 0.55
    Orientation: 65.36 -2.23 -0.18
    Orientation: 64.94 -3.62 -0.89
    Orientation: 64.49 -3.15 -0.49
    Orientation: 64.02 -2.68 0.00
    Orientation: 63.59 -2.24 -0.33
    Orientation: 63.09 -2.45 0.88
    Orientation: 62.66 -2.48 0.02

    1. so this is expected [even with madgwick filter]?
    2. this can ONLY be only corrected with magnetometer values?
    3. Any other way of manual calibration without magnetometer?

    Thanks

Leave a Comment

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