Skip to content

Commit 818e1f8

Browse files
authored
Merge pull request adafruit#4 from siddacious/master
Adding active, dropped, and tapped for adafruit#3
2 parents 3eabad6 + 2c5ca6f commit 818e1f8

File tree

5 files changed

+299
-37
lines changed

5 files changed

+299
-37
lines changed

adafruit_adxl34x.py

Lines changed: 224 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -58,36 +58,41 @@
5858
_ADXL345_MG2G_MULTIPLIER = 0.004 # 4mg per lsb
5959
_STANDARD_GRAVITY = 9.80665 # earth standard gravity
6060

61-
_ADXL345_REG_DEVID = const(0x00) # Device ID
62-
_ADXL345_REG_THRESH_TAP = const(0x1D) # Tap threshold
63-
_ADXL345_REG_OFSX = const(0x1E) # X-axis offset
64-
_ADXL345_REG_OFSY = const(0x1F) # Y-axis offset
65-
_ADXL345_REG_OFSZ = const(0x20) # Z-axis offset
66-
_ADXL345_REG_DUR = const(0x21) # Tap duration
67-
_ADXL345_REG_LATENT = const(0x22) # Tap latency
68-
_ADXL345_REG_WINDOW = const(0x23) # Tap window
69-
_ADXL345_REG_THRESH_ACT = const(0x24) # Activity threshold
70-
_ADXL345_REG_THRESH_INACT = const(0x25) # Inactivity threshold
71-
_ADXL345_REG_TIME_INACT = const(0x26) # Inactivity time
72-
_ADXL345_REG_ACT_INACT_CTL = const(0x27) # Axis enable control for [in]activity detection
73-
_ADXL345_REG_THRESH_FF = const(0x28) # Free-fall threshold
74-
_ADXL345_REG_TIME_FF = const(0x29) # Free-fall time
75-
_ADXL345_REG_TAP_AXES = const(0x2A) # Axis control for single/double tap
76-
_ADXL345_REG_ACT_TAP_STATUS = const(0x2B) # Source for single/double tap
77-
_ADXL345_REG_BW_RATE = const(0x2C) # Data rate and power mode control
78-
_ADXL345_REG_POWER_CTL = const(0x2D) # Power-saving features control
79-
_ADXL345_REG_INT_ENABLE = const(0x2E) # Interrupt enable control
80-
_ADXL345_REG_INT_MAP = const(0x2F) # Interrupt mapping control
81-
_ADXL345_REG_INT_SOURCE = const(0x30) # Source of interrupts
82-
_ADXL345_REG_DATA_FORMAT = const(0x31) # Data format control
83-
_ADXL345_REG_DATAX0 = const(0x32) # X-axis data 0
84-
_ADXL345_REG_DATAX1 = const(0x33) # X-axis data 1
85-
_ADXL345_REG_DATAY0 = const(0x34) # Y-axis data 0
86-
_ADXL345_REG_DATAY1 = const(0x35) # Y-axis data 1
87-
_ADXL345_REG_DATAZ0 = const(0x36) # Z-axis data 0
88-
_ADXL345_REG_DATAZ1 = const(0x37) # Z-axis data 1
89-
_ADXL345_REG_FIFO_CTL = const(0x38) # FIFO control
90-
_ADXL345_REG_FIFO_STATUS = const(0x39) # FIFO status
61+
_REG_DEVID = const(0x00) # Device ID
62+
_REG_THRESH_TAP = const(0x1D) # Tap threshold
63+
_REG_OFSX = const(0x1E) # X-axis offset
64+
_REG_OFSY = const(0x1F) # Y-axis offset
65+
_REG_OFSZ = const(0x20) # Z-axis offset
66+
_REG_DUR = const(0x21) # Tap duration
67+
_REG_LATENT = const(0x22) # Tap latency
68+
_REG_WINDOW = const(0x23) # Tap window
69+
_REG_THRESH_ACT = const(0x24) # Activity threshold
70+
_REG_THRESH_INACT = const(0x25) # Inactivity threshold
71+
_REG_TIME_INACT = const(0x26) # Inactivity time
72+
_REG_ACT_INACT_CTL = const(0x27) # Axis enable control for [in]activity detection
73+
_REG_THRESH_FF = const(0x28) # Free-fall threshold
74+
_REG_TIME_FF = const(0x29) # Free-fall time
75+
_REG_TAP_AXES = const(0x2A) # Axis control for single/double tap
76+
_REG_ACT_TAP_STATUS = const(0x2B) # Source for single/double tap
77+
_REG_BW_RATE = const(0x2C) # Data rate and power mode control
78+
_REG_POWER_CTL = const(0x2D) # Power-saving features control
79+
_REG_INT_ENABLE = const(0x2E) # Interrupt enable control
80+
_REG_INT_MAP = const(0x2F) # Interrupt mapping control
81+
_REG_INT_SOURCE = const(0x30) # Source of interrupts
82+
_REG_DATA_FORMAT = const(0x31) # Data format control
83+
_REG_DATAX0 = const(0x32) # X-axis data 0
84+
_REG_DATAX1 = const(0x33) # X-axis data 1
85+
_REG_DATAY0 = const(0x34) # Y-axis data 0
86+
_REG_DATAY1 = const(0x35) # Y-axis data 1
87+
_REG_DATAZ0 = const(0x36) # Z-axis data 0
88+
_REG_DATAZ1 = const(0x37) # Z-axis data 1
89+
_REG_FIFO_CTL = const(0x38) # FIFO control
90+
_REG_FIFO_STATUS = const(0x39) # FIFO status
91+
_INT_SINGLE_TAP = const(0b01000000) # SINGLE_TAP bit
92+
_INT_DOUBLE_TAP = const(0b00100000) # DOUBLE_TAP bit
93+
_INT_ACT = const(0b00010000) # ACT bit
94+
_INT_INACT = const(0b00001000) # INACT bit
95+
_INT_FREE_FALL = const(0b00000100) # FREE_FALL bit
9196

