/ 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      """