├── LICENSE ├── LIS3DH.py ├── README.md ├── __init__.py └── testLIS3DH.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Matt Dyson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LIS3DH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | LIS3DH Python Library for Raspberry Pi 4 | Created by Matt Dyson (mattdyson.org) 5 | Project hosted at https://github.com/mattdy/python-lis3dh 6 | Version 1.0 - 10/01/16 7 | Version 1.1 - 19/03/16 (Mal Smalley) Adding click detection 8 | Version 1.2 - 21/01/18 (holzfigure) Using new Adafruit library Adafruit_GPIO; 9 | Changing default busnumber (-1 -> 1); 10 | Fixing indentation issues; 11 | Increasing PEP8 and Python3 compatibility; 12 | Replacing variable name "range" 13 | (which is a keyword) with "g-range"; 14 | 15 | Requires the Adafruit_Python_GPIO library 16 | https://github.com/adafruit/Adafruit_Python_GPIO 17 | 18 | Inspiration and assistance from: 19 | - https://github.com/adafruit/Adafruit_LIS3DH 20 | - https://www.adafruit.com/datasheets/LIS3DH.pdf 21 | """ 22 | 23 | from Adafruit_GPIO import I2C 24 | import RPi.GPIO as GPIO # needed for Hardware interrupt 25 | 26 | 27 | class LIS3DH: 28 | 29 | # I2C 30 | i2c = None 31 | I2C_ADDRESS_1 = 0x18 32 | I2C_ADDRESS_2 = 0x19 33 | # default 34 | I2C_DEFAULT = I2C_ADDRESS_1 35 | 36 | # bus 37 | BUS_NUMBER = 1 # -1 38 | 39 | # Ranges 40 | RANGE_2G = 0b00 # default 41 | RANGE_4G = 0b01 42 | RANGE_8G = 0b10 43 | RANGE_16G = 0b11 44 | # default 45 | RANGE_DEFAULT = RANGE_2G 46 | 47 | # Refresh rates 48 | DATARATE_400HZ = 0b0111 # 400Hz # default 49 | DATARATE_200HZ = 0b0110 # 200Hz 50 | DATARATE_100HZ = 0b0101 # 100Hz 51 | DATARATE_50HZ = 0b0100 # 50Hz 52 | DATARATE_25HZ = 0b0011 # 25Hz 53 | DATARATE_10HZ = 0b0010 # 10Hz 54 | DATARATE_1HZ = 0b0001 # 1Hz 55 | DATARATE_POWERDOWN = 0 # Power down 56 | DATARATE_LOWPOWER_1K6HZ = 0b1000 # Low power mode (1.6KHz) 57 | DATARATE_LOWPOWER_5KHZ = 0b1001 # Low power mode (5KHz) / Normal power mode (1.25KHz) 58 | # default 59 | DATARATE_DEFAULT = DATARATE_400HZ 60 | 61 | # Registers 62 | REG_STATUS1 = 0x07 63 | REG_OUTADC1_L = 0x08 64 | REG_OUTADC1_H = 0x09 65 | REG_OUTADC2_L = 0x0A 66 | REG_OUTADC2_H = 0x0B 67 | REG_OUTADC3_L = 0x0C 68 | REG_OUTADC3_H = 0x0D 69 | REG_INTCOUNT = 0x0E 70 | REG_WHOAMI = 0x0F # Device identification register 71 | REG_TEMPCFG = 0x1F 72 | REG_CTRL1 = 0x20 # Used for data rate selection, and enabling/disabling individual axis 73 | REG_CTRL2 = 0x21 74 | REG_CTRL3 = 0x22 75 | REG_CTRL4 = 0x23 # Used for BDU, scale selection, resolution selection and self-testing 76 | REG_CTRL5 = 0x24 77 | REG_CTRL6 = 0x25 78 | REG_REFERENCE = 0x26 79 | REG_STATUS2 = 0x27 80 | REG_OUT_X_L = 0x28 81 | REG_OUT_X_H = 0x29 82 | REG_OUT_Y_L = 0x2A 83 | REG_OUT_Y_H = 0x2B 84 | REG_OUT_Z_L = 0x2C 85 | REG_OUT_Z_H = 0x2D 86 | REG_FIFOCTRL = 0x2E 87 | REG_FIFOSRC = 0x2F 88 | REG_INT1CFG = 0x30 89 | REG_INT1SRC = 0x31 90 | REG_INT1THS = 0x32 91 | REG_INT1DUR = 0x33 92 | REG_CLICKCFG = 0x38 93 | REG_CLICKSRC = 0x39 94 | REG_CLICKTHS = 0x3A 95 | REG_TIMELIMIT = 0x3B 96 | REG_TIMELATENCY = 0x3C 97 | REG_TIMEWINDOW = 0x3D 98 | 99 | # Values 100 | DEVICE_ID = 0x33 101 | INT_IO = 0x04 # GPIO pin for interrupt 102 | CLK_NONE = 0x00 103 | CLK_SINGLE = 0x01 104 | CLK_DOUBLE = 0x02 105 | 106 | AXIS_X = 0x00 107 | AXIS_Y = 0x01 108 | AXIS_Z = 0x02 109 | 110 | # changed busnumber to 1 (from -1) 111 | # alternative i2c address=0x19 112 | def __init__(self, address=I2C_DEFAULT, bus=BUS_NUMBER, 113 | g_range=RANGE_DEFAULT, datarate=DATARATE_DEFAULT, 114 | debug=False): 115 | self.isDebug = debug 116 | self.debug("Initialising LIS3DH") 117 | 118 | # self.i2c = Adafruit_I2C(address, busnum=bus) 119 | self.i2c = I2C.Device(address, busnum=bus) 120 | self.address = address 121 | 122 | try: 123 | val = self.i2c.readU8(self.REG_WHOAMI) 124 | if val != self.DEVICE_ID: 125 | # raise Exception(("Device ID incorrect - expected 0x%X, " + 126 | # "got 0x%X at address 0x%X") % ( 127 | # self.DEVICE_ID, val, self.address)) 128 | raise Exception(("Device ID incorrect - expected 0x{:x}, " + 129 | "got 0x{:x} at address 0x{:x}").format( 130 | self.DEVICE_ID, val, self.address)) 131 | 132 | self.debug(("Successfully connected to LIS3DH " + 133 | "at address 0x{:x}").format(self.address)) 134 | except Exception as e: 135 | print("Error establishing connection with LIS3DH") 136 | print(e) 137 | 138 | # Enable all axis 139 | self.setAxisStatus(self.AXIS_X, True) 140 | self.setAxisStatus(self.AXIS_Y, True) 141 | self.setAxisStatus(self.AXIS_Z, True) 142 | 143 | # Set refresh rate (default: 400Hz) 144 | self.setDataRate(datarate) 145 | 146 | self.setHighResolution() 147 | self.setBDU() 148 | 149 | self.setRange(g_range) 150 | 151 | # Get reading from X axis 152 | def getX(self): 153 | return self.getAxis(self.AXIS_X) 154 | 155 | # Get reading from Y axis 156 | def getY(self): 157 | return self.getAxis(self.AXIS_Y) 158 | 159 | # Get reading from Z axis 160 | def getZ(self): 161 | return self.getAxis(self.AXIS_Z) 162 | 163 | # Get a reading from the desired axis 164 | def getAxis(self, axis): 165 | # Determine which register we need to read from (2 per axis) 166 | base = self.REG_OUT_X_L + (2 * axis) 167 | 168 | # Read the first register (lower bits) 169 | low = self.i2c.readU8(base) 170 | # Read the next register (higher bits) 171 | high = self.i2c.readU8(base + 1) 172 | # Combine the two components 173 | res = low | (high << 8) 174 | # Calculate the twos compliment of the result 175 | res = self.twosComp(res) 176 | 177 | # Fetch the range we're set to, so we can 178 | # accurately calculate the result 179 | g_range = self.getRange() 180 | divisor = 1 181 | if g_range == self.RANGE_2G: divisor = 16380 182 | elif g_range == self.RANGE_4G: divisor = 8190 183 | elif g_range == self.RANGE_8G: divisor = 4096 184 | elif g_range == self.RANGE_16G: divisor = 1365.33 185 | 186 | return float(res) / divisor 187 | 188 | # Get the range that the sensor is currently set to 189 | def getRange(self): 190 | val = self.i2c.readU8(self.REG_CTRL4) # Get value from register 191 | val = (val >> 4) # Remove lowest 4 bits 192 | val &= 0b0011 # Mask off two highest bits 193 | 194 | if val == self.RANGE_2G: return self.RANGE_2G 195 | elif val == self.RANGE_4G: return self.RANGE_4G 196 | elif val == self.RANGE_8G: return self.RANGE_8G 197 | else: return self.RANGE_16G 198 | 199 | # Set the range of the sensor (2G, 4G, 8G, 16G) 200 | def setRange(self, g_range): 201 | if g_range < 0 or g_range > 3: 202 | raise Exception("Tried to set invalid range") 203 | 204 | val = self.i2c.readU8(self.REG_CTRL4) # Get value from register 205 | val &= ~(0b110000) # Mask off lowest 4 bits 206 | val |= (g_range << 4) # Write in our new range 207 | self.writeRegister(self.REG_CTRL4, val) # Write back to register 208 | 209 | # Enable or disable an individual axis 210 | # Read status from CTRL_REG1, then write back with 211 | # appropriate status bit changed 212 | def setAxisStatus(self, axis, enable): 213 | if axis < 0 or axis > 2: 214 | raise Exception("Tried to modify invalid axis") 215 | 216 | current = self.i2c.readU8(self.REG_CTRL1) 217 | status = 1 if enable else 0 218 | final = self.setBit(current, axis, status) 219 | self.writeRegister(self.REG_CTRL1, final) 220 | 221 | def setInterrupt(self, mycallback): 222 | GPIO.setmode(GPIO.BCM) 223 | GPIO.setup(self.INT_IO, GPIO.IN) 224 | GPIO.add_event_detect(self.INT_IO, GPIO.RISING, callback=mycallback) 225 | 226 | def setClick(self, clickmode, clickthresh=80, 227 | timelimit=10, timelatency=20, timewindow=100, 228 | mycallback=None): 229 | if (clickmode == self.CLK_NONE): 230 | val = self.i2c.readU8(self.REG_CTRL3) # Get value from register 231 | val &= ~(0x80) # unset bit 8 to disable interrupt 232 | self.writeRegister(self.REG_CTRL3, val) # Write back to register 233 | self.writeRegister(self.REG_CLICKCFG, 0) # disable all interrupts 234 | return 235 | self.writeRegister(self.REG_CTRL3, 0x80) # turn on int1 click 236 | self.writeRegister(self.REG_CTRL5, 0x08) # latch interrupt on int1 237 | 238 | if (clickmode == self.CLK_SINGLE): 239 | # turn on all axes & singletap 240 | self.writeRegister(self.REG_CLICKCFG, 0x15) 241 | if (clickmode == self.CLK_DOUBLE): 242 | # turn on all axes & doubletap 243 | self.writeRegister(self.REG_CLICKCFG, 0x2A) 244 | 245 | # set timing parameters 246 | self.writeRegister(self.REG_CLICKTHS, clickthresh) 247 | self.writeRegister(self.REG_TIMELIMIT, timelimit) 248 | self.writeRegister(self.REG_TIMELATENCY, timelatency) 249 | self.writeRegister(self.REG_TIMEWINDOW, timewindow) 250 | 251 | if mycallback is not None: 252 | self.setInterrupt(mycallback) 253 | 254 | def getClick(self): 255 | reg = self.i2c.readU8(self.REG_CLICKSRC) # read click register 256 | self.i2c.readU8(self.REG_INT1SRC) # reset interrupt flag 257 | return reg 258 | 259 | # Set the rate (cycles per second) at which data is gathered 260 | def setDataRate(self, dataRate): 261 | val = self.i2c.readU8(self.REG_CTRL1) # Get current value 262 | val &= 0b1111 # Mask off lowest 4 bits 263 | val |= (dataRate << 4) # Write in our new data rate to highest 4 bits 264 | self.writeRegister(self.REG_CTRL1, val) # Write back to register 265 | 266 | # Set whether we want to use high resolution or not 267 | def setHighResolution(self, highRes=True): 268 | val = self.i2c.readU8(self.REG_CTRL4) # Get current value 269 | status = 1 if highRes else 0 270 | 271 | # High resolution is bit 4 of REG_CTRL4 272 | final = self.setBit(val, 3, status) 273 | self.writeRegister(self.REG_CTRL4, final) 274 | 275 | # Set whether we want to use block data update or not 276 | # False = output registers not updated until MSB and LSB reading 277 | def setBDU(self, bdu=True): 278 | val = self.i2c.readU8(self.REG_CTRL4) # Get current value 279 | status = 1 if bdu else 0 280 | 281 | # Block data update is bit 8 of REG_CTRL4 282 | final = self.setBit(val, 7, status) 283 | self.writeRegister(self.REG_CTRL4, final) 284 | 285 | # Write the given value to the given register 286 | def writeRegister(self, register, value): 287 | self.debug("WRT {} to register 0x{:x}".format( 288 | bin(value), register)) 289 | self.i2c.write8(register, value) 290 | 291 | # Set the bit at index 'bit' to 'value' on 'input' and return 292 | def setBit(self, input, bit, value): 293 | mask = 1 << bit 294 | input &= ~mask 295 | if value: 296 | input |= mask 297 | return input 298 | 299 | # Return a 16-bit signed number (two's compliment) 300 | # Thanks to http://stackoverflow.com/questions/16124059/trying-to- 301 | # read-a-twos-complement-16bit-into-a-signed-decimal 302 | def twosComp(self, x): 303 | if (0x8000 & x): 304 | x = - (0x010000 - x) 305 | return x 306 | 307 | # Print an output of all registers 308 | def dumpRegisters(self): 309 | for x in range(0x0, 0x3D): 310 | read = self.i2c.readU8(x) 311 | print("{:x}: {}".format(x, bin(read))) 312 | 313 | def debug(self, message): 314 | if not self.isDebug: 315 | return 316 | print(message) 317 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **DEPRECATION WARNING** 2 | Adafruit has deprecated their [Adafruit_Python_GPIO library](https://github.com/adafruit/Adafruit_Python_GPIO) in favour of [blinka](https://github.com/adafruit/Adafruit_Blinka), which allows you to use their full-fledged CircuitPython code for the sensor. This can be found here: https://github.com/adafruit/Adafruit_CircuitPython_LIS3DH. 3 | 4 | The library in this repository will still function under Python 2, however [Python 2 has reached end-of-life](https://www.python.org/doc/sunset-python-2/) and you should therefore move to using the above libraries with Python 3. 5 | 6 | Because of this, this repository will be archived and no further updates will be made. My thanks to everyone who has contributed to it in the past. 7 | 8 | # python-lis3dh 9 | Python library for using a [LIS3DH triple-axis accelerometer](https://www.adafruit.com/products/2809) on a Raspberry Pi 10 | 11 | This is not a complete implementation of all the features of the LIS3DH - if you can help add more functionality then please contribute! 12 | 13 | ## Requirements 14 | Requires the Adafruit_Python_GPIO library which can be found at https://github.com/adafruit/Adafruit_Python_GPIO 15 | 16 | ## Useful reading 17 | * https://www.adafruit.com/product/2809 18 | * https://www.adafruit.com/datasheets/LIS3DH.pdf 19 | * https://github.com/adafruit/Adafruit_LIS3DH/blob/master/Adafruit_LIS3DH.cpp 20 | * https://github.com/adafruit/Adafruit_LIS3DH/blob/master/Adafruit_LIS3DH.h 21 | * https://github.com/adafruit/Adafruit_Python_GPIO 22 | 23 | ## Credits 24 | * [Matt Dyson](http://mattdyson.org) - Original implementation 25 | * [Mal Smalley](https://github.com/MalSmalley) - Implementation of 'click' functionality 26 | * [Tunniel Holzfigure](https://github.com/holzfigure) - Adaptation for new Adafruit library 27 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /testLIS3DH.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from LIS3DH import LIS3DH 4 | from time import sleep 5 | 6 | 7 | def clickcallback(channel): 8 | # interrupt handler callback 9 | print("Interrupt detected") 10 | click = sensor.getClick() 11 | print("Click detected (0x%2X)" % (click)) 12 | if (click & 0x10): 13 | print(" single click") 14 | if (click & 0x20): 15 | print(" double click") 16 | 17 | 18 | if __name__ == '__main__': 19 | sensor = LIS3DH(debug=True) 20 | sensor.setRange(LIS3DH.RANGE_2G) 21 | sensor.setClick(LIS3DH.CLK_DOUBLE, 200, mycallback=clickcallback) 22 | 23 | # second accelerometer 24 | # s2 = LIS3DH(address=0x19, debug=True) 25 | 26 | print("Starting stream") 27 | while True: 28 | 29 | x = sensor.getX() 30 | y = sensor.getY() 31 | z = sensor.getZ() 32 | 33 | # raw values 34 | print("\rX: %.6f\tY: %.6f\tZ: %.6f" % (x, y, z)) 35 | sleep(0.1) 36 | 37 | # click sensor if polling & not using interrupt 38 | # click = sensor.getClick() 39 | # if (click & 0x30) : 40 | # print "Click detected (0x%2X)" % (click) 41 | # if (click & 0x10): print " single click" 42 | # if (click & 0x20): print " double click" 43 | --------------------------------------------------------------------------------