periph 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- periph/__init__.py +1 -0
- periph/chips/accelerometer/adxl345.py +524 -0
- periph/chips/accelerometer/adxl362.py +704 -0
- periph/chips/adc_dac/ad7705.py +476 -0
- periph/chips/adc_dac/ad7706.py +495 -0
- periph/chips/adc_dac/hx710a.py +187 -0
- periph/chips/adc_dac/hx710b.py +189 -0
- periph/chips/adc_dac/hx711.py +170 -0
- periph/chips/adc_dac/mcp4725.py +167 -0
- periph/chips/adc_dac/mcp4728.py +317 -0
- periph/chips/adc_dac/pcf8591.py +199 -0
- periph/chips/comms/mcp2515.py +651 -0
- periph/chips/comms/rda5807m.py +420 -0
- periph/chips/comms/rfm9x.py +594 -0
- periph/chips/display/pcf8576.py +223 -0
- periph/chips/environmental/aht21.py +150 -0
- periph/chips/environmental/bme280.py +384 -0
- periph/chips/environmental/bme680.py +501 -0
- periph/chips/gas/ens160.py +273 -0
- periph/chips/gnss/neo6.py +439 -0
- periph/chips/gyroscope/l3g4200d.py +448 -0
- periph/chips/gyroscope/l3gd20h.py +337 -0
- periph/chips/humidity/dht11.py +123 -0
- periph/chips/imu/mpu6050.py +251 -0
- periph/chips/imu/mpu9250.py +378 -0
- periph/chips/imu/mpu9255.py +434 -0
- periph/chips/io_expander/mcp23017.py +494 -0
- periph/chips/io_expander/pcf8574.py +401 -0
- periph/chips/io_expander/pcf8575.py +420 -0
- periph/chips/io_expander/tpic6b595.py +271 -0
- periph/chips/led/_color.py +23 -0
- periph/chips/led/_neopixel_rgb_base.py +175 -0
- periph/chips/led/_neopixel_rgbw_base.py +193 -0
- periph/chips/led/apa102.py +200 -0
- periph/chips/led/sk6812rgbw.py +52 -0
- periph/chips/led/ws2812b.py +46 -0
- periph/chips/led/ws2814.py +52 -0
- periph/chips/light/apds9960.py +496 -0
- periph/chips/light/apds_9930.py +364 -0
- periph/chips/magnetometer/as5600.py +290 -0
- periph/chips/magnetometer/hmc5883l.py +210 -0
- periph/chips/memory/_24aa02uid.py +175 -0
- periph/chips/motor/drv8830.py +236 -0
- periph/chips/other/mpr121.py +465 -0
- periph/chips/power/ade7953.py +1196 -0
- periph/chips/power/ina219.py +195 -0
- periph/chips/power/ina226.py +217 -0
- periph/chips/power/ina3221.py +359 -0
- periph/chips/pressure/bmp085.py +234 -0
- periph/chips/pressure/bmp180.py +234 -0
- periph/chips/pressure/bmp280.py +292 -0
- periph/chips/pressure/bmp384.py +471 -0
- periph/chips/pressure/bmp581.py +561 -0
- periph/chips/pressure/lps22df.py +394 -0
- periph/chips/pressure/lps28dfw.py +337 -0
- periph/chips/pressure/lps33hw.py +385 -0
- periph/chips/rfid/mfrc522.py +818 -0
- periph/chips/rtc/ds3231.py +522 -0
- periph/chips/rtc/pcf8523.py +549 -0
- periph/chips/temperature/mcp9808.py +453 -0
- periph/chips/temperature/tmp117.py +461 -0
- periph/chips/tof/_vl53_base.py +174 -0
- periph/chips/tof/vl53l0x.py +816 -0
- periph/chips/tof/vl53l1x.py +815 -0
- periph/connection/__init__.py +31 -0
- periph/connection/base.py +103 -0
- periph/connection/dhtxx_auto.py +52 -0
- periph/connection/dhtxx_circuitpython.py +138 -0
- periph/connection/dhtxx_linux.py +230 -0
- periph/connection/dhtxx_micropython.py +133 -0
- periph/connection/dhtxx_mock.py +34 -0
- periph/connection/hx711_auto.py +66 -0
- periph/connection/hx711_circuitpython.py +114 -0
- periph/connection/hx711_linux.py +121 -0
- periph/connection/hx711_micropython.py +113 -0
- periph/connection/hx711_mock.py +43 -0
- periph/connection/i2c_auto.py +54 -0
- periph/connection/i2c_circuitpython.py +84 -0
- periph/connection/i2c_linux.py +81 -0
- periph/connection/i2c_micropython.py +65 -0
- periph/connection/i2c_mock.py +57 -0
- periph/connection/input_pin.py +259 -0
- periph/connection/neo6_mock.py +36 -0
- periph/connection/neopixel_auto.py +75 -0
- periph/connection/neopixel_circuitpython.py +44 -0
- periph/connection/neopixel_linux.py +47 -0
- periph/connection/neopixel_micropython.py +38 -0
- periph/connection/output_pin.py +81 -0
- periph/connection/sipo_circuitpython.py +119 -0
- periph/connection/sipo_linux.py +149 -0
- periph/connection/sipo_micropython.py +104 -0
- periph/connection/smbus_auto.py +68 -0
- periph/connection/smbus_circuitpython.py +118 -0
- periph/connection/smbus_linux.py +119 -0
- periph/connection/smbus_micropython.py +100 -0
- periph/connection/spi_auto.py +79 -0
- periph/connection/spi_circuitpython.py +97 -0
- periph/connection/spi_linux.py +66 -0
- periph/connection/spi_micropython.py +64 -0
- periph/connection/spi_mock.py +96 -0
- periph/connection/uart_auto.py +76 -0
- periph/connection/uart_circuitpython.py +81 -0
- periph/connection/uart_linux.py +120 -0
- periph/connection/uart_micropython.py +81 -0
- periph-1.2.1.dist-info/METADATA +113 -0
- periph-1.2.1.dist-info/RECORD +108 -0
- periph-1.2.1.dist-info/WHEEL +5 -0
- periph-1.2.1.dist-info/top_level.txt +1 -0
periph/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .connection import Connection
|
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
"""ADXL345 — 3-axis MEMS accelerometer (Analog Devices).
|
|
2
|
+
|
|
3
|
+
Provides 3-axis acceleration readings with no configuration beyond the
|
|
4
|
+
connection. The chip supports both I²C and SPI; the same driver file is
|
|
5
|
+
used on MicroPython, CircuitPython, and Linux hosts.
|
|
6
|
+
|
|
7
|
+
Default configuration (written at construction):
|
|
8
|
+
- Data format: full-resolution (3.9 mg/LSB at all ranges), ±2 g
|
|
9
|
+
- Output data rate: 100 Hz
|
|
10
|
+
- FIFO: bypass
|
|
11
|
+
- All interrupts disabled
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
connection: Configured I²C or SPI connection pointing at the device.
|
|
15
|
+
bus_type: Bus type string, ``'i2c'`` (default) or ``'spi'``.
|
|
16
|
+
SPI writes prepend a command byte (R/W|MB|A5..A0) to each
|
|
17
|
+
transfer; multi-byte reads set MB=1 so the register pointer
|
|
18
|
+
auto-increments.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_BUS_I2C = 'i2c'
|
|
23
|
+
_BUS_SPI = 'spi'
|
|
24
|
+
|
|
25
|
+
# Default BW_RATE: 100 Hz output data rate, normal power.
|
|
26
|
+
_BW_RATE_DEFAULT = 0x0A
|
|
27
|
+
|
|
28
|
+
# Default DATA_FORMAT: FULL_RES=1, everything else 0 → ±2 g range.
|
|
29
|
+
_DATA_FORMAT_DEFAULT = 0x08
|
|
30
|
+
# Default POWER_CTL: Measure bit set; everything else cleared.
|
|
31
|
+
_POWER_CTL_DEFAULT = 0x08
|
|
32
|
+
|
|
33
|
+
# Conversion: full-resolution mode is constant 3.9 mg/LSB = 0.0039 g/LSB.
|
|
34
|
+
_FULL_RES_SCALE_G_PER_LSB = 3.9e-3
|
|
35
|
+
|
|
36
|
+
# 10-bit mode scales (g/LSB), indexed by range bits (00=±2, 01=±4, 10=±8, 11=±16).
|
|
37
|
+
_10BIT_SCALE_G_PER_LSB = (3.9e-3, 7.8e-3, 15.6e-3, 31.2e-3)
|
|
38
|
+
|
|
39
|
+
# Valid BW_RATE codes (Rate bits 3:0) and their actual output data rates.
|
|
40
|
+
_RATE_CODES = (
|
|
41
|
+
(0x0F, 3200.0),
|
|
42
|
+
(0x0E, 1600.0),
|
|
43
|
+
(0x0D, 800.0),
|
|
44
|
+
(0x0C, 400.0),
|
|
45
|
+
(0x0B, 200.0),
|
|
46
|
+
(0x0A, 100.0),
|
|
47
|
+
(0x09, 50.0),
|
|
48
|
+
(0x08, 25.0),
|
|
49
|
+
(0x07, 12.5),
|
|
50
|
+
(0x06, 6.25),
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _delay_ms(ms):
|
|
55
|
+
import time
|
|
56
|
+
if hasattr(time, 'sleep_ms'):
|
|
57
|
+
time.sleep_ms(ms)
|
|
58
|
+
else:
|
|
59
|
+
time.sleep(ms / 1000.0)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ADXL345Minimal:
|
|
63
|
+
"""ADXL345 3-axis accelerometer — minimal interface.
|
|
64
|
+
|
|
65
|
+
Reads X, Y, Z acceleration in *g* with sensible defaults; no
|
|
66
|
+
configuration is required beyond the connection.
|
|
67
|
+
|
|
68
|
+
Default configuration (baked in at construction):
|
|
69
|
+
- Full-resolution mode (3.9 mg/LSB at any range)
|
|
70
|
+
- ±2 g measurement range
|
|
71
|
+
- 100 Hz output data rate, normal power
|
|
72
|
+
- FIFO bypass, all interrupts disabled, no offsets
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
connection: Configured I²C or SPI connection pointing at the device.
|
|
76
|
+
bus_type: Bus type string, ``'i2c'`` (default) or ``'spi'``.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
# Register map (6-bit addresses, 0x00–0x39).
|
|
80
|
+
_REG_DEVID = 0x00
|
|
81
|
+
_REG_THRESH_TAP = 0x1D
|
|
82
|
+
_REG_OFSX = 0x1E
|
|
83
|
+
_REG_OFSY = 0x1F
|
|
84
|
+
_REG_OFSZ = 0x20
|
|
85
|
+
_REG_DUR = 0x21
|
|
86
|
+
_REG_LATENT = 0x22
|
|
87
|
+
_REG_WINDOW = 0x23
|
|
88
|
+
_REG_THRESH_ACT = 0x24
|
|
89
|
+
_REG_THRESH_INACT = 0x25
|
|
90
|
+
_REG_TIME_INACT = 0x26
|
|
91
|
+
_REG_ACT_INACT_CTL = 0x27
|
|
92
|
+
_REG_THRESH_FF = 0x28
|
|
93
|
+
_REG_TIME_FF = 0x29
|
|
94
|
+
_REG_TAP_AXES = 0x2A
|
|
95
|
+
_REG_ACT_TAP_STATUS = 0x2B
|
|
96
|
+
_REG_BW_RATE = 0x2C
|
|
97
|
+
_REG_POWER_CTL = 0x2D
|
|
98
|
+
_REG_INT_ENABLE = 0x2E
|
|
99
|
+
_REG_INT_MAP = 0x2F
|
|
100
|
+
_REG_INT_SOURCE = 0x30
|
|
101
|
+
_REG_DATA_FORMAT = 0x31
|
|
102
|
+
_REG_DATAX0 = 0x32
|
|
103
|
+
_REG_FIFO_CTL = 0x38
|
|
104
|
+
_REG_FIFO_STATUS = 0x39
|
|
105
|
+
|
|
106
|
+
# Fixed device ID; reading any other value indicates wrong device/wiring.
|
|
107
|
+
_DEVID_VALUE = 0xE5
|
|
108
|
+
|
|
109
|
+
def __init__(self, connection, bus_type=_BUS_I2C):
|
|
110
|
+
self._connection = connection
|
|
111
|
+
self._bus_type = bus_type
|
|
112
|
+
self._range_bits = 0 # 0..3: 0=±2, 1=±4, 2=±8, 3=±16 g
|
|
113
|
+
self._full_res = True
|
|
114
|
+
self._write_reg(self._REG_DATA_FORMAT, _DATA_FORMAT_DEFAULT)
|
|
115
|
+
self._write_reg(self._REG_BW_RATE, _BW_RATE_DEFAULT)
|
|
116
|
+
self._write_reg(self._REG_POWER_CTL, _POWER_CTL_DEFAULT)
|
|
117
|
+
devid = self._read_reg(self._REG_DEVID)
|
|
118
|
+
if devid != self._DEVID_VALUE:
|
|
119
|
+
raise ValueError('ADXL345 DEVID: expected 0x{:02X}, got 0x{:02X}'.format(
|
|
120
|
+
self._DEVID_VALUE, devid))
|
|
121
|
+
# Per datasheet: allow at least one ODR period before reading data.
|
|
122
|
+
_delay_ms(11)
|
|
123
|
+
|
|
124
|
+
def _cmd_byte(self, reg, read=False, multi=False):
|
|
125
|
+
"""Build the SPI command byte for the ADXL345.
|
|
126
|
+
|
|
127
|
+
I²C calls bypass this — the connection's address byte is independent.
|
|
128
|
+
"""
|
|
129
|
+
addr = (reg & 0x3F)
|
|
130
|
+
if multi:
|
|
131
|
+
addr |= 0x40
|
|
132
|
+
if read:
|
|
133
|
+
addr |= 0x80
|
|
134
|
+
return addr
|
|
135
|
+
|
|
136
|
+
def _write_reg(self, reg, value):
|
|
137
|
+
if self._bus_type == _BUS_SPI:
|
|
138
|
+
cmd = self._cmd_byte(reg, read=False, multi=False)
|
|
139
|
+
self._connection.write(bytes([cmd, value & 0xFF]))
|
|
140
|
+
else:
|
|
141
|
+
self._connection.write(bytes([reg & 0xFF, value & 0xFF]))
|
|
142
|
+
|
|
143
|
+
def _read_reg(self, reg):
|
|
144
|
+
if self._bus_type == _BUS_SPI:
|
|
145
|
+
cmd = self._cmd_byte(reg, read=True, multi=False)
|
|
146
|
+
return self._connection.write_read(bytes([cmd]), 1)[0]
|
|
147
|
+
return self._connection.write_read(bytes([reg & 0xFF]), 1)[0]
|
|
148
|
+
|
|
149
|
+
def _read_burst(self, reg, n):
|
|
150
|
+
"""Read n bytes starting at reg, auto-incrementing the register pointer."""
|
|
151
|
+
if self._bus_type == _BUS_SPI:
|
|
152
|
+
cmd = self._cmd_byte(reg, read=True, multi=(n > 1))
|
|
153
|
+
return self._connection.write_read(bytes([cmd]), n)
|
|
154
|
+
return self._connection.write_read(bytes([reg & 0xFF]), n)
|
|
155
|
+
|
|
156
|
+
def read(self):
|
|
157
|
+
"""Read 3-axis linear acceleration.
|
|
158
|
+
|
|
159
|
+
Reads all six data bytes (DATAX0..DATAZ1) in one burst so the X, Y,
|
|
160
|
+
Z samples are guaranteed to come from a single measurement.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
tuple: (x, y, z) acceleration in *g*.
|
|
164
|
+
"""
|
|
165
|
+
raw = self._read_burst(self._REG_DATAX0, 6)
|
|
166
|
+
# Each axis is little-endian, signed 16-bit two's complement.
|
|
167
|
+
rx = raw[0] | (raw[1] << 8)
|
|
168
|
+
ry = raw[2] | (raw[3] << 8)
|
|
169
|
+
rz = raw[4] | (raw[5] << 8)
|
|
170
|
+
if rx & 0x8000:
|
|
171
|
+
rx -= 0x10000
|
|
172
|
+
if ry & 0x8000:
|
|
173
|
+
ry -= 0x10000
|
|
174
|
+
if rz & 0x8000:
|
|
175
|
+
rz -= 0x10000
|
|
176
|
+
if self._full_res:
|
|
177
|
+
scale = _FULL_RES_SCALE_G_PER_LSB
|
|
178
|
+
else:
|
|
179
|
+
scale = _10BIT_SCALE_G_PER_LSB[self._range_bits & 0x03]
|
|
180
|
+
return (rx * scale, ry * scale, rz * scale)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class ADXL345Full(ADXL345Minimal):
|
|
184
|
+
"""ADXL345 full interface — extends ADXL345Minimal with configuration, FIFO,
|
|
185
|
+
tap / activity / inactivity / free-fall detection, and interrupt routing.
|
|
186
|
+
|
|
187
|
+
Adds:
|
|
188
|
+
- Range and data-rate selection (FULL_RES preserved).
|
|
189
|
+
- Low-power mode and self-test.
|
|
190
|
+
- Per-axis offset calibration (in *g*).
|
|
191
|
+
- Single/double-tap detection with configurable threshold, duration,
|
|
192
|
+
latency, and window.
|
|
193
|
+
- Activity and inactivity detection with ac/dc coupling.
|
|
194
|
+
- Free-fall detection.
|
|
195
|
+
- 32-level FIFO (bypass / FIFO / stream / trigger modes).
|
|
196
|
+
- Interrupt routing (INT1 / INT2) and source read.
|
|
197
|
+
- Sleep, auto-sleep, and link mode for power management.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
connection: Configured I²C or SPI connection pointing at the device.
|
|
201
|
+
bus_type: Bus type string, ``'i2c'`` (default) or ``'spi'``.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
# Interrupt source bits — match INT_ENABLE / INT_MAP / INT_SOURCE layout.
|
|
205
|
+
INT_DATA_READY = 0x80
|
|
206
|
+
INT_SINGLE_TAP = 0x40
|
|
207
|
+
INT_DOUBLE_TAP = 0x20
|
|
208
|
+
INT_ACTIVITY = 0x10
|
|
209
|
+
INT_INACTIVITY = 0x08
|
|
210
|
+
INT_FREE_FALL = 0x04
|
|
211
|
+
INT_WATERMARK = 0x02
|
|
212
|
+
INT_OVERRUN = 0x01
|
|
213
|
+
|
|
214
|
+
# FIFO mode values — match FIFO_CTL bits 7:6.
|
|
215
|
+
FIFO_BYPASS = 0x00
|
|
216
|
+
FIFO_FIFO = 0x40
|
|
217
|
+
FIFO_STREAM = 0x80
|
|
218
|
+
FIFO_TRIGGER = 0xC0
|
|
219
|
+
|
|
220
|
+
# Sleep-mode wakeup sample rates (POWER_CTL Wakeup bits 2:1).
|
|
221
|
+
WAKEUP_8_HZ = 0x00
|
|
222
|
+
WAKEUP_4_HZ = 0x02
|
|
223
|
+
WAKEUP_2_HZ = 0x04
|
|
224
|
+
WAKEUP_1_HZ = 0x06
|
|
225
|
+
|
|
226
|
+
def __init__(self, connection, bus_type=_BUS_I2C):
|
|
227
|
+
super().__init__(connection, bus_type)
|
|
228
|
+
|
|
229
|
+
def set_range(self, range_g):
|
|
230
|
+
"""Set the measurement range.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
range_g: One of 2, 4, 8, 16 (g). FULL_RES is preserved, so the
|
|
234
|
+
scale factor stays 3.9 mg/LSB regardless of range.
|
|
235
|
+
"""
|
|
236
|
+
code = {2: 0, 4: 1, 8: 2, 16: 3}.get(range_g)
|
|
237
|
+
if code is None:
|
|
238
|
+
raise ValueError('range_g must be one of 2, 4, 8, 16')
|
|
239
|
+
self._range_bits = code
|
|
240
|
+
df = self._read_reg(self._REG_DATA_FORMAT)
|
|
241
|
+
df = (df & ~0x03) | (code & 0x03)
|
|
242
|
+
if self._full_res:
|
|
243
|
+
df |= 0x08
|
|
244
|
+
self._write_reg(self._REG_DATA_FORMAT, df)
|
|
245
|
+
|
|
246
|
+
def set_data_rate(self, rate_hz):
|
|
247
|
+
"""Set the output data rate to the nearest supported value.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
rate_hz: Requested ODR in Hz; clamped to the nearest of
|
|
251
|
+
6.25, 12.5, 25, 50, 100, 200, 400, 800, 1600, 3200 Hz.
|
|
252
|
+
"""
|
|
253
|
+
best_code = _RATE_CODES[-1][0]
|
|
254
|
+
best_rate = _RATE_CODES[-1][1]
|
|
255
|
+
for code, actual in _RATE_CODES:
|
|
256
|
+
if abs(actual - rate_hz) < abs(best_rate - rate_hz):
|
|
257
|
+
best_code, best_rate = code, actual
|
|
258
|
+
bw = self._read_reg(self._REG_BW_RATE)
|
|
259
|
+
bw = (bw & ~0x0F) | (best_code & 0x0F)
|
|
260
|
+
self._write_reg(self._REG_BW_RATE, bw)
|
|
261
|
+
|
|
262
|
+
def set_low_power(self, enabled):
|
|
263
|
+
"""Enable or disable low-power mode (higher noise)."""
|
|
264
|
+
bw = self._read_reg(self._REG_BW_RATE)
|
|
265
|
+
if enabled:
|
|
266
|
+
bw |= 0x10
|
|
267
|
+
else:
|
|
268
|
+
bw &= ~0x10
|
|
269
|
+
self._write_reg(self._REG_BW_RATE, bw)
|
|
270
|
+
|
|
271
|
+
def set_offset(self, x, y, z):
|
|
272
|
+
"""Set per-axis offset in *g*.
|
|
273
|
+
|
|
274
|
+
Offsets are added to the output after filtering; the scale factor is
|
|
275
|
+
15.6 mg/LSB and the signed value is clamped to ±2 *g* (≈ ±128 LSB).
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
x: X offset in *g*.
|
|
279
|
+
y: Y offset in *g*.
|
|
280
|
+
z: Z offset in *g*.
|
|
281
|
+
"""
|
|
282
|
+
self._write_reg(self._REG_OFSX, self._encode_offset(x))
|
|
283
|
+
self._write_reg(self._REG_OFSY, self._encode_offset(y))
|
|
284
|
+
self._write_reg(self._REG_OFSZ, self._encode_offset(z))
|
|
285
|
+
|
|
286
|
+
@staticmethod
|
|
287
|
+
def _encode_offset(offset_g):
|
|
288
|
+
raw = int(round(offset_g / 15.6e-3))
|
|
289
|
+
if raw > 127:
|
|
290
|
+
raw = 127
|
|
291
|
+
if raw < -128:
|
|
292
|
+
raw = -128
|
|
293
|
+
return raw & 0xFF
|
|
294
|
+
|
|
295
|
+
def calibrate_offset(self, target_x=0.0, target_y=0.0, target_z=1.0, samples=128):
|
|
296
|
+
"""Measure and write per-axis offsets to bring readings onto the targets.
|
|
297
|
+
|
|
298
|
+
Averages ``samples`` reads and computes the residual (target − mean),
|
|
299
|
+
then writes the negative residual into the offset registers. Call
|
|
300
|
+
with the sensor stationary and oriented so the Z axis points up
|
|
301
|
+
(target_z=1 *g*) for a typical gravity-referenced calibration.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
target_x: Expected X reading during calibration (in *g*).
|
|
305
|
+
target_y: Expected Y reading during calibration (in *g*).
|
|
306
|
+
target_z: Expected Z reading during calibration (in *g*).
|
|
307
|
+
samples: Number of samples to average.
|
|
308
|
+
"""
|
|
309
|
+
sx = sy = sz = 0.0
|
|
310
|
+
for _ in range(samples):
|
|
311
|
+
x, y, z = self.read()
|
|
312
|
+
sx += x
|
|
313
|
+
sy += y
|
|
314
|
+
sz += z
|
|
315
|
+
_delay_ms(11)
|
|
316
|
+
sx /= samples
|
|
317
|
+
sy /= samples
|
|
318
|
+
sz /= samples
|
|
319
|
+
self.set_offset(target_x - sx, target_y - sy, target_z - sz)
|
|
320
|
+
|
|
321
|
+
def set_tap_detection(self, threshold_g, duration_ms, axes=0x07, suppress=False):
|
|
322
|
+
"""Configure single-tap detection and enable the SINGLE_TAP interrupt.
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
threshold_g: Tap acceleration threshold in *g* (62.5 mg/LSB).
|
|
326
|
+
duration_ms: Maximum tap duration in ms (625 µs/LSB).
|
|
327
|
+
axes: Bitmask of axes that participate (bit 2=X, 1=Y, 0=Z; default 0x07).
|
|
328
|
+
suppress: Suppress double-tap if acceleration persists between taps.
|
|
329
|
+
"""
|
|
330
|
+
self._write_reg(self._REG_THRESH_TAP, int(round(threshold_g / 62.5e-3)) & 0xFF)
|
|
331
|
+
self._write_reg(self._REG_DUR, int(round(duration_ms / 0.625)) & 0xFF)
|
|
332
|
+
tap_axes = (axes & 0x07) | (0x08 if suppress else 0x00)
|
|
333
|
+
self._write_reg(self._REG_TAP_AXES, tap_axes)
|
|
334
|
+
self._enable_interrupt(self.INT_SINGLE_TAP)
|
|
335
|
+
|
|
336
|
+
def set_double_tap(self, latency_ms, window_ms):
|
|
337
|
+
"""Configure double-tap latency and window; enables DOUBLE_TAP interrupt.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
latency_ms: Time after a tap when a second tap is expected (1.25 ms/LSB).
|
|
341
|
+
window_ms: Time window after latency during which the second tap must occur (1.25 ms/LSB).
|
|
342
|
+
"""
|
|
343
|
+
self._write_reg(self._REG_LATENT, int(round(latency_ms / 1.25)) & 0xFF)
|
|
344
|
+
self._write_reg(self._REG_WINDOW, int(round(window_ms / 1.25)) & 0xFF)
|
|
345
|
+
self._enable_interrupt(self.INT_DOUBLE_TAP)
|
|
346
|
+
|
|
347
|
+
def set_activity(self, threshold_g, axes=0x70, ac_coupled=True):
|
|
348
|
+
"""Configure activity detection.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
threshold_g: Activity threshold in *g* (62.5 mg/LSB).
|
|
352
|
+
axes: ACT_INACT_CTL activity bits (bits 7:4: ACT ac/dc, ACT_X
|
|
353
|
+
enable, ACT_Y enable, ACT_Z enable). Default 0x70 =
|
|
354
|
+
ac-coupled, all axes enabled.
|
|
355
|
+
ac_coupled: If True (default), use ac-coupled activity detection
|
|
356
|
+
(filters out the static gravity vector).
|
|
357
|
+
"""
|
|
358
|
+
self._write_reg(self._REG_THRESH_ACT, int(round(threshold_g / 62.5e-3)) & 0xFF)
|
|
359
|
+
aic = self._read_reg(self._REG_ACT_INACT_CTL)
|
|
360
|
+
aic &= ~0xF0 # clear ACT ac/dc and ACT axis enables
|
|
361
|
+
if ac_coupled:
|
|
362
|
+
aic |= 0x80
|
|
363
|
+
aic |= axes & 0x70
|
|
364
|
+
self._write_reg(self._REG_ACT_INACT_CTL, aic)
|
|
365
|
+
self._enable_interrupt(self.INT_ACTIVITY)
|
|
366
|
+
|
|
367
|
+
def set_inactivity(self, threshold_g, time_sec, axes=0x07, ac_coupled=False):
|
|
368
|
+
"""Configure inactivity detection.
|
|
369
|
+
|
|
370
|
+
Args:
|
|
371
|
+
threshold_g: Inactivity threshold in *g* (62.5 mg/LSB).
|
|
372
|
+
time_sec: Inactivity time in seconds (1 s/LSB).
|
|
373
|
+
axes: ACT_INACT_CTL inactivity bits (bits 3:0: INACT ac/dc,
|
|
374
|
+
INACT_X enable, INACT_Y enable, INACT_Z enable). Default 0x07 =
|
|
375
|
+
dc-coupled, all axes enabled.
|
|
376
|
+
ac_coupled: If True, use ac-coupled inactivity detection.
|
|
377
|
+
"""
|
|
378
|
+
self._write_reg(self._REG_THRESH_INACT, int(round(threshold_g / 62.5e-3)) & 0xFF)
|
|
379
|
+
self._write_reg(self._REG_TIME_INACT, int(round(time_sec)) & 0xFF)
|
|
380
|
+
aic = self._read_reg(self._REG_ACT_INACT_CTL)
|
|
381
|
+
aic &= ~0x0F # clear INACT ac/dc and INACT axis enables
|
|
382
|
+
if ac_coupled:
|
|
383
|
+
aic |= 0x08
|
|
384
|
+
aic |= axes & 0x07
|
|
385
|
+
self._write_reg(self._REG_ACT_INACT_CTL, aic)
|
|
386
|
+
self._enable_interrupt(self.INT_INACTIVITY)
|
|
387
|
+
|
|
388
|
+
def set_free_fall(self, threshold_g, time_ms):
|
|
389
|
+
"""Configure free-fall detection and enable the FREE_FALL interrupt.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
threshold_g: Free-fall threshold in *g* (62.5 mg/LSB).
|
|
393
|
+
Recommended range 0.3–0.6 *g* (codes 0x05–0x09).
|
|
394
|
+
time_ms: Free-fall time in ms (5 ms/LSB). Recommended 100–350 ms
|
|
395
|
+
(codes 0x14–0x46).
|
|
396
|
+
"""
|
|
397
|
+
self._write_reg(self._REG_THRESH_FF, int(round(threshold_g / 62.5e-3)) & 0xFF)
|
|
398
|
+
self._write_reg(self._REG_TIME_FF, int(round(time_ms / 5.0)) & 0xFF)
|
|
399
|
+
self._enable_interrupt(self.INT_FREE_FALL)
|
|
400
|
+
|
|
401
|
+
def set_interrupt(self, source, enabled, pin=1):
|
|
402
|
+
"""Enable or disable an interrupt source and route it to INT1 or INT2.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
source: One of the ``INT_*`` constants.
|
|
406
|
+
enabled: True to enable, False to disable.
|
|
407
|
+
pin: 1 (default) for INT1, 2 for INT2.
|
|
408
|
+
"""
|
|
409
|
+
ie = self._read_reg(self._REG_INT_ENABLE)
|
|
410
|
+
im = self._read_reg(self._REG_INT_MAP)
|
|
411
|
+
if enabled:
|
|
412
|
+
ie |= source
|
|
413
|
+
if pin == 2:
|
|
414
|
+
im |= source
|
|
415
|
+
else:
|
|
416
|
+
im &= ~source
|
|
417
|
+
else:
|
|
418
|
+
ie &= ~source
|
|
419
|
+
self._write_reg(self._REG_INT_ENABLE, ie)
|
|
420
|
+
self._write_reg(self._REG_INT_MAP, im)
|
|
421
|
+
|
|
422
|
+
def _enable_interrupt(self, source):
|
|
423
|
+
self.set_interrupt(source, True, pin=1)
|
|
424
|
+
|
|
425
|
+
def read_interrupt_source(self):
|
|
426
|
+
"""Read the INT_SOURCE register; clears latched interrupts.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
int: Bitmask of currently active interrupt sources (see ``INT_*``).
|
|
430
|
+
"""
|
|
431
|
+
return self._read_reg(self._REG_INT_SOURCE)
|
|
432
|
+
|
|
433
|
+
def set_fifo_mode(self, mode, samples=16):
|
|
434
|
+
"""Configure the FIFO.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
mode: One of ``FIFO_BYPASS`` (0x00), ``FIFO_FIFO`` (0x40),
|
|
438
|
+
``FIFO_STREAM`` (0x80), ``FIFO_TRIGGER`` (0xC0).
|
|
439
|
+
samples: Watermark level for FIFO / Stream mode, or number of
|
|
440
|
+
samples to retain before triggering for Trigger mode.
|
|
441
|
+
"""
|
|
442
|
+
fifo_ctl = (mode & 0xC0) | (samples & 0x1F)
|
|
443
|
+
self._write_reg(self._REG_FIFO_CTL, fifo_ctl)
|
|
444
|
+
|
|
445
|
+
def fifo_count(self):
|
|
446
|
+
"""Return the number of FIFO entries currently available (0–32)."""
|
|
447
|
+
status = self._read_reg(self._REG_FIFO_STATUS)
|
|
448
|
+
return status & 0x3F
|
|
449
|
+
|
|
450
|
+
def read_fifo(self):
|
|
451
|
+
"""Drain the FIFO, returning all available samples.
|
|
452
|
+
|
|
453
|
+
Each sample is the same 6-byte X/Y/Z burst as ``read()`` returns.
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
list[tuple]: List of (x, y, z) tuples in *g*. Empty if FIFO is empty.
|
|
457
|
+
"""
|
|
458
|
+
n = self.fifo_count()
|
|
459
|
+
if n == 0:
|
|
460
|
+
return []
|
|
461
|
+
out = []
|
|
462
|
+
for _ in range(n):
|
|
463
|
+
raw = self._read_burst(self._REG_DATAX0, 6)
|
|
464
|
+
rx = raw[0] | (raw[1] << 8)
|
|
465
|
+
ry = raw[2] | (raw[3] << 8)
|
|
466
|
+
rz = raw[4] | (raw[5] << 8)
|
|
467
|
+
if rx & 0x8000:
|
|
468
|
+
rx -= 0x10000
|
|
469
|
+
if ry & 0x8000:
|
|
470
|
+
ry -= 0x10000
|
|
471
|
+
if rz & 0x8000:
|
|
472
|
+
rz -= 0x10000
|
|
473
|
+
if self._full_res:
|
|
474
|
+
scale = _FULL_RES_SCALE_G_PER_LSB
|
|
475
|
+
else:
|
|
476
|
+
scale = _10BIT_SCALE_G_PER_LSB[self._range_bits & 0x03]
|
|
477
|
+
out.append((rx * scale, ry * scale, rz * scale))
|
|
478
|
+
return out
|
|
479
|
+
|
|
480
|
+
def set_sleep(self, enabled, wakeup_hz=8):
|
|
481
|
+
"""Enter or leave sleep mode.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
enabled: True to sleep, False to wake.
|
|
485
|
+
wakeup_hz: Sample rate during sleep (8 / 4 / 2 / 1 Hz).
|
|
486
|
+
"""
|
|
487
|
+
pwr = self._read_reg(self._REG_POWER_CTL)
|
|
488
|
+
if enabled:
|
|
489
|
+
wakeup_code = {8: self.WAKEUP_8_HZ, 4: self.WAKEUP_4_HZ,
|
|
490
|
+
2: self.WAKEUP_2_HZ, 1: self.WAKEUP_1_HZ}.get(wakeup_hz)
|
|
491
|
+
if wakeup_code is None:
|
|
492
|
+
raise ValueError('wakeup_hz must be 8, 4, 2, or 1')
|
|
493
|
+
pwr = (pwr & ~0x06) | wakeup_code | 0x08 # keep Measure bit set
|
|
494
|
+
pwr |= 0x04 # Sleep=1
|
|
495
|
+
else:
|
|
496
|
+
pwr &= ~0x04
|
|
497
|
+
self._write_reg(self._REG_POWER_CTL, pwr)
|
|
498
|
+
|
|
499
|
+
def set_link_mode(self, enabled):
|
|
500
|
+
"""Enable or disable the activity/inactivity serial-link mode."""
|
|
501
|
+
pwr = self._read_reg(self._REG_POWER_CTL)
|
|
502
|
+
if enabled:
|
|
503
|
+
pwr |= 0x40
|
|
504
|
+
else:
|
|
505
|
+
pwr &= ~0x40
|
|
506
|
+
self._write_reg(self._REG_POWER_CTL, pwr)
|
|
507
|
+
|
|
508
|
+
def set_auto_sleep(self, enabled):
|
|
509
|
+
"""Enable or disable auto-sleep on inactivity (requires Link=1)."""
|
|
510
|
+
pwr = self._read_reg(self._REG_POWER_CTL)
|
|
511
|
+
if enabled:
|
|
512
|
+
pwr |= 0x20
|
|
513
|
+
else:
|
|
514
|
+
pwr &= ~0x20
|
|
515
|
+
self._write_reg(self._REG_POWER_CTL, pwr)
|
|
516
|
+
|
|
517
|
+
def self_test(self, enabled):
|
|
518
|
+
"""Enable or disable the electrostatic self-test force on all axes."""
|
|
519
|
+
df = self._read_reg(self._REG_DATA_FORMAT)
|
|
520
|
+
if enabled:
|
|
521
|
+
df |= 0x80
|
|
522
|
+
else:
|
|
523
|
+
df &= ~0x80
|
|
524
|
+
self._write_reg(self._REG_DATA_FORMAT, df)
|