Accelerometer

Wiring

We'll be using the LSM6DSOX sensor to precisely measure acceleration data. The MCP2221 and LSM6DSOX both have STEMMA QT connectors, so you can either wire it up on a breadboard or use a STEMMA QT cable.

Try to avoid hot plugging I2C sensors. The MCP2221 doesn't seem to like that. Remove USB power first.

Adafruit LSM6DSOX 6 DoF Accelerometer and Gyroscope

PRODUCT ID: 4438
Behold, the ST LSM6DSOX: The latest in a long line of  quality Accelerometer+Gyroscope 6-DOF IMUs from ST.This IMU sensor has 6 degrees of freedom - 3 degrees each of...
$11.95
IN STOCK

STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long

PRODUCT ID: 4210
This 4-wire cable is a little over 100mm / 4" long and fitted with JST-SH female 4-pin connectors on both ends. Compared with the chunkier JST-PH these are 1mm pitch instead of...
$0.95
IN STOCK

Make the following connections between the MCP2221 and the LSM6DSOX:

  • Board 3V to sensor VIN (red wire)
  • Board GND to sensor GND (black wire)
  • Board SCL to sensor SCL (yellow wire)
  • Board SDA to sensor SDA (blue wire)

Code Usage

In the Jupyter file browser, click Upload. From the file browser, select the LSM6DSOX_Accel.ipynb example.

Click the Run button to execute the first cell. This cell installs the required libraries for using this notebook.

This cell installs the adafruit-circuitpython-lsm6dsox library for interfacing with the LSM303 sensor .

This cell also installs a Jupyter Extension, ipympl. This extension makes it possible for us to create interactive Matplotlib graphs from within a Jupyter notebook.

The next cell sets an environment variable so Blinka knows we're using the MCP2221.

Then, it imports CircuitPython libraries and initializes the I2C connection with the sensor.

The next cell imports CircuitPython modules (such as board and busio) and initializes the i2c connection with the sensor. To verify that your board is properly initialized, it should also values form the LSM6DSOX's acceleration sensor.

The next cell sets an environment variable so Blinka knows we're using the MCP2221.

Then, it imports CircuitPython modules (such as board and busio) and initializes the i2c connection with the sensor. To verify that your board is properly initialized, it should also print values from the LSM6DSOX acceleration sensor.

If you receive an error with the board module, make sure your MCP2221 is plugged into a usb port on your computer.

The third code cell uses Matplotlib to generate a graph for the LSM6DSOX's acceleration data. We used three side-by-side subplots to visualize the X, Y, and Z axis. 

Increasing the Number of Sensor Readings

By default, this code cell only displays 20 sensor readings. If you want to display more sensor readings on your graph, simply change the value of the HISTORY_SIZE variable in the code cell and re-run it. 

For more information about how this code cell works, read on!

Code Walkthrough

First, we import all required libraries for this code cell. Most of the libraries come from the Matplotlib library. We use this library to plot the data obtained by our sensor.

Download: file
%matplotlib notebook
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import datetime
import matplotlib.dates as mdates
from collections import deque

Our code only plots and displays 20 sensor readings at a time. We store these readings in a list-like object called a deque container datatype. If you've never seen this datatype before, don't worry - it's very similar to a list object except it's "optimized for fast fixed-length operations" and support a maxlen argument which sets the maximum possible size of a deque. When the deque grows beyond its maxlen size, it pops objects off of its opposite end (like a FIFO stack).

We'll be using four deque objects to represent the x-axis, the first graph's y-axis (accelerometer's x-axis data), the second graph's y-axis (accelerometer's y-axis data), and the third graph's y-axis (accelerometer's y-axis data). These deque objects use HISTORY_SIZE as the deque's maxlen. You may increase the amount of data to display on the graph by increasing HISTORY_SIZE.

Download: file
# Deque for X-Axis (time)
x_vals = deque(maxlen=HISTORY_SIZE)

# Deque for Y-Axis (accelerometer readings)
accel_x = deque(maxlen=HISTORY_SIZE)
accel_y = deque(maxlen=HISTORY_SIZE)
accel_z = deque(maxlen=HISTORY_SIZE)

Next, we'll create three side-by-side sub-plots and call tight_layout to adjust the subplot parameters to give nicer padding between examples.

Download: file
# Create 3 side-by-side subplots
fig, (ax1, ax2, ax3) = plt.subplots(1,3)

# Automatically adjust subplot parameters for nicer padding between plots
plt.tight_layout()

Let's now take a look at the animate method. This method polls the LSM303's acceleration values and stores them in a tuple named accel_data. Next, the code appends the values from the tuple to deque objects, accel_x, accel_y, accel_z.

Download: file
def animate(i):
    # Poll the LSM303AGR
    accel_data = accel.acceleration
    # Add the X/Y/Z values to the accel arrays
    accel_x.append(accel_data[0])
    accel_y.append(accel_data[1])
    accel_z.append(accel_data[2])

We grab the current time (in seconds using CPython's datetime module) and store it in a deque, x_vals.

Download: file
# Grab the datetime, auto-range based on length of accel_x array
x_vals = [datetime.datetime.now() + datetime.timedelta(seconds=i) for i in range(len(accel_x))]

Now we're up to the fun part of this code walkthrough - displaying the graphs. Since we're "animating" the graph, the axis will need to be cleared and re-drawn each time the animate method runs. Let's clear the three axis, set up grid titles and enable grid lines. 

Download: file
# Clear all axis
ax1.cla()
ax2.cla()
ax3.cla()

# Set grid titles
ax1.set_title('X', fontsize=10)
ax2.set_title('Y', fontsize=10)
ax3.set_title('Z', fontsize=10)

# Enable subplot grid lines
ax1.grid(True, linewidth=0.5, linestyle=':')
ax2.grid(True, linewidth=0.5, linestyle=':')   
ax3.grid(True, linewidth=0.5, linestyle=':')

Since we are displaying a large amount of data on the x-axis, we'll use Matplotlib's autofmt_xdate() method to automatically align and roate the x-axis labels. 

The first image on the left shows this code without a call to autofmt_xdate while the second image shows a nicely formatted graph. Pretty neat, right

Finally, we'll display the sub-plots on the figure by calling ax.plot and specifying the x-axis and y-axis deques. We'll also specify different colors for each graphs to help us visually identify the sub-graphs.

Download: file
# Display the sub-plots
ax1.plot(x_vals, accel_x, color='r')
ax2.plot(x_vals, accel_y, color='g')
ax3.plot(x_vals, accel_z, color='b')

Finally, we'll pause the plot's drawing for INTERVAL seconds. 

Download: file
# Pause the plot for INTERVAL seconds 
plt.pause(INTERVAL)

About Notebook Performance

Computers with less available resources will render choppy graphs. For reference, all GIFs in this guide were rendered on a computer with a 2.6GHz i7 and 32GB of RAM.

We can increase the INTERVAL, keeping in mind two things:

  1. USB is limited to one transaction per millisecond
  2. We are displaying HISTORY_SIZE samples at a time. You may want to decrease the number of samples displayed on the graph for better performance. 

This method "makes an animation by repeatedly calling a function", animate. We provide it the figure we generated earlier and the function we'd like to animate.

Download: file
# Update graph every 125ms
ani = FuncAnimation(fig, animate)
This guide was first published on Dec 24, 2019. It was last updated on Dec 24, 2019. This page (Accelerometer) was last updated on Feb 20, 2020.