Overview

Acceleration makes the world go around--literally!  It's the force that causes movement like a car accelerating away from a stop light or an object falling to the ground from gravity when dropped.  Accelerometers are small sensors that can detect the force of acceleration and are great for detecting motion and orientation.  The LIS3DH triple-axis accelerometer in particular is an inexpensive and easy to use accelerometer with features like X, Y, Z axis acceleration and click detection.  This guide will show you how to wire the LIS3DH to a board like the ESP8266 or SAMD21/M0 and start reading acceleration values from it in CircuitPython!

Before you get started you'll want to be familiar with CircuitPython, MicroPython, and the LIS3DH by reading these guides:

Continue on to learn about the hardware needed to follow this guide.

Hardware

Parts

You'll need the following hardware to follow this guide:

Start by following the LIS3DH breakout guide to assemble and test the board.  Then continue on below to learn how to wire it to a Feather for use with CircuitPython.

Wiring

There are two ways to connect the LIS3DH accelerometer to a board, either with an I2C or SPI connection.  The I2C connection requires just a couple data lines and is the recommended way to use the accelerometer.  However if for some reason you can't use I2C the board also supports a SPI interface which uses a few more data lines.  See below for details on wiring up either interface to your board.

I2C Wiring

  • LIS3DH Vin to board 3V (or 5V) output - red wire.
  • LIS3DH GND to board GND/ground - black wire.
  • LIS3DH SCL to board SCL (I2C clock) - orange wire.
  • LIS3DH SDA to board SDA (I2C data) - yellow wire.

SPI Wiring

  • LIS3DH Vin to board 3V (or 5V) output - red wire.
  • LIS3DH GND to board GND/ground - black wire.
  • LIS3DH SCL to board SCK (SPI clock) - orange wire.
  • LIS3DH SDA to board MOSI (SPI master out/slave in) - yellow wire.
  • LIS3DH SDO to board MISO (SPI master in/slave out) - green wire.
  • LIS3DH CS to board pin #6 (or any free digital I/O pin) - blue wire.

Once the LIS3DH is wired to your board continue on to learn how to install the CircuitPython modules necessary to control it from your Python code.

Software

Adafruit CircuitPython Module Install

To use the LIS3DH with your Adafruit CircuitPython board you'll need to install the Adafruit_CircuitPython_LIS3DH module on your board. Remember this module is for Adafruit CircuitPython firmware and not MicroPython.org firmware!

First make sure you are running the latest version of Adafruit CircuitPython for your board (note you need to be running the CircuitPython 1.0.0-rc1 or greater release for this library and examples).

Next download the latest adafruit_lis3dh.zip file from the releases page of the Adafruit_CircuitPython_LIS3DH GitHub repository. You'll need to unzip this file and copy the entire adafruit_lis3dh directory to the board's root filesystem.

If your board supports USB mass storage, like the SAMD21 CircuitPython port, then simply drag the files to the board's file system. Note on boards without external SPI flash, like a Feather M0 or Trinket/Gemma M0, you might run into issues on Mac OSX with hidden files taking up too much space when drag and drop copying, see this page for a workaround.

If your board doesn't support USB mass storage, like the ESP8266, then use a tool like ampy to copy the file to the board. You can use the latest version of ampy and its new directory copy command to easily move module directories to the board.

In addition you'll need the Adafruit CircuitPython Bus Device module installed on your board. Just like installing the LIS3DH module as mentioned above, download the latest release .zip file and copy the folder inside it to the board's root filesystem.

Before continuing make sure your board's root filesystem has the adafruit_lis3dh, and adafruit_bus_device folders/modules copied over.

Examples & Usage

To learn how to use the LIS3DH module code you can look at a few examples included with the library:

  • accel.py - This example demonstrates reading the X, Y, Z axis of the accelerometer and prints their values (in gravities or Gs).
  • adc.py - This example demonstrates reading the analog to digital converters on the board.
  • click.py - This example demonstrates reading the click detection on the board.

Intialization

In all of the examples you'll notice they have the same initialization code at the top.  This is how you initialize the I2C or SPI bus (depending on how you have the board wired to your hardware) and setup the LIS3DH module:

import board
import adafruit_lis3dh

# Uncomment _one_ of the hardware setups below depending on your wiring:

# Hardware I2C setup:
import busio
i2c = busio.I2C(board.SCL, board.SDA)
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)

# Software I2C setup:
#import bitbangio
#i2c = bitbangio.I2C(board.SCL, board.SDA)
#lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)

# Hardware SPI setup:
#import busio
#import digitalio
#spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
#cs = digitalio.DigitalInOut(board.D6)  # Set to appropriate CS pin!
#lis3dh = adafruit_lis3dh.LIS3DH_SPI(spi, cs)

Notice that there are three ways to initialize the library.  The first way is uncommented and shows initializing the library with a hardware I2C configuration.  This is perfect SAMD21-based boards like the Feather M0 which support a hardware I2C interface.

The second way is with a software I2C configuration.  The only difference is that the bitbangio module is used in place of the busio module. This can be useful to run I2C on pins without hardware support. (On boards with no hardware support, such as the ESP8266, busio will automatically bitbang I2C.)

The third way is with a hardware SPI configuration.  Notice here you must specify a CS or chip select pin as one of the digital IO pins on your board.  Be sure to set this pin to the right value for your wiring.

Update the example code so that only one of the initialization options is uncommented.

Accelerometer Usage

Update the accel.py example initialization as mentioned above and then use a tool like ampy to run it on your board.  You'll want to run the example with the -n option so that ampy runs the script and doesn't wait for any output, like:

ampy --port /dev/serial/port run -n accel.py

