Skip to content

Commit 035a98c

Browse files
committed
Initial commit.
1 parent be4a0d1 commit 035a98c

File tree

6 files changed

+436
-0
lines changed

6 files changed

+436
-0
lines changed

.travis.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Travis CI configuration for automated .mpy file generation.
2+
# Version: 2.0 (support for both .mpy and packages)
3+
# Author: Tony DiCola
4+
# License: Public Domain
5+
# This configuration will work with Travis CI (travis-ci.org) to automacially
6+
# build .mpy files and packages for MicroPython when a new tagged release is
7+
# created. This file is relatively generic and can be shared across multiple
8+
# repositories by following these steps:
9+
# 1. Copy this file into a .travis.yml file in the root of the repository.
10+
# 2. Change the deploy > file section below to list each of the .mpy files or
11+
# package .zip files that should be generated.
12+
# For each .mpy file listed the config will automatically look for .py files
13+
# with the same name as the source for generating the .mpy files. Note that
14+
# the .mpy extension should be lower case!
15+
# For each .zip file listed the config will assume a folder with the same
16+
# name exists (minus the .zip extension) and will recursively walk the folder
17+
# to generate .mpy versions of all .py files EXCEPT __init__.py (not supported
18+
# right now because of a bug). Then a zip of the directory will be generated
19+
# with just the .mpy and __init__.py files.
20+
# 3. Commit the .travis.yml file and push it to GitHub.
21+
# 4. Go to travis-ci.org and find the repository (it needs to be setup to access
22+
# your github account, and your github account needs access to write to the
23+
# repo). Flip the 'ON' switch on for Travis and the repo, see the Travis
24+
# docs for more details: https://docs.travis-ci.com/user/getting-started/
25+
# 5. Get a GitHub 'personal access token' which has at least 'public_repo' or
26+
# 'repo' scope: https://help.github.com/articles/creating-an-access-token-for-command-line-use/
27+
# Keep this token safe and secure! Anyone with the token will be able to
28+
# access and write to your GitHub repositories. Travis will use the token
29+
# to attach the .mpy files to the release.
30+
# 6. In the Travis CI settings for the repository that was enabled find the
31+
# environment variable editing page: https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings
32+
# Add an environment variable named GITHUB_TOKEN and set it to the value
33+
# of the GitHub personal access token above. Keep 'Display value in build
34+
# log' flipped off.
35+
# 7. That's it! Tag a release and Travis should go to work to add .mpy files
36+
# to the release. It takes about a 2-3 minutes for a worker to spin up,
37+
# build mpy-cross, and add the binaries to the release.
38+
language: generic
39+
40+
sudo: true
41+
42+
deploy:
43+
provider: releases
44+
api_key: $GITHUB_TOKEN
45+
file:
46+
- adafruit_lis3dh.zip
47+
skip_cleanup: true
48+
on:
49+
tags: true
50+
51+
before_install:
52+
- wget https://raw.githubusercontent.com/adafruit/MicroPython_TravisCI_Deploy/master/install_dependencies.sh
53+
- chmod +x install_dependencies.sh
54+
- source install_dependencies.sh
55+
56+
before_deploy:
57+
- wget https://raw.githubusercontent.com/adafruit/MicroPython_TravisCI_Deploy/master/build_release.sh
58+
- chmod +x build_release.sh
59+
- ./build_release.sh

adafruit_lis3dh/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from adafruit_lis3dh.lis3dh import *

