#!/usr/bin/env python """ LIS3DH Python Library for Raspberry Pi Created by Matt Dyson (mattdyson.org) Project hosted at https://github.com/mattdy/python-lis3dh Version 1.0 - 10/01/16 Version 1.1 - 19/03/16 (Mal Smalley) Adding click detection Version 1.2 - 21/01/18 (holzfigure) Using new Adafruit library Adafruit_GPIO; Changing default busnumber (-1 -> 1); Fixing indentation issues; Increasing PEP8 and Python3 compatibility; Replacing variable name "range" (which is a keyword) with "g-range"; Requires the Adafruit-GPIO library https://github.com/adafruit/Adafruit_Python_GPIO Inspiration and assistance from: - https://github.com/adafruit/Adafruit_LIS3DH - https://www.adafruit.com/datasheets/LIS3DH.pdf """ from Adafruit_GPIO import I2C import RPi.GPIO as GPIO # needed for Hardware interrupt class LIS3DH: # I2C i2c = None I2C_ADDRESS_1 = 0x18 I2C_ADDRESS_2 = 0x19 # default I2C_DEFAULT = I2C_ADDRESS_1 # bus BUS_NUMBER = 1 # -1 # Ranges RANGE_2G = 0b00 # default RANGE_4G = 0b01 RANGE_8G = 0b10 RANGE_16G = 0b11 # default RANGE_DEFAULT = RANGE_2G # Refresh rates DATARATE_400HZ = 0b0111 # 400Hz # default DATARATE_200HZ = 0b0110 # 200Hz DATARATE_100HZ = 0b0101 # 100Hz DATARATE_50HZ = 0b0100 # 50Hz DATARATE_25HZ = 0b0011 # 25Hz DATARATE_10HZ = 0b0010 # 10Hz DATARATE_1HZ = 0b0001 # 1Hz DATARATE_POWERDOWN = 0 # Power down DATARATE_LOWPOWER_1K6HZ = 0b1000 # Low power mode (1.6KHz) DATARATE_LOWPOWER_5KHZ = ( 0b1001 # Low power mode (5KHz) / Normal power mode (1.25KHz) ) # default DATARATE_DEFAULT = DATARATE_400HZ # Registers REG_STATUS1 = 0x07 REG_OUTADC1_L = 0x08 REG_OUTADC1_H = 0x09 REG_OUTADC2_L = 0x0A REG_OUTADC2_H = 0x0B REG_OUTADC3_L = 0x0C REG_OUTADC3_H = 0x0D REG_INTCOUNT = 0x0E REG_WHOAMI = 0x0F # Device identification register REG_TEMPCFG = 0x1F REG_CTRL1 = ( 0x20 # Used for data rate selection, and enabling/disabling individual axis ) REG_CTRL2 = 0x21 REG_CTRL3 = 0x22 REG_CTRL4 = ( 0x23 # Used for BDU, scale selection, resolution selection and self-testing ) REG_CTRL5 = 0x24 REG_CTRL6 = 0x25 REG_REFERENCE = 0x26 REG_STATUS2 = 0x27 REG_OUT_X_L = 0x28 REG_OUT_X_H = 0x29 REG_OUT_Y_L = 0x2A REG_OUT_Y_H = 0x2B REG_OUT_Z_L = 0x2C REG_OUT_Z_H = 0x2D REG_FIFOCTRL = 0x2E REG_FIFOSRC = 0x2F REG_INT1CFG = 0x30 REG_INT1SRC = 0x31 REG_INT1THS = 0x32 REG_INT1DUR = 0x33 REG_CLICKCFG = 0x38 REG_CLICKSRC = 0x39 REG_CLICKTHS = 0x3A REG_TIMELIMIT = 0x3B REG_TIMELATENCY = 0x3C REG_TIMEWINDOW = 0x3D # Values DEVICE_ID = 0x33 INT_IO = 0x04 # GPIO pin for interrupt CLK_NONE = 0x00 CLK_SINGLE = 0x01 CLK_DOUBLE = 0x02 AXIS_X = 0x00 AXIS_Y = 0x01 AXIS_Z = 0x02 # changed busnumber to 1 (from -1) # alternative i2c address=0x19 def __init__( self, address=I2C_DEFAULT, bus=BUS_NUMBER, g_range=RANGE_DEFAULT, datarate=DATARATE_DEFAULT, debug=False, ): self.isDebug = debug self.debug("Initialising LIS3DH") # self.i2c = Adafruit_I2C(address, busnum=bus) self.i2c = I2C.Device(address, busnum=bus) self.address = address try: val = self.i2c.readU8(self.REG_WHOAMI) if val != self.DEVICE_ID: # raise Exception(("Device ID incorrect - expected 0x%X, " + # "got 0x%X at address 0x%X") % ( # self.DEVICE_ID, val, self.address)) raise Exception( ( "Device ID incorrect - expected 0x{:x}, " + "got 0x{:x} at address 0x{:x}" ).format(self.DEVICE_ID, val, self.address) ) self.debug( ("Successfully connected to LIS3DH " + "at address 0x{:x}").format( self.address ) ) except Exception as e: print("Error establishing connection with LIS3DH") print(e) # Enable all axis self.setAxisStatus(self.AXIS_X, True) self.setAxisStatus(self.AXIS_Y, True) self.setAxisStatus(self.AXIS_Z, True) # Set refresh rate (default: 400Hz) self.setDataRate(datarate) self.setHighResolution() self.setBDU() self.setRange(g_range) # Get reading from X axis def getX(self): return self.getAxis(self.AXIS_X) # Get reading from Y axis def getY(self): return self.getAxis(self.AXIS_Y) # Get reading from Z axis def getZ(self): return self.getAxis(self.AXIS_Z) # Get a reading from the desired axis def getAxis(self, axis): # Determine which register we need to read from (2 per axis) base = self.REG_OUT_X_L + (2 * axis) # Read the first register (lower bits) low = self.i2c.readU8(base) # Read the next register (higher bits) high = self.i2c.readU8(base + 1) # Combine the two components res = low | (high << 8) # Calculate the twos compliment of the result res = self.twosComp(res) # Fetch the range we're set to, so we can # accurately calculate the result g_range = self.getRange() divisor = 1 if g_range == self.RANGE_2G: divisor = 16380 elif g_range == self.RANGE_4G: divisor = 8190 elif g_range == self.RANGE_8G: divisor = 4096 elif g_range == self.RANGE_16G: divisor = 1365.33 return float(res) / divisor # Get the range that the sensor is currently set to def getRange(self): val = self.i2c.readU8(self.REG_CTRL4) # Get value from register val = val >> 4 # Remove lowest 4 bits val &= 0b0011 # Mask off two highest bits if val == self.RANGE_2G: return self.RANGE_2G elif val == self.RANGE_4G: return self.RANGE_4G elif val == self.RANGE_8G: return self.RANGE_8G else: return self.RANGE_16G # Set the range of the sensor (2G, 4G, 8G, 16G) def setRange(self, g_range): if g_range < 0 or g_range > 3: raise Exception("Tried to set invalid range") val = self.i2c.readU8(self.REG_CTRL4) # Get value from register val &= ~(0b110000) # Mask off lowest 4 bits val |= g_range << 4 # Write in our new range self.writeRegister(self.REG_CTRL4, val) # Write back to register # Enable or disable an individual axis # Read status from CTRL_REG1, then write back with # appropriate status bit changed def setAxisStatus(self, axis, enable): if axis < 0 or axis > 2: raise Exception("Tried to modify invalid axis") current = self.i2c.readU8(self.REG_CTRL1) status = 1 if enable else 0 final = self.setBit(current, axis, status) self.writeRegister(self.REG_CTRL1, final) def setInterrupt(self, mycallback): GPIO.setmode(GPIO.BCM) GPIO.setup(self.INT_IO, GPIO.IN) GPIO.add_event_detect(self.INT_IO, GPIO.RISING, callback=mycallback) def setClick( self, clickmode, clickthresh=80, timelimit=10, timelatency=20, timewindow=100, mycallback=None, ): if clickmode == self.CLK_NONE: val = self.i2c.readU8(self.REG_CTRL3) # Get value from register val &= ~(0x80) # unset bit 8 to disable interrupt self.writeRegister(self.REG_CTRL3, val) # Write back to register self.writeRegister(self.REG_CLICKCFG, 0) # disable all interrupts return self.writeRegister(self.REG_CTRL3, 0x80) # turn on int1 click self.writeRegister(self.REG_CTRL5, 0x08) # latch interrupt on int1 if clickmode == self.CLK_SINGLE: # turn on all axes & singletap self.writeRegister(self.REG_CLICKCFG, 0x15) if clickmode == self.CLK_DOUBLE: # turn on all axes & doubletap self.writeRegister(self.REG_CLICKCFG, 0x2A) # set timing parameters self.writeRegister(self.REG_CLICKTHS, clickthresh) self.writeRegister(self.REG_TIMELIMIT, timelimit) self.writeRegister(self.REG_TIMELATENCY, timelatency) self.writeRegister(self.REG_TIMEWINDOW, timewindow) if mycallback is not None: self.setInterrupt(mycallback) def getClick(self): reg = self.i2c.readU8(self.REG_CLICKSRC) # read click register self.i2c.readU8(self.REG_INT1SRC) # reset interrupt flag return reg # Set the rate (cycles per second) at which data is gathered def setDataRate(self, dataRate): val = self.i2c.readU8(self.REG_CTRL1) # Get current value val &= 0b1111 # Mask off lowest 4 bits val |= dataRate << 4 # Write in our new data rate to highest 4 bits self.writeRegister(self.REG_CTRL1, val) # Write back to register # Set whether we want to use high resolution or not def setHighResolution(self, highRes=True): val = self.i2c.readU8(self.REG_CTRL4) # Get current value status = 1 if highRes else 0 # High resolution is bit 4 of REG_CTRL4 final = self.setBit(val, 3, status) self.writeRegister(self.REG_CTRL4, final) # Set whether we want to use block data update or not # False = output registers not updated until MSB and LSB reading def setBDU(self, bdu=True): val = self.i2c.readU8(self.REG_CTRL4) # Get current value status = 1 if bdu else 0 # Block data update is bit 8 of REG_CTRL4 final = self.setBit(val, 7, status) self.writeRegister(self.REG_CTRL4, final) # Write the given value to the given register def writeRegister(self, register, value): self.debug("WRT {} to register 0x{:x}".format(bin(value), register)) self.i2c.write8(register, value) # Set the bit at index 'bit' to 'value' on 'input' and return def setBit(self, input, bit, value): mask = 1 << bit input &= ~mask if value: input |= mask return input # Return a 16-bit signed number (two's compliment) # Thanks to http://stackoverflow.com/questions/16124059/trying-to- # read-a-twos-complement-16bit-into-a-signed-decimal def twosComp(self, x): if 0x8000 & x: x = -(0x010000 - x) return x # Print an output of all registers def dumpRegisters(self): for x in range(0x0, 0x3D): read = self.i2c.readU8(x) print("{:x}: {}".format(x, bin(read))) def debug(self, message): if not self.isDebug: return print(message)