ESP32 Thing Motion Shield Hookup Guide

Pages
Contributors: MTaylor
Favorited Favorite 2

Using the IMU

The IMU on the shield is the LSM9DS1, which is connected the the ESP32 Thing to the I2C port only. Any of the examples from the LSM9DS1 library should be good to go, as long as you specify the port type to be I2C with default addresses.

There are two examples in this section that will work without modification on the Motion Board.

The first example is a modification of the LSM9DS1_ESP32_Settings.ino example, with SPI related information removed. This is a good place to start because it shows all of the API, and you can comment out what you don't need.

language:c
/*****************************************************************
LSM9DS1_ESP32_Settings.ino
SFE_LSM9DS1 Library Settings Configuration Example
Original Creation: August 13, 2015 by Jim Lindblom
https://github.com/sparkfun/LSM9DS1_Breakout

This Arduino sketch demonstrates how to configure every 
possible configuration value in the SparkFunLSM9DS1 library.

It demonstrates how to set the output data rates and scales
for each sensor, along with other settings like LPF cutoff
frequencies and low-power settings.

It also demonstrates how to turn various sensors in the
LSM9DS1 on or off.

Hardware setup: This library is intended to be used with a 
ESP32 Motion shield connected directly to the ESP32 Thing.

Development environment specifics:
IDE: Arduino 1.8.2
Hardware Platform: ESP32 Arduion Board

This code is beerware. If you see me (or any other SparkFun 
employee) at the local, and you've found our code helpful, 
please buy us a round!

Distributed as-is; no warranty is given.
*****************************************************************/
// Include SparkFunLSM9DS1 library and its dependencies
#include <Wire.h>
#include <SPI.h>
#include <SparkFunLSM9DS1.h>

LSM9DS1 imu;  // Create an LSM9DS1 object

// Mag address must be 0x1E, would be 0x1C if SDO_M is LOW
#define LSM9DS1_M   0x1E
// Accel/gyro address must be 0x6B, would be 0x6A if SDO_AG is LOW
#define LSM9DS1_AG  0x6B

// Global variables to keep track of update rates
unsigned long startTime;
unsigned int accelReadCounter = 0;
unsigned int gyroReadCounter = 0;
unsigned int magReadCounter = 0;
unsigned int tempReadCounter = 0;

// Global variables to print to serial monitor at a steady rate
unsigned long lastPrint = 0;
const unsigned int PRINT_RATE = 500;


void setupGyro()
{
// [enabled] turns the gyro on or off.
imu.settings.gyro.enabled = true;  // Enable the gyro
// [scale] sets the full-scale range of the gyroscope.
// scale can be set to either 245, 500, or 2000
imu.settings.gyro.scale = 245; // Set scale to +/-245dps
// [sampleRate] sets the output data rate (ODR) of the gyro
// sampleRate can be set between 1-6
// 1 = 14.9    4 = 238
// 2 = 59.5    5 = 476
// 3 = 119     6 = 952
imu.settings.gyro.sampleRate = 3; // 59.5Hz ODR
// [bandwidth] can set the cutoff frequency of the gyro.
// Allowed values: 0-3. Actual value of cutoff frequency
// depends on the sample rate. (Datasheet section 7.12)
imu.settings.gyro.bandwidth = 0;
// [lowPowerEnable] turns low-power mode on or off.
imu.settings.gyro.lowPowerEnable = false; // LP mode off
// [HPFEnable] enables or disables the high-pass filter
imu.settings.gyro.HPFEnable = true; // HPF disabled
// [HPFCutoff] sets the HPF cutoff frequency (if enabled)
// Allowable values are 0-9. Value depends on ODR.
// (Datasheet section 7.14)
imu.settings.gyro.HPFCutoff = 1; // HPF cutoff = 4Hz
// [flipX], [flipY], and [flipZ] are booleans that can
// automatically switch the positive/negative orientation
// of the three gyro axes.
imu.settings.gyro.flipX = false; // Don't flip X
imu.settings.gyro.flipY = false; // Don't flip Y
imu.settings.gyro.flipZ = false; // Don't flip Z
}

