/ adafruit_adxl34x.py
adafruit_adxl34x.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2018 Bryan Siepert for Adafruit Industries 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 13 # all 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 21 # THE SOFTWARE. 22 """ 23 `adafruit_adxl34x` 24 ==================================================== 25 26 A driver for the ADXL34x 3-axis accelerometer family 27 28 * Author(s): Bryan Siepert 29 30 Based on drivers by K. Townsend and Tony DiCola 31 32 Implementation Notes 33 -------------------- 34 35 **Hardware:** 36 https://www.adafruit.com/product/1231 37 38 **Software and Dependencies:** 39 40 * Adafruit CircuitPython firmware for the supported boards: 41 https://github.com/adafruit/circuitpython/releases 42 43 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 44 """ 45 46 from micropython import const 47 from adafruit_bus_device import i2c_device 48 49 try: 50 from struct import unpack 51 except ImportError: 52 from ustruct import unpack 53 __version__ = "0.0.0-auto.0" 54 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_ADXL34x.git" 55 # pylint: disable=bad-whitespace 56 _ADXL345_DEFAULT_ADDRESS = const(0x53) # Assumes ALT address pin low 57 58 # Conversion factors 59 _ADXL345_MG2G_MULTIPLIER = 0.004 # 4mg per lsb 60 _STANDARD_GRAVITY = 9.80665 # earth standard gravity 61 62 _REG_DEVID = const(0x00) # Device ID 63 _REG_THRESH_TAP = const(0x1D) # Tap threshold 64 _REG_OFSX = const(0x1E) # X-axis offset 65 _REG_OFSY = const(0x1F) # Y-axis offset 66 _REG_OFSZ = const(0x20) # Z-axis offset 67 _REG_DUR = const(0x21) # Tap duration 68 _REG_LATENT = const(0x22) # Tap latency 69 _REG_WINDOW = const(0x23) # Tap window 70 _REG_THRESH_ACT = const(0x24) # Activity threshold 71 _REG_THRESH_INACT = const(0x25) # Inactivity threshold 72 _REG_TIME_INACT = const(0x26) # Inactivity time 73 _REG_ACT_INACT_CTL = const(0x27) # Axis enable control for [in]activity detection 74 _REG_THRESH_FF = const(0x28) # Free-fall threshold 75 _REG_TIME_FF = const(0x29) # Free-fall time 76 _REG_TAP_AXES = const(0x2A) # Axis control for single/double tap 77 _REG_ACT_TAP_STATUS = const(0x2B) # Source for single/double tap 78 _REG_BW_RATE = const(0x2C) # Data rate and power mode control 79 _REG_POWER_CTL = const(0x2D) # Power-saving features control 80 _REG_INT_ENABLE = const(0x2E) # Interrupt enable control 81 _REG_INT_MAP = const(0x2F) # Interrupt mapping control 82 _REG_INT_SOURCE = const(0x30) # Source of interrupts 83 _REG_DATA_FORMAT = const(0x31) # Data format control 84 _REG_DATAX0 = const(0x32) # X-axis data 0 85 _REG_DATAX1 = const(0x33) # X-axis data 1 86 _REG_DATAY0 = const(0x34) # Y-axis data 0 87 _REG_DATAY1 = const(0x35) # Y-axis data 1 88 _REG_DATAZ0 = const(0x36) # Z-axis data 0 89 _REG_DATAZ1 = const(0x37) # Z-axis data 1 90 _REG_FIFO_CTL = const(0x38) # FIFO control 91 _REG_FIFO_STATUS = const(0x39) # FIFO status 92 _INT_SINGLE_TAP = const(0b01000000) # SINGLE_TAP bit 93 _INT_DOUBLE_TAP = const(0b00100000) # DOUBLE_TAP bit 94 _INT_ACT = const(0b00010000) # ACT bit 95 _INT_INACT = const(0b00001000) # INACT bit 96 _INT_FREE_FALL = const(0b00000100) # FREE_FALL bit 97 98 99 class DataRate: # pylint: disable=too-few-public-methods 100 """An enum-like class representing the possible data rates. 101 Possible values are 102 103 - ``DataRate.RATE_3200_HZ`` 104 - ``DataRate.RATE_1600_HZ`` 105 - ``DataRate.RATE_800_HZ`` 106 - ``DataRate.RATE_400_HZ`` 107 - ``DataRate.RATE_200_HZ`` 108 - ``DataRate.RATE_100_HZ`` 109 - ``DataRate.RATE_50_HZ`` 110 - ``DataRate.RATE_25_HZ`` 111 - ``DataRate.RATE_12_5_HZ`` 112 - ``DataRate.RATE_6_25HZ`` 113 - ``DataRate.RATE_3_13_HZ`` 114 - ``DataRate.RATE_1_56_HZ`` 115 - ``DataRate.RATE_0_78_HZ`` 116 - ``DataRate.RATE_0_39_HZ`` 117 - ``DataRate.RATE_0_20_HZ`` 118 - ``DataRate.RATE_0_10_HZ`` 119 120 """ 121 122 RATE_3200_HZ = const(0b1111) # 1600Hz Bandwidth 140mA IDD 123 RATE_1600_HZ = const(0b1110) # 800Hz Bandwidth 90mA IDD 124 RATE_800_HZ = const(0b1101) # 400Hz Bandwidth 140mA IDD 125 RATE_400_HZ = const(0b1100) # 200Hz Bandwidth 140mA IDD 126 RATE_200_HZ = const(0b1011) # 100Hz Bandwidth 140mA IDD 127 RATE_100_HZ = const(0b1010) # 50Hz Bandwidth 140mA IDD 128 RATE_50_HZ = const(0b1001) # 25Hz Bandwidth 90mA IDD 129 RATE_25_HZ = const(0b1000) # 12.5Hz Bandwidth 60mA IDD 130 RATE_12_5_HZ = const(0b0111) # 6.25Hz Bandwidth 50mA IDD 131 RATE_6_25HZ = const(0b0110) # 3.13Hz Bandwidth 45mA IDD 132 RATE_3_13_HZ = const(0b0101) # 1.56Hz Bandwidth 40mA IDD 133 RATE_1_56_HZ = const(0b0100) # 0.78Hz Bandwidth 34mA IDD 134 RATE_0_78_HZ = const(0b0011) # 0.39Hz Bandwidth 23mA IDD 135 RATE_0_39_HZ = const(0b0010) # 0.20Hz Bandwidth 23mA IDD 136 RATE_0_20_HZ = const(0b0001) # 0.10Hz Bandwidth 23mA IDD 137 RATE_0_10_HZ = const(0b0000) # 0.05Hz Bandwidth 23mA IDD (default value) 138 139 140 class Range: # pylint: disable=too-few-public-methods 141 """An enum-like class representing the possible measurement ranges in +/- G. 142 143 Possible values are 144 145 - ``Range.RANGE_16_G`` 146 - ``Range.RANGE_8_G`` 147 - ``Range.RANGE_4_G`` 148 - ``Range.RANGE_2_G`` 149 150 """ 151 152 RANGE_16_G = const(0b11) # +/- 16g 153 RANGE_8_G = const(0b10) # +/- 8g 154 RANGE_4_G = const(0b01) # +/- 4g 155 RANGE_2_G = const(0b00) # +/- 2g (default value) 156 157 158 # pylint: enable=bad-whitespace 159 class ADXL345: 160 """Driver for the ADXL345 3 axis accelerometer 161 162 :param ~busio.I2C i2c_bus: The I2C bus the ADXL345 is connected to. 163 :param address: The I2C device address for the sensor. Default is ``0x53``. 164 165 """ 166 167 def __init__(self, i2c, address=_ADXL345_DEFAULT_ADDRESS): 168 169 self._i2c = i2c_device.I2CDevice(i2c, address) 170 self._buffer = bytearray(6) 171 # set the 'measure' bit in to enable measurement 172 self._write_register_byte(_REG_POWER_CTL, 0x08) 173 self._write_register_byte(_REG_INT_ENABLE, 0x0) 174 175 self._enabled_interrupts = {} 176 self._event_status = {} 177 178 @property 179 def acceleration(self): 180 """The x, y, z acceleration values returned in a 3-tuple in m / s ^ 2.""" 181 x, y, z = unpack("<hhh", self._read_register(_REG_DATAX0, 6)) 182 x = x * _ADXL345_MG2G_MULTIPLIER * _STANDARD_GRAVITY 183 y = y * _ADXL345_MG2G_MULTIPLIER * _STANDARD_GRAVITY 184 z = z * _ADXL345_MG2G_MULTIPLIER * _STANDARD_GRAVITY 185 return (x, y, z) 186 187 @property 188 def events(self): 189 """ 190 ``events`` will return a dictionary with a key for each event type that has been enabled. 191 The possible keys are: 192 193 +------------+----------------------------------------------------------------------------+ 194 | Key | Description | 195 +============+============================================================================+ 196 | ``tap`` | True if a tap was detected recently. Whether it's looking for a single or | 197 | | double tap is determined by the tap param of `enable_tap_detection` | 198 +------------+----------------------------------------------------------------------------+ 199 | ``motion`` | True if the sensor has seen acceleration above the threshold | 200 | | set with `enable_motion_detection`. | 201 +------------+----------------------------------------------------------------------------+ 202 |``freefall``| True if the sensor was in freefall. Parameters are set when enabled with | 203 | | `enable_freefall_detection` | 204 +------------+----------------------------------------------------------------------------+ 205 206 207 """ 208 209 interrupt_source_register = self._read_clear_interrupt_source() 210 211 self._event_status.clear() 212 213 for event_type, value in self._enabled_interrupts.items(): 214 if event_type == "motion": 215 self._event_status[event_type] = ( 216 interrupt_source_register & _INT_ACT > 0 217 ) 218 if event_type == "tap": 219 if value == 1: 220 self._event_status[event_type] = ( 221 interrupt_source_register & _INT_SINGLE_TAP > 0 222 ) 223 else: 224 self._event_status[event_type] = ( 225 interrupt_source_register & _INT_DOUBLE_TAP > 0 226 ) 227 if event_type == "freefall": 228 self._event_status[event_type] = ( 229 interrupt_source_register & _INT_FREE_FALL > 0 230 ) 231 232 return self._event_status 233 234 def enable_motion_detection(self, *, threshold=18): 235 """ 236 The activity detection parameters. 237 238 :param int threshold: The value that acceleration on any axis must exceed to\ 239 register as active. The scale factor is 62.5 mg/LSB. 240 241 If you wish to set them yourself rather than using the defaults, 242 you must use keyword arguments:: 243 244 accelerometer.enable_motion_detection(threshold=20) 245 246 """ 247 active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE) 248 249 self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup 250 self._write_register_byte( 251 _REG_ACT_INACT_CTL, 0b01110000 252 ) # enable activity on X,Y,Z 253 self._write_register_byte(_REG_THRESH_ACT, threshold) 254 self._write_register_byte(_REG_INT_ENABLE, _INT_ACT) # Inactive interrupt only 255 256 active_interrupts |= _INT_ACT 257 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 258 self._enabled_interrupts["motion"] = True 259 260 def disable_motion_detection(self): 261 """ 262 Disable motion detection 263 """ 264 active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE) 265 active_interrupts &= ~_INT_ACT 266 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 267 self._enabled_interrupts.pop("motion") 268 269 def enable_freefall_detection(self, *, threshold=10, time=25): 270 """ 271 Freefall detection parameters: 272 273 :param int threshold: The value that acceleration on all axes must be under to\ 274 register as dropped. The scale factor is 62.5 mg/LSB. 275 276 :param int time: The amount of time that acceleration on all axes must be less than\ 277 ``threshhold`` to register as dropped. The scale factor is 5 ms/LSB. Values between 100 ms\ 278 and 350 ms (20 to 70) are recommended. 279 280 If you wish to set them yourself rather than using the defaults, 281 you must use keyword arguments:: 282 283 accelerometer.enable_freefall_detection(time=30) 284 285 """ 286 287 active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE) 288 289 self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup 290 self._write_register_byte(_REG_THRESH_FF, threshold) 291 self._write_register_byte(_REG_TIME_FF, time) 292 293 # add FREE_FALL to the active interrupts and set them to re-enable 294 active_interrupts |= _INT_FREE_FALL 295 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 296 self._enabled_interrupts["freefall"] = True 297 298 def disable_freefall_detection(self): 299 "Disable freefall detection" 300 active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE) 301 active_interrupts &= ~_INT_FREE_FALL 302 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 303 self._enabled_interrupts.pop("freefall") 304 305 def enable_tap_detection( 306 self, *, tap_count=1, threshold=20, duration=50, latency=20, window=255 307 ): # pylint: disable=line-too-long 308 """ 309 The tap detection parameters. 310 311 :param int tap_count: 1 to detect only single taps, and 2 to detect only double taps. 312 313 :param int threshold: A threshold for the tap detection. The scale factor is 62.5 mg/LSB\ 314 The higher the value the less sensitive the detection. 315 316 317 :param int duration: This caps the duration of the impulse above ``threshhold``.\ 318 Anything above ``duration`` won't register as a tap. The scale factor is 625 µs/LSB 319 320 :param int latency(double tap only): The length of time after the initial impulse\ 321 falls below ``threshold`` to start the window looking for a second impulse.\ 322 The scale factor is 1.25 ms/LSB. 323 324 :param int window(double tap only): The length of the window in which to look for a\ 325 second tap. The scale factor is 1.25 ms/LSB 326 327 If you wish to set them yourself rather than using the defaults, 328 you must use keyword arguments:: 329 330 accelerometer.enable_tap_detection(duration=30, threshold=25) 331 332 """ 333 active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE) 334 335 self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup 336 self._write_register_byte( 337 _REG_TAP_AXES, 0b00000111 338 ) # enable X, Y, Z axes for tap 339 self._write_register_byte(_REG_THRESH_TAP, threshold) 340 self._write_register_byte(_REG_DUR, duration) 341 342 if tap_count == 1: 343 active_interrupts |= _INT_SINGLE_TAP 344 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 345 self._enabled_interrupts["tap"] = 1 346 elif tap_count == 2: 347 self._write_register_byte(_REG_LATENT, latency) 348 self._write_register_byte(_REG_WINDOW, window) 349 350 active_interrupts |= _INT_DOUBLE_TAP 351 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 352 self._enabled_interrupts["tap"] = 2 353 else: 354 raise ValueError( 355 "tap must be 0 to disable, 1 for single tap, or 2 for double tap" 356 ) 357 358 def disable_tap_detection(self): 359 "Disable tap detection" 360 active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE) 361 active_interrupts &= ~_INT_SINGLE_TAP 362 active_interrupts &= ~_INT_DOUBLE_TAP 363 self._write_register_byte(_REG_INT_ENABLE, active_interrupts) 364 self._enabled_interrupts.pop("tap") 365 366 @property 367 def data_rate(self): 368 """The data rate of the sensor.""" 369 rate_register = self._read_register_unpacked(_REG_BW_RATE) 370 return rate_register & 0x0F 371 372 @data_rate.setter 373 def data_rate(self, val): 374 self._write_register_byte(_REG_BW_RATE, val) 375 376 @property 377 def range(self): 378 """The measurement range of the sensor.""" 379 range_register = self._read_register_unpacked(_REG_DATA_FORMAT) 380 return range_register & 0x03 381 382 @range.setter 383 def range(self, val): 384 # read the current value of the data format register 385 format_register = self._read_register_unpacked(_REG_DATA_FORMAT) 386 387 # clear the bottom 4 bits and update the data rate 388 format_register &= ~0x0F 389 format_register |= val 390 391 # Make sure that the FULL-RES bit is enabled for range scaling 392 format_register |= 0x08 393 394 # write the updated values 395 self._write_register_byte(_REG_DATA_FORMAT, format_register) 396 397 def _read_clear_interrupt_source(self): 398 return self._read_register_unpacked(_REG_INT_SOURCE) 399 400 def _read_register_unpacked(self, register): 401 return unpack("<b", self._read_register(register, 1))[0] 402 403 def _read_register(self, register, length): 404 self._buffer[0] = register & 0xFF 405 with self._i2c as i2c: 406 i2c.write(self._buffer, start=0, end=1) 407 i2c.readinto(self._buffer, start=0, end=length) 408 return self._buffer[0:length] 409 410 def _write_register_byte(self, register, value): 411 self._buffer[0] = register & 0xFF 412 self._buffer[1] = value & 0xFF 413 with self._i2c as i2c: 414 i2c.write(self._buffer, start=0, end=2) 415 416 417 class ADXL343(ADXL345): 418 """ 419 Stub class for the ADXL343 3-axis accelerometer 420 """