adafruit_lis3dh/lis3dh.py

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Adafruit LIS3DH Accelerometer CircuitPython Driver
2+
# Based on the Arduino LIS3DH driver from:
3+
# https://github.com/adafruit/Adafruit_LIS3DH/
4+
# Author: Tony DiCola
5+
# License: MIT License (https://en.wikipedia.org/wiki/MIT_License)
6+
import ustruct
7+
8+
import adafruit_bus_device.i2c_device as i2c_device
9+
import adafruit_bus_device.spi_device as spi_device
10+
11+
12+
# Register addresses:
13+
REG_OUTADC1_L = const(0x08)
14+
REG_WHOAMI = const(0x0F)
15+
REG_TEMPCFG = const(0x1F)
16+
REG_CTRL1 = const(0x20)
17+
REG_CTRL3 = const(0x22)
18+
REG_CTRL4 = const(0x23)
19+
REG_CTRL5 = const(0x24)
20+
REG_OUT_X_L = const(0x28)
21+
REG_CLICKCFG = const(0x38)
22+
REG_CLICKSRC = const(0x39)
23+
REG_CLICKTHS = const(0x3A)
24+
REG_TIMELIMIT = const(0x3B)
25+
REG_TIMELATENCY = const(0x3C)
26+
REG_TIMEWINDOW = const(0x3D)
27+
28+
# Register value constants:
29+
RANGE_16_G = const(0b11) # +/- 16g
30+
RANGE_8_G = const(0b10) # +/- 8g
31+
RANGE_4_G = const(0b01) # +/- 4g
32+
RANGE_2_G = const(0b00) # +/- 2g (default value)
33+
DATARATE_400_HZ = const(0b0111) # 400Hz
34+
DATARATE_200_HZ = const(0b0110) # 200Hz
35+
DATARATE_100_HZ = const(0b0101) # 100Hz
36+
DATARATE_50_HZ = const(0b0100) # 50Hz
37+
DATARATE_25_HZ = const(0b0011) # 25Hz
38+
DATARATE_10_HZ = const(0b0010) # 10 Hz
39+
DATARATE_1_HZ = const(0b0001) # 1 Hz
40+
DATARATE_POWERDOWN = const(0)
41+
DATARATE_LOWPOWER_1K6HZ = const(0b1000)
42+
DATARATE_LOWPOWER_5KHZ = const(0b1001)
43+
44+
45+
class LIS3DH:
46+
47+
def __init__(self):
48+
# Check device ID.
49+
device_id = self._read_register_byte(REG_WHOAMI)
50+
if device_id != 0x33:
51+
raise RuntimeError('Failed to find LIS3DH!')
52+
# Enable all axes, normal mode.
53+
self._write_register_byte(REG_CTRL1, 0x07)
54+
# Set 400Hz data rate.
55+
self.data_rate = DATARATE_400_HZ
56+
# High res & BDU enabled.
57+
self._write_register_byte(REG_CTRL4, 0x88)
58+
# DRDY on INT1.
59+
self._write_register_byte(REG_CTRL3, 0x10)
60+
# Enable ADCs.
61+
self._write_register_byte(REG_TEMPCFG, 0x80)
62+
63+
@property
64+
def data_rate(self):
65+
"""Get/set the data rate of the accelerometer. Can be DATA_RATE_400_HZ,
66+
DATA_RATE_200_HZ, DATA_RATE_100_HZ, DATA_RATE_50_HZ, DATA_RATE_25_HZ,
67+
DATA_RATE_10_HZ, DATA_RATE_1_HZ, DATA_RATE_POWERDOWN, DATA_RATE_LOWPOWER_1K6HZ,
68+
or DATA_RATE_LOWPOWER_5KHZ.
69+
"""
70+
ctl1 = self._read_register_byte(REG_CTRL1)
71+
return (ctl1 >> 4) & 0x0F
72+
73+
@data_rate.setter
74+
def data_rate(self, rate):
75+
ctl1 = self._read_register_byte(REG_CTRL1)
76+
ctl1 &= ~(0xF0)
77+
ctl1 |= rate << 4
78+
self._write_register_byte(REG_CTRL1, ctl1)
79+
80+
@property
81+
def range(self):
82+
"""Get/set the range of the accelerometer. Can be RANGE_2_G, RANGE_4_G,
83+
RANGE_8_G, or RANGE_16_G.
84+
"""
85+
ctl4 = self._read_register_byte(REG_CTRL4)
86+
return (ctl4 >> 4) & 0x03
87+
88+
@range.setter
89+
def range(self, range_value):
90+
ctl4 = self._read_register_byte(REG_CTRL4)
91+
ctl4 &= ~(0x30)
92+
ctl4 |= range_value << 4
93+
self._write_register_byte(REG_CTRL4, ctl4)
94+
95+
def read_accel_g(self):
96+
"""Read the x, y, z acceleration values. These values are returned in
97+
a 3-tuple and are in gravities (G).
98+
"""
99+
data = self._read_register(REG_OUT_X_L | 0x80, 6)
100+
x = ustruct.unpack('<h', data[0:2])[0]
101+
y = ustruct.unpack('<h', data[2:4])[0]
102+
z = ustruct.unpack('<h', data[4:6])[0]
103+
divider = 1
104+
accel_range = self.range
105+
if accel_range == RANGE_16_G:
106+
divider = 1365
107+
elif accel_range == RANGE_8_G:
108+
divider = 4096
109+
elif accel_range == RANGE_4_G:
110+
divider = 8190
111+
elif accel_range == RANGE_2_G:
112+
divider = 16380
113+
return (x / divider, y / divider, z / divider)
114+
115+
def read_adc_raw(self, adc):
116+
"""Retrieve the raw analog to digital converter value. ADC must be a
117+
value 1, 2, or 3.
118+
"""
119+
if adc < 1 or adc > 3:
120+
raise ValueError('ADC must be a value 1 to 3!')
121+
data = self._read_register((REG_OUTADC1_L+((adc-1)*2)) | 0x80, 2)
122+
return ustruct.unpack('<h', data[0:2])[0]
123+
124+
def read_adc_mV(self, adc):
125+
"""Read the specified analog to digital converter value in millivolts.
126+
ADC must be a value 1, 2, or 3. NOTE the ADC can only measure voltages
127+
in the range of ~900-1200mV!
128+
"""
129+
raw = self.read_adc_raw(adc)
130+
# Interpolate between 900mV and 1800mV, see:
131+
# https://learn.adafruit.com/adafruit-lis3dh-triple-axis-accelerometer-breakout/wiring-and-test#reading-the-3-adc-pins
132+
# This is a simplified linear interpolation of:
133+
# return y0 + (x-x0)*((y1-y0)/(x1-x0))
134+
# Where:
135+
# x = ADC value
136+
# x0 = -32512
137+
# x1 = 32512
138+
# y0 = 1800
139+
# y1 = 900
140+
return 1800+(raw+32512)*(-900/65024)
141+
142+
def read_click_raw(self):
143+
"""Read the raw click register byte value."""
144+
return self._read_register_byte(REG_CLICKSRC)
145+
146+
def read_click(self):
147+
"""Read a 2-tuple of bools where the first value is True if a single
148+
click was detected and the second value is True if a double click was
149+
detected.
150+
"""
151+
raw = self.read_click_raw()
152+
return (raw & 0x10 > 0, raw & 0x20 > 0)
153+
154+
def set_click(self, click, threshold, time_limit=10, time_latency=20, time_window=255):
155+
"""Set the click detection parameters. Must specify at least:
156+
click - Set to 0 to disable click detection, 1 to detect only single
157+
clicks, and 2 to detect single & double clicks.
158+
threshold - A threshold for the click detection. The higher the value
159+
the less sensitive the detection. This changes based on
160+
the accelerometer range. Good values are 5-10 for 16G,
161+
10-20 for 8G, 20-40 for 4G, and 40-80 for 2G.
162+
Optionally specify (see datasheet for meaning of these):
163+
time_limit - Time limit register value (default 10).
164+
time_latency - Time latency register value (default 20).
165+
time_window - Time window register value (default 255).
166+
"""
167+
if click < 0 or click > 2:
168+
raise ValueError('Click must be 0 (disabled), 1 (single click), or 2 (double click)!')
169+
if click == 0:
170+
# Disable click interrupt.
171+
r = self._read_register_byte(REG_CTRL3)
172+
r &= ~(0x80) # Turn off I1_CLICK.
173+
self._write_register_byte(REG_CTRL3, r)
174+
self._write_register_byte(REG_CLICKCFG, 0)
175+
return
176+
# Else enable click with specified parameters.
177+
self._write_register_byte(REG_CTRL3, 0x80) # Turn on int1 click.
178+
self._write_register_byte(REG_CTRL5, 0x08) # Latch interrupt on int1.
179+
if click == 1:
180+
self._write_register_byte(REG_CLICKCFG, 0x15) # Turn on all axes & singletap.
181+
elif click == 2:
182+
self._write_register_byte(REG_CLICKCFG, 0x2A) # Turn on all axes & doubletap.
183+
self._write_register_byte(REG_CLICKTHS, threshold)
184+
self._write_register_byte(REG_TIMELIMIT, time_limit)
185+
self._write_register_byte(REG_TIMELATENCY, time_latency)
186+
self._write_register_byte(REG_TIMEWINDOW, time_window)
187+
188+
def _read_register_byte(self, register):
189+
# Read a byte register value and return it.
190+
return self._read_register(register, 1)[0]
191+
192+
def _read_register(self, register, length):
193+
# Read an arbitrarily long register (specified by length number of
194+
# bytes) and return a bytearray of the retrieved data.
195+
# Subclasses MUST implement this!
196+
raise NotImplementedError
197+
198+
def _write_register_byte(self, register, value):
199+
# Write a single byte register at the specified register address.
200+
# Subclasses MUST implement this!
201+
raise NotImplementedError
202+
203+
204+
class LIS3DH_I2C(LIS3DH):
205+
206+
def __init__(self, i2c, address=0x18):
207+
self._i2c = i2c_device.I2CDevice(i2c, address)
208+
self._buffer = bytearray(6)
209+
super().__init__()
210+
211+
def _read_register(self, register, length):
212+
self._buffer[0] = register & 0xFF
213+
with self._i2c as i2c:
214+
i2c.writeto(self._buffer, start=0, end=1)
215+
i2c.readfrom_into(self._buffer, start=0, end=length)
216+
return self._buffer
217+
218+
def _write_register_byte(self, register, value):
219+
self._buffer[0] = register & 0xFF
220+
self._buffer[1] = value & 0xFF
221+
with self._i2c as i2c:
222+
i2c.writeto(self._buffer, start=0, end=2)
223+
224+
225+
class LIS3DH_SPI(LIS3DH):
226+
227+
def __init__(self, spi, cs, baudrate=500000):
228+
self._spi = spi_device.SPIDevice(spi, cs, baudrate=baudrate)
229+
super().__init__()
230+
231+
def _read_register(self, register, length):
232+
# TODO: Once https://github.com/adafruit/circuitpython/issues/108 is
233+
# resolved switch away from dynamic buffers to a single static one.
234+
request = bytearray(1)
235+
request[0] = (register | 0x80) & 0xFF # Read, bit 7 high.
236+
result = bytearray(length)
237+
with self._spi as spi:
238+
spi.write(request)
239+
spi.readinto(result)
240+
return result
241+
242+
def _write_register_byte(self, register, value):
243+
# TODO: Once https://github.com/adafruit/circuitpython/issues/108 is
244+
# resolved switch away from dynamic buffers to a single static one.
245+
request = bytearray(2)
246+
request[0] = (register & ~0x80) & 0xFF # Write, bit 7 low.
247+
request[1] = value & 0xFF
248+
with self._spi as spi:
249+
spi.write(request)