9297
class DataRate: #pylint: disable=too-few-public-methods
9398
"""An enum-like class representing the possible data rates.
@@ -155,40 +160,210 @@ class ADXL345:
155160
"""
156161
def __init__(self, i2c, address=_ADXL345_DEFAULT_ADDRESS):
157162

163+
158164
self._i2c = i2c_device.I2CDevice(i2c, address)
159165
self._buffer = bytearray(6)
160166
# set the 'measure' bit in to enable measurement
161-
self._write_register_byte(_ADXL345_REG_POWER_CTL, 0x08)
167+
self._write_register_byte(_REG_POWER_CTL, 0x08)
168+
self._write_register_byte(_REG_INT_ENABLE, 0x0)
169+
170+
self._enabled_interrupts = {}
171+
self._event_status = {}
162172

163173
@property
164174
def acceleration(self):
165175
"""The x, y, z acceleration values returned in a 3-tuple in m / s ^ 2."""
166-
x, y, z = unpack('<hhh', self._read_register(_ADXL345_REG_DATAX0, 6))
176+
x, y, z = unpack('<hhh', self._read_register(_REG_DATAX0, 6))
167177
x = x * _ADXL345_MG2G_MULTIPLIER * _STANDARD_GRAVITY
168178
y = y * _ADXL345_MG2G_MULTIPLIER * _STANDARD_GRAVITY
169179
z = z * _ADXL345_MG2G_MULTIPLIER * _STANDARD_GRAVITY
170180
return (x, y, z)
171181