void setupAccel()
{
// [enabled] turns the acclerometer on or off.
imu.settings.accel.enabled = true; // Enable accelerometer
// [enableX], [enableY], and [enableZ] can turn on or off
// select axes of the acclerometer.
imu.settings.accel.enableX = true; // Enable X
imu.settings.accel.enableY = true; // Enable Y
imu.settings.accel.enableZ = true; // Enable Z
// [scale] sets the full-scale range of the accelerometer.
// accel scale can be 2, 4, 8, or 16
imu.settings.accel.scale = 8; // Set accel scale to +/-8g.
// [sampleRate] sets the output data rate (ODR) of the
// accelerometer. ONLY APPLICABLE WHEN THE GYROSCOPE IS
// DISABLED! Otherwise accel sample rate = gyro sample rate.
// accel sample rate can be 1-6
// 1 = 10 Hz    4 = 238 Hz
// 2 = 50 Hz    5 = 476 Hz
// 3 = 119 Hz   6 = 952 Hz
imu.settings.accel.sampleRate = 1; // Set accel to 10Hz.
// [bandwidth] sets the anti-aliasing filter bandwidth.
// Accel cutoff freqeuncy can be any value between -1 - 3. 
// -1 = bandwidth determined by sample rate
// 0 = 408 Hz   2 = 105 Hz
// 1 = 211 Hz   3 = 50 Hz
imu.settings.accel.bandwidth = 0; // BW = 408Hz
// [highResEnable] enables or disables high resolution 
// mode for the acclerometer.
imu.settings.accel.highResEnable = false; // Disable HR
// [highResBandwidth] sets the LP cutoff frequency of
// the accelerometer if it's in high-res mode.
// can be any value between 0-3
// LP cutoff is set to a factor of sample rate
// 0 = ODR/50    2 = ODR/9
// 1 = ODR/100   3 = ODR/400
imu.settings.accel.highResBandwidth = 0;  
}

void setupMag()
{
// [enabled] turns the magnetometer on or off.
imu.settings.mag.enabled = true; // Enable magnetometer
// [scale] sets the full-scale range of the magnetometer
// mag scale can be 4, 8, 12, or 16
imu.settings.mag.scale = 12; // Set mag scale to +/-12 Gs
// [sampleRate] sets the output data rate (ODR) of the
// magnetometer.
// mag data rate can be 0-7:
// 0 = 0.625 Hz  4 = 10 Hz
// 1 = 1.25 Hz   5 = 20 Hz
// 2 = 2.5 Hz    6 = 40 Hz
// 3 = 5 Hz      7 = 80 Hz
imu.settings.mag.sampleRate = 5; // Set OD rate to 20Hz
// [tempCompensationEnable] enables or disables 
// temperature compensation of the magnetometer.
imu.settings.mag.tempCompensationEnable = false;
// [XYPerformance] sets the x and y-axis performance of the
// magnetometer to either:
// 0 = Low power mode      2 = high performance
// 1 = medium performance  3 = ultra-high performance
imu.settings.mag.XYPerformance = 3; // Ultra-high perform.
// [ZPerformance] does the same thing, but only for the z
imu.settings.mag.ZPerformance = 3; // Ultra-high perform.
// [lowPowerEnable] enables or disables low power mode in
// the magnetometer.
imu.settings.mag.lowPowerEnable = false;
// [operatingMode] sets the operating mode of the
// magnetometer. operatingMode can be 0-2:
// 0 = continuous conversion
// 1 = single-conversion
// 2 = power down
imu.settings.mag.operatingMode = 0; // Continuous mode
}

void setupTemperature()
{
// [enabled] turns the temperature sensor on or off.
imu.settings.temp.enabled = true;
}