examples/accel.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Accelerometer example.
2+
# Reads the accelerometer x, y, z values and prints them every tenth of a second.
3+
# Open the serial port after running to see the output printed.
4+
# Author: Tony DiCola
5+
import time
6+
import board
7+
import adafruit_lis3dh
8+
9+
10+
# Uncomment _one_ of the hardware setups below depending on your wiring:
11+
12+
# Hardware I2C setup:
13+
import nativeio
14+
i2c = nativeio.I2C(board.SCL, board.SDA)
15+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
16+
17+
# Software I2C setup:
18+
#import bitbangio
19+
#i2c = bitbangio.I2C(board.SCL, board.SDA)
20+
#lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
21+
22+
# Hardware SPI setup:
23+
#import nativeio
24+
#spi = nativeio.SPI(board.SCK, board.MOSI, board.MISO)
25+
#cs = nativeio.DigitalInOut(board.D6) # Set to appropriate CS pin!
26+
#lis3dh = adafruit_lis3dh.LIS3DH_SPI(spi, cs)
27+
28+
29+
# Set range of accelerometer (can be RANGE_2_G, RANGE_4_G, RANGE_8_G or RANGE_16_G).
30+
lis3dh.range = adafruit_lis3dh.RANGE_2_G
31+
32+
# Loop forever printing accelerometer values
33+
while True:
34+
# Read accelerometer values (in gravities or G). Returns a 3-tuple of x, y,
35+
# z axis values.
36+
x, y, z = lis3dh.read_accel_g()
37+
print('x = {}G, y = {}G, z = {}G'.format(x, y, z))
38+
# Small delay to keep things responsive but give time for interrupt processing.
39+
time.sleep(0.1)