(change the --port value to the name of your board's serial port)

Now open the board's serial terminal and you should see the accelerometer X, Y, Z values printed a few times a second.  Try moving the board around and notice how the values change!  The values are in gravities or Gs which are common units for measuring acceleration (1G is ~9.8m/s^2, or typical Earth gravity).

If you examine the accel.py code you can see how it initializes and reads the accelerometer values:

# Set range of accelerometer (can be RANGE_2_G, RANGE_4_G, RANGE_8_G or RANGE_16_G).
lis3dh.range = adafruit_lis3dh.RANGE_2_G

# Loop forever printing accelerometer values
while True:
    # Read accelerometer values (in m / s ^ 2).  Returns a 3-tuple of x, y,
    # z axis values.
    x, y, z = lis3dh.acceleration
    print('x = {}G, y = {}G, z = {}G'.format(x / 9.806, y / 9.806, z / 9.806))
    # Small delay to keep things responsive but give time for interrupt processing.
    time.sleep(0.1)

First the range of the accelerometer is set by changing the range property on the LIS3DH object.  Notice how you can set it to one of four possible ranges, where a 2G range gives you a lot of accuracy in a small range vs. up to a 16G range with less accuracy over a much wider range.  You'll need to pick the right range for your usage needs, although starting with a simple 2G range is a smart idea.

Next in the main loop you can see the acceleration property is read.  This property is a 3-tuple with the X, Y, Z acceleration values that were read by the sensor.  Remember these values are in meters per second squared so you might need to convert to other units depending on your needs.

That's all there is to reading the accelerometer values!

Click Detection Usage

Update the click.py example initialization as mentioned above and then use a tool like ampy to run it on your board.  You'll want to run the example with the -n option so that ampy runs the script and doesn't wait for any output, like:

ampy --port /dev/serial/port run -n click.py

(change the --port value to the name of your board's serial port)

Now open the board's serial terminal and tap on the LIS3DH chip with your finger.  You should see the terminal print out messages when it detects a click event.  By default the example will try to detect both single and double clicks:

If you examine the click.py code you'll see how it configures and detects click events:

# Set range of accelerometer (can be RANGE_2_G, RANGE_4_G, RANGE_8_G or RANGE_16_G).
lis3dh.range = adafruit_lis3dh.RANGE_2_G

# Set click detection to double and single clicks.  The first parameter is a value:
#  - 0 = Disable click detection.
#  - 1 = Detect single clicks.
#  - 2 = Detect single and double clicks.
# The second parameter is the threshold and a higher value means less sensitive 
# click detection.  Note the threshold should be set based on the range above:
#  - 2G = 40-80 threshold
#  - 4G = 20-40 threshold
#  - 8G = 10-20 threshold
#  - 16G = 5-10 threshold
lis3dh.set_click(2, 80)

# Loop forever printing if a single or double click is detected.
while True:
    # Read the click detection.  Two booleans are returned, single click and
    # double click detected.  Each can be independently true/false.
    single, double = lis3dh.read_click()
    if single:
        print('Single click!')
    if double:
        print('Double click!')
    # Small delay to keep things responsive but give time for interrupt processing.
    time.sleep(0.05)

Like with reading the accelerometer the range property is first set to adjust the range of acceleration the sensor can read.  Then notice the set_click function is called and passed a couple parameters:

  • Type of click events to detect - Set this to 0 to disable click detection, 1 to detect only single clicks, and 2 to detect single and double click events.
  • Click threshold - This is a number that is the threshold for click detection (see the LIS3DH datasheet for exact details on its meaning).  In general this value depends on the range that was set for the accelerometer.  See the example values mentioned in the comments and try setting it appropriately in your code.

In addition the set_click function can take the following optional keyword parameters to further adjust the click detection.  See the LIS3DH datasheet for more details on what they mean and how they change the detection.

  • time_limit - This is the TIMELIMIT register value and defaults to 10.
  • time_latency - This is the TIMELATENCY register value and defaults to 20.
  • time_window - This is the TIMEWINDOW register value and defaults to 255.

Once the set_click function is called to configure click detection the main loop below it will call the read_click function to get the current click detection state.  This function returns a 2-tuple of boolean values.  The first is true/false if a single click event was detected, and the second is true/false if a double click event was detected.

That's all there is to detecting click events!

Analog to Digital Converter Usage

Update the adc.py example initialization as mentioned above and then use a tool like ampy to run it on your board.  You'll want to run the example with the -n option so that ampy runs the script and doesn't wait for any output, like:

ampy --port /dev/serial/port run -n adc.py

(change the --port value to the name of your board's serial port)

Now open the board's serial terminal and notice every second the A1 ADC channel value is printed, both as a raw and millivolt output:

Remember the ADC on the LIS3DH is somewhat limited and can only measure voltages in the range of ~900mV to ~1200mV!

If you examine the adc.py code you'll see how it configures and reads the ADC:

# Loop forever printing ADC readings.
while True:
    # Read raw ADC value.  Specify which ADC to read: 1, 2, or 3.
    adc1_raw = lis3dh.read_adc_raw(1)
    # Or read the ADC value in millivolts:
    adc1_mV = lis3dh.read_adc_mV(1)
    print('ADC 1 = {} ({} mV)'.format(adc1_raw, adc1_mV))
    time.sleep(1)

The read_adc_raw function is called and passed in the number of the ADC channel to read (1, 2, or 3).  This function returns the raw value returned by the ADC register, see the LIS3DH guide and datasheet for more details on its meaning.

As a convenience the read_adc_mV function can also be called and passed the number of the ADC channel to read.  This function converts the raw reading into a millivolt value and returns it.  Remember only voltages in the range of ~900mV to ~1200mV can be read by the LIS3DH ADC!

That's all there is to reading the analog to digital converter on the LIS3DH!