uint16_t initLSM9DS1()
{
setupDevice(); // Setup general device parameters
setupGyro(); // Set up gyroscope parameters
setupAccel(); // Set up accelerometer parameters
setupMag(); // Set up magnetometer parameters
setupTemperature(); // Set up temp sensor parameter

return imu.begin(LSM9DS1_AG, LSM9DS1_M, Wire);
}

void setup() 
{
Serial.begin(115200);

Wire.begin();

Serial.println("Initializing the LSM9DS1");
uint16_t status = initLSM9DS1();
Serial.print("LSM9DS1 WHO_AM_I's returned: 0x");
Serial.println(status, HEX);
Serial.println("Should be 0x683D");
Serial.println();

startTime = millis();
}

void loop() 
{  
// imu.accelAvailable() returns 1 if new accelerometer
// data is ready to be read. 0 otherwise.
if (imu.accelAvailable())
{
    imu.readAccel();
    accelReadCounter++;
}

// imu.gyroAvailable() returns 1 if new gyroscope
// data is ready to be read. 0 otherwise.
if (imu.gyroAvailable())
{
    imu.readGyro();
    gyroReadCounter++;
}

// imu.magAvailable() returns 1 if new magnetometer
// data is ready to be read. 0 otherwise.
if (imu.magAvailable())
{
    imu.readMag();
    magReadCounter++;
}

// imu.tempAvailable() returns 1 if new temperature sensor
// data is ready to be read. 0 otherwise.
if (imu.tempAvailable())
{
    imu.readTemp();
    tempReadCounter++;
}

// Every PRINT_RATE milliseconds, print sensor data:
if ((lastPrint + PRINT_RATE) < millis())
{
    printSensorReadings();
    lastPrint = millis();
}
}

// printSensorReadings prints the latest IMU readings
// along with a calculated update rate.
void printSensorReadings()
{
float runTime = (float)(millis() - startTime) / 1000.0;
float accelRate = (float)accelReadCounter / runTime;
float gyroRate = (float)gyroReadCounter / runTime;
float magRate = (float)magReadCounter / runTime;
float tempRate = (float)tempReadCounter / runTime;
Serial.print("A: ");
Serial.print(imu.calcAccel(imu.ax));
Serial.print(", ");
Serial.print(imu.calcAccel(imu.ay));
Serial.print(", ");
Serial.print(imu.calcAccel(imu.az));
Serial.print(" g \t| ");
Serial.print(accelRate);
Serial.println(" Hz");
Serial.print("G: ");
Serial.print(imu.calcGyro(imu.gx));
Serial.print(", ");
Serial.print(imu.calcGyro(imu.gy));
Serial.print(", ");
Serial.print(imu.calcGyro(imu.gz));
Serial.print(" dps \t| ");
Serial.print(gyroRate);
Serial.println(" Hz");
Serial.print("M: ");
Serial.print(imu.calcMag(imu.mx));
Serial.print(", ");
Serial.print(imu.calcMag(imu.my));
Serial.print(", ");
Serial.print(imu.calcMag(imu.mz));
Serial.print(" Gs \t| ");
Serial.print(magRate);
Serial.println(" Hz");
Serial.print("T: ");
Serial.print(imu.temperature);
Serial.print(" \t\t\t| ");
Serial.print(tempRate);
Serial.println(" Hz");  
Serial.println();
}