182+
@property
183+
def events(self):
184+
"""
185+
``events`` will return a dictionary with a key for each event type that has been enabled.
186+
The possible keys are:
187+
188+
+------------+----------------------------------------------------------------------------+
189+
| Key | Description |
190+
+============+============================================================================+
191+
| ``tap`` | True if a tap was detected recently. Whether it's looking for a single or |
192+
| | double tap is determined by the tap param of `enable_tap_detection` |
193+
+------------+----------------------------------------------------------------------------+
194+
| ``motion`` | True if the sensor has seen acceleration above the threshold |
195+
| | set with `enable_motion_detection`. |
196+
+------------+----------------------------------------------------------------------------+
197+
|``freefall``| True if the sensor was in freefall. Parameters are set when enabled with |
198+
| | `enable_freefall_detection` |
199+
+------------+----------------------------------------------------------------------------+
200+
201+
202+
"""
203+
204+
interrupt_source_register = self._read_clear_interrupt_source()
205+
206+
self._event_status.clear()
207+
208+
for event_type, value in self._enabled_interrupts.items():
209+
if event_type == "motion":
210+
self._event_status[event_type] = interrupt_source_register & _INT_ACT > 0
211+
if event_type == "tap":
212+
if value == 1:
213+
self._event_status[event_type] = interrupt_source_register & _INT_SINGLE_TAP > 0
214+
else:
215+
self._event_status[event_type] = interrupt_source_register & _INT_DOUBLE_TAP > 0
216+
if event_type == "freefall":
217+
self._event_status[event_type] = interrupt_source_register & _INT_FREE_FALL > 0
218+
219+
return self._event_status
220+
221+
def enable_motion_detection(self, *, threshold=18):
222+
"""
223+
The activity detection parameters.
224+
225+
:param int threshold: The value that acceleration on any axis must exceed to\
226+
register as active. The scale factor is 62.5 mg/LSB.
227+
228+
If you wish to set them yourself rather than using the defaults,
229+
you must use keyword arguments::
230+
231+
accelerometer.enable_motion_detection(threshold=20)
232+
233+
"""
234+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
235+
236+
237+
self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup
238+
self._write_register_byte(_REG_ACT_INACT_CTL, 0b01110000) # enable activity on X,Y,Z
239+
self._write_register_byte(_REG_THRESH_ACT, threshold)
240+
self._write_register_byte(_REG_INT_ENABLE, _INT_ACT) # Inactive interrupt only
241+
242+
active_interrupts |= _INT_ACT
243+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
244+
self._enabled_interrupts["motion"] = True
245+
246+
def disable_motion_detection(self):
247+
"""
248+
Disable motion detection
249+
"""
250+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
251+
active_interrupts &= ~_INT_ACT
252+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
253+
self._enabled_interrupts.pop("motion")
254+
255+
256+
def enable_freefall_detection(self, *, threshold=10, time=25):
257+
"""
258+
Freefall detection parameters:
259+
260+
:param int threshold: The value that acceleration on all axes must be under to\
261+
register as dropped. The scale factor is 62.5 mg/LSB.
262+
263+
:param int time: The amount of time that acceleration on all axes must be less than\
264+
``threshhold`` to register as dropped. The scale factor is 5 ms/LSB. Values between 100 ms\
265+
and 350 ms (20 to 70) are recommended.
266+
267+
If you wish to set them yourself rather than using the defaults,
268+
you must use keyword arguments::
269+
270+
accelerometer.enable_freefall_detection(time=30)
271+
272+
"""
273+
274+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
275+
276+
self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup
277+
self._write_register_byte(_REG_THRESH_FF, threshold)
278+
self._write_register_byte(_REG_TIME_FF, time)
279+
280+
# add FREE_FALL to the active interrupts and set them to re-enable
281+
active_interrupts |= _INT_FREE_FALL
282+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
283+
self._enabled_interrupts["freefall"] = True
284+
285+
def disable_freefall_detection(self):
286+
"Disable freefall detection"
287+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
288+
active_interrupts &= ~_INT_FREE_FALL
289+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
290+
self._enabled_interrupts.pop("freefall")
291+
292+
def enable_tap_detection(self, *, tap_count=1, threshold=20, duration=50, latency=20, window=255):#pylint: disable=line-too-long
293+
"""
294+
The tap detection parameters.
295+
296+
:param int tap_count: 1 to detect only single taps, and 2 to detect only double taps.
297+
298+
:param int threshold: A threshold for the tap detection. The scale factor is 62.5 mg/LSB\
299+
The higher the value the less sensitive the detection. This changes based on the\
300+
accelerometer range.
301+
302+
:param int duration: This caps the duration of the impulse above ``threshhold``.\
303+
Anything above ``duration`` won't register as a tap. The scale factor is 625 µs/LSB
304+
305+
:param int latency(double tap only): The length of time after the initial impulse\
306+
falls below ``threshold`` to start the window looking for a second impulse.\
307+
The scale factor is 1.25 ms/LSB.
308+
309+
:param int window(double tap only): The length of the window in which to look for a\
310+
second tap. The scale factor is 1.25 ms/LSB
311+
312+
If you wish to set them yourself rather than using the defaults,
313+
you must use keyword arguments::
314+
315+
accelerometer.enable_tap_detection(duration=30, threshold=25)
316+
317+
"""
318+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
319+
320+
self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup
321+
self._write_register_byte(_REG_TAP_AXES, 0b00000111) # enable X, Y, Z axes for tap
322+
self._write_register_byte(_REG_THRESH_TAP, threshold)
323+
self._write_register_byte(_REG_DUR, duration)
324+
325+
if tap_count == 1:
326+
active_interrupts |= _INT_SINGLE_TAP
327+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
328+
self._enabled_interrupts["tap"] = 1
329+
elif tap_count == 2:
330+
self._write_register_byte(_REG_LATENT, latency)
331+
self._write_register_byte(_REG_WINDOW, window)
332+
333+
active_interrupts |= _INT_DOUBLE_TAP
334+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
335+
self._enabled_interrupts["tap"] = 2
336+
else:
337+
raise ValueError("tap must be 0 to disable, 1 for single tap, or 2 for double tap")
338+
339+
def disable_tap_detection(self):
340+
"Disable tap detection"
341+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
342+
active_interrupts &= ~_INT_SINGLE_TAP
343+
active_interrupts &= ~_INT_DOUBLE_TAP
344+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)
345+
self._enabled_interrupts.pop("tap")
346+
172347
@property
173348
def data_rate(self):
174349
"""The data rate of the sensor."""
175-
rate_register = unpack("<b", self._read_register(_ADXL345_REG_BW_RATE, 1))[0]
350+
rate_register = self._read_register_unpacked(_REG_BW_RATE)
176351
return rate_register & 0x0F
177352

