Now let's get a little fancier and use a sensor generated interrupt.

Configuring The Sensor

The MagTag includes a LIS3DH accelerometer, which has the ability to generate an interrupt for various events. There are numerous options, like shake, tap, free fall, etc. There are also numerous registers that come into play, so this requires additional setup. You can checkout the datasheet for all the details.

We ended up just copying the recommended setup we found in this ST Design Tip memo:

That's what these lines of code do:

Download: file
lis._write_register_byte(0x20, 0x3F)  # low power mode with ODR = 25Hz
lis._write_register_byte(0x22, 0x40)  # AOI1 interrupt generation is routed to INT1 pin
lis._write_register_byte(0x23, 0x80)  # FS = ±2g low power mode with BDU bit enabled
lis._write_register_byte(
    0x24, 0x0C
)  # Interrupt signal on INT1 pin is latched with D4D_INT1 bit enabled
lis._write_register_byte(
    0x32, 0x20
)  # Threshold = 32LSBs * 15.625mg/LSB = 500mg. (~30 deg of tilt)
lis._write_register_byte(0x33, 0x01)  # Duration = 1LSBs * (1/25Hz) = 0.04s

The one difference for our case is we only care about two orientations - up and down. If you look on the back of the MagTag, you'll see the LIS3DH along with a little axis diagram:

So we can see that only the Y axis is involved for determining up / down orientation. We don't care about Z or X. Therefore, we only enable those bits in the associated register:

Look at the variable irq_config in the code to see how this is done.

Pin Alarm from Sensor

OK, so the LIS3DH is configured to generate (output) an interrupt on its INT1 pin. How does the ESP32-S2 see that (input) on its end? Very simple. The LIS3DH INT1 pin is routed to IO9 on the ESP32-S2:

Now it's just like the button example. We set up a pin alarm on the ESP32-S2 pin connected to the LIS3DH INT1 pin. In CircuitPython, this pin is accessed using board.ACCELEROMETER_INTERRUPT. The interrupt pin goes HIGH when it fires, so we set up the alarm to trigger on a value of True.

That's what this line of code does:

Download: file
pin_alarm = alarm.pin.PinAlarm(pin=board.ACCELEROMETER_INTERRUPT, value=True)

Code

Here's the complete code for the version that will change (wake) when flipped. Download this and save it as code.py into your CIRCUITPY folder so it will run automatically. Use Download: Project Zip to get a zip file with the bitmap images also, if not downloaded earlier.

import time
import board
import alarm
import displayio
import adafruit_lis3dh

# get the display
epd = board.DISPLAY

# set up accelerometer
lis = adafruit_lis3dh.LIS3DH_I2C(board.I2C(), address=0x19)

# See: ST Design Tip DT0008 - Simple screen rotation using
#      the accelerometer built-in 4D detection interrupt
# pylint: disable=protected-access
lis._write_register_byte(0x20, 0x3F)  # low power mode with ODR = 25Hz
lis._write_register_byte(0x22, 0x40)  # AOI1 interrupt generation is routed to INT1 pin
lis._write_register_byte(0x23, 0x80)  # FS = ±2g low power mode with BDU bit enabled
lis._write_register_byte(
    0x24, 0x0C
)  # Interrupt signal on INT1 pin is latched with D4D_INT1 bit enabled
lis._write_register_byte(
    0x32, 0x20
)  # Threshold = 32LSBs * 15.625mg/LSB = 500mg. (~30 deg of tilt)
lis._write_register_byte(0x33, 0x01)  # Duration = 1LSBs * (1/25Hz) = 0.04s

# read to clear
_ = lis._read_register_byte(0x31)

# get current accel values
_, y, _ = lis.acceleration

# update based on orientation
if y > 0:
    # upside up
    bmp_file = "clean.bmp"
    rotation = 270
    irq_config = 0b01000100
else:
    # upside down
    bmp_file = "dirty.bmp"
    rotation = 90
    irq_config = 0b01001000

# show bitmap
epd.rotation = rotation
with open(bmp_file, "rb") as fp:
    bitmap = displayio.OnDiskBitmap(fp)
    tile_grid = displayio.TileGrid(bitmap, pixel_shader=displayio.ColorConverter())
    group = displayio.Group(max_size=1)
    group.append(tile_grid)
    epd.show(group)
    time.sleep(epd.time_to_refresh + 0.01)
    epd.refresh()
    while epd.busy:
        pass

# config accelo irq
lis._write_register_byte(0x30, irq_config)

# go to sleep
pin_alarm = alarm.pin.PinAlarm(pin=board.ACCELEROMETER_INTERRUPT, value=True)
alarm.exit_and_deep_sleep_until_alarms(pin_alarm)

This guide was first published on Jan 12, 2021. It was last updated on Jan 12, 2021.

This page (Wake On Flip) was last updated on Feb 20, 2021.

Text editor powered by tinymce.