The second example is more heavily modified example. It outputs CSV data to the serial port (which can be rerouted to a file if you're into that). Here, a simple configuration is chosen but more work is done with the output data.

language:c
/*****************************************************************
LSM9DS1_CSV.ino
Collecting IMU data as CSV for graphing

Original Creation: August 13, 2015 by Jim Lindblom
from the LSM9DS1_Basic_I2C.ino library example.
https://github.com/sparkfun/LSM9DS1_Breakout

Hardware setup: This library is intended to be used with a 
ESP32 Motion shield connected directly to the ESP32 Thing.

Development environment specifics:
IDE: Arduino 1.8.2
Hardware Platform: ESP32 Arduion Board

This code is beerware. If you see me (or any other SparkFun 
employee) at the local, and you've found our code helpful, 
please buy us a round!

Distributed as-is; no warranty is given.
*****************************************************************/
// The SFE_LSM9DS1 library requires both Wire and SPI be
// included BEFORE including the 9DS1 library.
#include <Wire.h>
#include <SPI.h>
#include <SparkFunLSM9DS1.h>

LSM9DS1 imu;  // Create an LSM9DS1 object

#define LSM9DS1_M   0x1E // Would be 0x1C if SDO_M is LOW
#define LSM9DS1_AG  0x6B // Would be 0x6A if SDO_AG is LOW

#define PRINT_SPEED 250 // 250 ms between prints
static unsigned long lastPrint = 0; // Keep track of print time

// Earth's magnetic field varies by location. Add or subtract 
// a declination to get a more accurate heading. Calculate 
// your's here:
// http://www.ngdc.noaa.gov/geomag-web/#declination
#define DECLINATION -8.58 // Declination (degrees) in Boulder, CO.

//Internal variables
float roll;
float pitch;
float heading;
char csvBuffer[300];

void setup() 
{
Serial.begin(115200);
delay(1000);
Serial.println("Starting Sketch");

delay(100);
Wire.begin();
if (imu.begin() == false)
{
    Serial.println("Failed to communicate with LSM9DS1.");
    Serial.println("Double-check wiring.");
    Serial.println("Default settings in this sketch will " \
    "work for an out of the box LSM9DS1 " \
    "Breakout, but may need to be modified " \
    "if the board jumpers are.");
    while (1)
    ;
}

}

void loop()
{
// Update the sensor values whenever new data is available
if ( imu.gyroAvailable() )
{
    // To read from the gyroscope,  first call the
    // readGyro() function. When it exits, it'll update the
    // gx, gy, and gz variables with the most current data.
    imu.readGyro();
}
if ( imu.accelAvailable() )
{
    // To read from the accelerometer, first call the
    // readAccel() function. When it exits, it'll update the
    // ax, ay, and az variables with the most current data.
    imu.readAccel();
}
if ( imu.magAvailable() )
{
    // To read from the magnetometer, first call the
    // readMag() function. When it exits, it'll update the
    // mx, my, and mz variables with the most current data.
    imu.readMag();
}

if ((lastPrint + PRINT_SPEED) < millis())
{
    // Print the collected data as CSV
    calcAttitude(imu.ax, imu.ay, imu.az, -imu.my, -imu.mx, imu.mz);
    float data1 = imu.calcGyro(imu.gx);
    float data2 = imu.calcGyro(imu.gy);
    float data3 = imu.calcGyro(imu.gz);
    float data4 = imu.calcAccel(imu.ax);
    float data5 = imu.calcAccel(imu.ay);
    float data6 = imu.calcAccel(imu.az);
    float data7 = imu.calcMag(imu.mx);
    float data8 = imu.calcMag(imu.my);
    float data9 = imu.calcMag(imu.mz);

    sprintf(csvBuffer, "%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,%4.3f,", data1, data2, data3, data4, data5, data6, data7, data8, data9, roll, pitch, heading);
    Serial.print(csvBuffer);
    Serial.println();

    lastPrint = millis(); // Update lastPrint time
}
}

void calcAttitude(float ax, float ay, float az, float mx, float my, float mz)
{
roll = atan2(ay, az);
pitch = atan2(-ax, sqrt(ay * ay + az * az));
heading;

if (my == 0)
    heading = (mx < 0) ? PI : 0;
else
    heading = atan2(mx, my);

heading -= DECLINATION * PI / 180;

if (heading > PI) heading -= (2 * PI);
else if (heading < -PI) heading += (2 * PI);
else if (heading < 0) heading += 2 * PI;

// Convert everything from radians to degrees:
heading *= 180.0 / PI;
pitch *= 180.0 / PI;
roll  *= 180.0 / PI;
}