178353
@data_rate.setter
179354
def data_rate(self, val):
180-
self._write_register_byte(_ADXL345_REG_BW_RATE, val)
355+
self._write_register_byte(_REG_BW_RATE, val)
181356

182357
@property
183358
def range(self):
184359
"""The measurement range of the sensor."""
185-
range_register = unpack("<b", self._read_register(_ADXL345_REG_DATA_FORMAT, 1))[0]
360+
range_register = self._read_register_unpacked(_REG_DATA_FORMAT)
186361
return range_register & 0x03
187362

188363
@range.setter
189364
def range(self, val):
190365
# read the current value of the data format register
191-
format_register = unpack("<b", self._read_register(_ADXL345_REG_DATA_FORMAT, 1))[0]
366+
format_register = self._read_register_unpacked(_REG_DATA_FORMAT)
192367

193368
# clear the bottom 4 bits and update the data rate
194369
format_register &= ~0x0F
@@ -198,7 +373,13 @@ def range(self, val):
198373
format_register |= 0x08
199374

200375
# write the updated values
201-
self._write_register_byte(_ADXL345_REG_DATA_FORMAT, format_register)
376+
self._write_register_byte(_REG_DATA_FORMAT, format_register)
377+
378+
def _read_clear_interrupt_source(self):
379+
return self._read_register_unpacked(_REG_INT_SOURCE)
380+
381+
def _read_register_unpacked(self, register):
382+
return unpack("<b", self._read_register(register, 1))[0]
202383