examples/adc.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Analog to digital converter example.
2+
# Will loop forever printing ADC channel 1 raw and mV values every second.
3+
# Open the serial port after running to see the output printed.
4+
# NOTE the ADC can only read voltages in the range of ~900mV to 1200mV!
5+
# Author: Tony DiCola
6+
import time
7+
import board
8+
import adafruit_lis3dh
9+
10+
11+
# Uncomment _one_ of the hardware setups below depending on your wiring:
12+
13+
# Hardware I2C setup:
14+
import nativeio
15+
i2c = nativeio.I2C(board.SCL, board.SDA)
16+
lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
17+
18+
# Software I2C setup:
19+
#import bitbangio
20+
#i2c = bitbangio.I2C(board.SCL, board.SDA)
21+
#lis3dh = adafruit_lis3dh.LIS3DH_I2C(i2c)
22+
23+
# Hardware SPI setup:
24+
#import nativeio
25+
#spi = nativeio.SPI(board.SCK, board.MOSI, board.MISO)
26+
#cs = nativeio.DigitalInOut(board.D6) # Set to appropriate CS pin!
27+
#lis3dh = adafruit_lis3dh.LIS3DH_SPI(spi, cs)
28+
29+
30+
# Loop forever printing ADC readings.
31+
while True:
32+
# Read raw ADC value. Specify which ADC to read: 1, 2, or 3.
33+
adc1_raw = lis3dh.read_adc_raw(1)
34+
# Or read the ADC value in millivolts:
35+
adc1_mV = lis3dh.read_adc_mV(1)
36+
print('ADC 1 = {} ({} mV)'.format(adc1_raw, adc1_mV))
37+
time.sleep(1)

0 commit comments

Comments
 (0)