203384
def _read_register(self, register, length):
204385
self._buffer[0] = register & 0xFF
@@ -212,3 +393,9 @@ def _write_register_byte(self, register, value):
212393
self._buffer[1] = value & 0xFF
213394
with self._i2c as i2c:
214395
i2c.write(self._buffer, start=0, end=2)
396+
397+
def _config_iterrupt_register(self, register, value):
398+
active_interrupts = self._read_register_unpacked(_REG_INT_ENABLE)
399+
self._write_register_byte(_REG_INT_ENABLE, 0x0) # disable interrupts for setup
400+
self._write_register_byte(register, value)
401+
self._write_register_byte(_REG_INT_ENABLE, active_interrupts)

docs/examples.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,30 @@ Ensure your device works with this simple test.
66
.. literalinclude:: ../examples/adxl34x_simpletest.py
77
:caption: examples/adxl34x_simpletest.py
88
:linenos:
9+
10+
Motion detection
11+
----------------
12+
13+
Use the accelerometer to detect motion.
14+
15+
.. literalinclude:: ../examples/adxl34x_motion_detection_test.py
16+
:caption: examples/adxl34x_motion_detection_test.py
17+
:linenos:
18+
19+
Freefall detection
20+
------------------
21+
22+
Use the accelerometer to detect when something is dropped.
23+
24+
.. literalinclude:: ../examples/adxl34x_freefall_detection_test.py
25+
:caption: examples/adxl34x_freefall_detection_test.py
26+
:linenos:
27+
28+
Tap detection
29+
-------------
30+
31+
The accelerometer can also be configured to detect taps.
32+
33+
.. literalinclude:: ../examples/adxl34x_tap_detection_test.py
34+
:caption: examples/adxl34x_tap_detection_test.py
35+
:linenos:
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import time
2+
import board
3+
import busio
4+
import adafruit_adxl34x
5+
6+
i2c = busio.I2C(board.SCL, board.SDA)
7+
8+
accelerometer = adafruit_adxl34x.ADXL345(i2c)
9+
accelerometer.enable_freefall_detection()
10+
# alternatively you can specify attributes when you enable freefall detection for more control:
11+
# accelerometer.enable_freefall_detection(threshold=10,time=25)
12+
while True:
13+
print("%f %f %f"%accelerometer.acceleration)
14+
15+
print("Dropped: %s"%accelerometer.events["freefall"])
16+
time.sleep(0.5)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import time
2+
import board
3+
import busio
4+
import adafruit_adxl34x
5+
6+
i2c = busio.I2C(board.SCL, board.SDA)
7+
8+
accelerometer = adafruit_adxl34x.ADXL345(i2c)
9+
accelerometer.enable_motion_detection()
10+
# alternatively you can specify the threshold when you enable motion detection for more control:
11+
# accelerometer.enable_motion_detection(threshold=10)
12+
while True:
13+
print("%f %f %f"%accelerometer.acceleration)
14+
15+
print("Motion detected: %s"%accelerometer.events['motion'])
16+
time.sleep(0.5)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import time
2+
import board
3+
import busio
4+
import adafruit_adxl34x
5+
6+
i2c = busio.I2C(board.SCL, board.SDA)
7+
8+
accelerometer = adafruit_adxl34x.ADXL345(i2c)
9+
accelerometer.enable_tap_detection()
10+
# you can also configure the tap detection parameters when you enable tap detection:
11+
# accelerometer.enable_tap_detection(tap_count=2,threshold=20, duration=50)
12+
while True:
13+
print("%f %f %f"%accelerometer.acceleration)
14+
15+
print("Tapped: %s"%accelerometer.events['tap'])
16+
time.sleep(0.5)

0 commit comments

Comments
 (0)