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.
Files changed (108) hide show
  1. periph/__init__.py +1 -0
  2. periph/chips/accelerometer/adxl345.py +524 -0
  3. periph/chips/accelerometer/adxl362.py +704 -0
  4. periph/chips/adc_dac/ad7705.py +476 -0
  5. periph/chips/adc_dac/ad7706.py +495 -0
  6. periph/chips/adc_dac/hx710a.py +187 -0
  7. periph/chips/adc_dac/hx710b.py +189 -0
  8. periph/chips/adc_dac/hx711.py +170 -0
  9. periph/chips/adc_dac/mcp4725.py +167 -0
  10. periph/chips/adc_dac/mcp4728.py +317 -0
  11. periph/chips/adc_dac/pcf8591.py +199 -0
  12. periph/chips/comms/mcp2515.py +651 -0
  13. periph/chips/comms/rda5807m.py +420 -0
  14. periph/chips/comms/rfm9x.py +594 -0
  15. periph/chips/display/pcf8576.py +223 -0
  16. periph/chips/environmental/aht21.py +150 -0
  17. periph/chips/environmental/bme280.py +384 -0
  18. periph/chips/environmental/bme680.py +501 -0
  19. periph/chips/gas/ens160.py +273 -0
  20. periph/chips/gnss/neo6.py +439 -0
  21. periph/chips/gyroscope/l3g4200d.py +448 -0
  22. periph/chips/gyroscope/l3gd20h.py +337 -0
  23. periph/chips/humidity/dht11.py +123 -0
  24. periph/chips/imu/mpu6050.py +251 -0
  25. periph/chips/imu/mpu9250.py +378 -0
  26. periph/chips/imu/mpu9255.py +434 -0
  27. periph/chips/io_expander/mcp23017.py +494 -0
  28. periph/chips/io_expander/pcf8574.py +401 -0
  29. periph/chips/io_expander/pcf8575.py +420 -0
  30. periph/chips/io_expander/tpic6b595.py +271 -0
  31. periph/chips/led/_color.py +23 -0
  32. periph/chips/led/_neopixel_rgb_base.py +175 -0
  33. periph/chips/led/_neopixel_rgbw_base.py +193 -0
  34. periph/chips/led/apa102.py +200 -0
  35. periph/chips/led/sk6812rgbw.py +52 -0
  36. periph/chips/led/ws2812b.py +46 -0
  37. periph/chips/led/ws2814.py +52 -0
  38. periph/chips/light/apds9960.py +496 -0
  39. periph/chips/light/apds_9930.py +364 -0
  40. periph/chips/magnetometer/as5600.py +290 -0
  41. periph/chips/magnetometer/hmc5883l.py +210 -0
  42. periph/chips/memory/_24aa02uid.py +175 -0
  43. periph/chips/motor/drv8830.py +236 -0
  44. periph/chips/other/mpr121.py +465 -0
  45. periph/chips/power/ade7953.py +1196 -0
  46. periph/chips/power/ina219.py +195 -0
  47. periph/chips/power/ina226.py +217 -0
  48. periph/chips/power/ina3221.py +359 -0
  49. periph/chips/pressure/bmp085.py +234 -0
  50. periph/chips/pressure/bmp180.py +234 -0
  51. periph/chips/pressure/bmp280.py +292 -0
  52. periph/chips/pressure/bmp384.py +471 -0
  53. periph/chips/pressure/bmp581.py +561 -0
  54. periph/chips/pressure/lps22df.py +394 -0
  55. periph/chips/pressure/lps28dfw.py +337 -0
  56. periph/chips/pressure/lps33hw.py +385 -0
  57. periph/chips/rfid/mfrc522.py +818 -0
  58. periph/chips/rtc/ds3231.py +522 -0
  59. periph/chips/rtc/pcf8523.py +549 -0
  60. periph/chips/temperature/mcp9808.py +453 -0
  61. periph/chips/temperature/tmp117.py +461 -0
  62. periph/chips/tof/_vl53_base.py +174 -0
  63. periph/chips/tof/vl53l0x.py +816 -0
  64. periph/chips/tof/vl53l1x.py +815 -0
  65. periph/connection/__init__.py +31 -0
  66. periph/connection/base.py +103 -0
  67. periph/connection/dhtxx_auto.py +52 -0
  68. periph/connection/dhtxx_circuitpython.py +138 -0
  69. periph/connection/dhtxx_linux.py +230 -0
  70. periph/connection/dhtxx_micropython.py +133 -0
  71. periph/connection/dhtxx_mock.py +34 -0
  72. periph/connection/hx711_auto.py +66 -0
  73. periph/connection/hx711_circuitpython.py +114 -0
  74. periph/connection/hx711_linux.py +121 -0
  75. periph/connection/hx711_micropython.py +113 -0
  76. periph/connection/hx711_mock.py +43 -0
  77. periph/connection/i2c_auto.py +54 -0
  78. periph/connection/i2c_circuitpython.py +84 -0
  79. periph/connection/i2c_linux.py +81 -0
  80. periph/connection/i2c_micropython.py +65 -0
  81. periph/connection/i2c_mock.py +57 -0
  82. periph/connection/input_pin.py +259 -0
  83. periph/connection/neo6_mock.py +36 -0
  84. periph/connection/neopixel_auto.py +75 -0
  85. periph/connection/neopixel_circuitpython.py +44 -0
  86. periph/connection/neopixel_linux.py +47 -0
  87. periph/connection/neopixel_micropython.py +38 -0
  88. periph/connection/output_pin.py +81 -0
  89. periph/connection/sipo_circuitpython.py +119 -0
  90. periph/connection/sipo_linux.py +149 -0
  91. periph/connection/sipo_micropython.py +104 -0
  92. periph/connection/smbus_auto.py +68 -0
  93. periph/connection/smbus_circuitpython.py +118 -0
  94. periph/connection/smbus_linux.py +119 -0
  95. periph/connection/smbus_micropython.py +100 -0
  96. periph/connection/spi_auto.py +79 -0
  97. periph/connection/spi_circuitpython.py +97 -0
  98. periph/connection/spi_linux.py +66 -0
  99. periph/connection/spi_micropython.py +64 -0
  100. periph/connection/spi_mock.py +96 -0
  101. periph/connection/uart_auto.py +76 -0
  102. periph/connection/uart_circuitpython.py +81 -0
  103. periph/connection/uart_linux.py +120 -0
  104. periph/connection/uart_micropython.py +81 -0
  105. periph-1.2.1.dist-info/METADATA +113 -0
  106. periph-1.2.1.dist-info/RECORD +108 -0
  107. periph-1.2.1.dist-info/WHEEL +5 -0
  108. periph-1.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,704 @@
1
+ """ADXL362 — 3-axis MEMS accelerometer (Analog Devices), SPI only.
2
+
3
+ The ADXL362 is an ultralow power 3-axis accelerometer with 12-bit output
4
+ resolution, a 512-sample FIFO, on-chip temperature sensor, autonomous
5
+ activity/inactivity (motion) detection with two independent interrupt pins,
6
+ and a wake-up mode that consumes ~270 nA. Unlike the ADXL345, it is
7
+ SPI-only; there is no I²C mode.
8
+
9
+ SPI command structure:
10
+ WRITE register 0x0A + addr + data...
11
+ READ register 0x0B + addr + data...
12
+ READ FIFO 0x0D + data...
13
+
14
+ Register addresses are 6 bits (0x00–0x3F). Burst (multi-byte) transfers
15
+ auto-increment the register pointer; the increment halts at the invalid
16
+ address 0x3F rather than wrapping. FIFO reads have no address byte and
17
+ may be burst-read continuously at the SPI clock rate.
18
+
19
+ Args:
20
+ connection: Configured SPI connection pointing at the device.
21
+ """
22
+
23
+
24
+ # SPI command bytes (per spec § Transport Configuration / SPI).
25
+ _CMD_WRITE_REG = 0x0A
26
+ _CMD_READ_REG = 0x0B
27
+ _CMD_READ_FIFO = 0x0D
28
+
29
+ # Soft-reset key (ASCII 'R').
30
+ _SOFT_RESET_KEY = 0x52
31
+
32
+ # Per-range sensitivity (g/LSB), typical values from the datasheet. The
33
+ # ±8 g value is intentionally non-linear (4.255 mg/LSB, not 4× the ±2 g
34
+ # scale of 1 mg/LSB) per the datasheet's per-range table.
35
+ _SENSITIVITY_G_PER_LSB = (0.001, 0.002, 0.004255)
36
+
37
+ # Temperature conversion: bias=350 LSB at 25 °C, sensitivity=0.065 °C/LSB.
38
+ _TEMP_BIAS_LSB = 350
39
+ _TEMP_BIAS_C = 25.0
40
+ _TEMP_SCALE_C = 0.065
41
+
42
+ # ODR codes (FILTER_CTL bits 2:0). '111' (400 Hz) collapses with 0x05–0x07.
43
+ _ODR_CODES = (
44
+ (0x00, 12.5),
45
+ (0x01, 25.0),
46
+ (0x02, 50.0),
47
+ (0x03, 100.0),
48
+ (0x04, 200.0),
49
+ (0x05, 400.0),
50
+ (0x06, 400.0),
51
+ (0x07, 400.0),
52
+ )
53
+
54
+ # Range codes (FILTER_CTL bits 7:6). '1X' both map to ±8 g.
55
+ _RANGE_CODES = (
56
+ (0x00, 2),
57
+ (0x40, 4),
58
+ (0x80, 8),
59
+ (0xC0, 8),
60
+ )
61
+
62
+
63
+ def _delay_ms(ms):
64
+ """Sleep ``ms`` milliseconds, portable across MicroPython and CPython."""
65
+ import time
66
+ if hasattr(time, 'sleep_ms'):
67
+ time.sleep_ms(int(ms))
68
+ else:
69
+ time.sleep(ms / 1000.0)
70
+
71
+
72
+ def _sign_extend_12(v):
73
+ """Sign-extend a 12-bit two's-complement value to a signed Python int."""
74
+ v &= 0x0FFF
75
+ if v & 0x0800:
76
+ v -= 0x1000
77
+ return v
78
+
79
+
80
+ def _sensitivity_for_range_bits(bits):
81
+ """Return the typical sensitivity (g/LSB) for the RANGE field (bits 7:6)."""
82
+ bits &= 0xC0
83
+ if bits == 0x00:
84
+ return _SENSITIVITY_G_PER_LSB[0]
85
+ if bits == 0x40:
86
+ return _SENSITIVITY_G_PER_LSB[1]
87
+ return _SENSITIVITY_G_PER_LSB[2]
88
+
89
+
90
+ class ADXL362Minimal:
91
+ """ADXL362 minimal interface — read X, Y, Z acceleration in *g*.
92
+
93
+ Performs the chip's full power-up sequence: verifies DEVID_AD=0xAD,
94
+ DEVID_MST=0x1D, and PARTID=0xF2 (the latter is 362 in octal); writes
95
+ the reset FILTER_CTL (RANGE=±2 g, HALF_BW=1, ODR=100 Hz); and switches
96
+ POWER_CTL into measurement mode.
97
+
98
+ Default configuration (baked in at construction):
99
+ - ±2 g measurement range
100
+ - 100 Hz output data rate, ODR/4 antialiasing bandwidth
101
+ - Normal noise mode (LOW_NOISE=00)
102
+ - Continuous measurement mode (POWER_CTL.MEASURE=10)
103
+ - FIFO disabled
104
+ - No interrupts mapped; INT1/INT2 high-impedance
105
+
106
+ Args:
107
+ connection: Configured SPI connection pointing at the device.
108
+ """
109
+
110
+ # Register map (6-bit addresses).
111
+ _REG_DEVID_AD = 0x00
112
+ _REG_DEVID_MST = 0x01
113
+ _REG_PARTID = 0x02
114
+ _REG_XDATA = 0x08
115
+ _REG_YDATA = 0x09
116
+ _REG_ZDATA = 0x0A
117
+ _REG_STATUS = 0x0B
118
+ _REG_FIFO_ENTRIES_L = 0x0C
119
+ _REG_FIFO_ENTRIES_H = 0x0D
120
+ _REG_XDATA_L = 0x0E
121
+ _REG_XDATA_H = 0x0F
122
+ _REG_YDATA_L = 0x10
123
+ _REG_YDATA_H = 0x11
124
+ _REG_ZDATA_L = 0x12
125
+ _REG_ZDATA_H = 0x13
126
+ _REG_TEMP_L = 0x14
127
+ _REG_TEMP_H = 0x15
128
+ _REG_SOFT_RESET = 0x1F
129
+ _REG_THRESH_ACT_L = 0x20
130
+ _REG_THRESH_ACT_H = 0x21
131
+ _REG_TIME_ACT = 0x22
132
+ _REG_THRESH_INACT_L = 0x23
133
+ _REG_THRESH_INACT_H = 0x24
134
+ _REG_TIME_INACT_L = 0x25
135
+ _REG_TIME_INACT_H = 0x26
136
+ _REG_ACT_INACT_CTL = 0x27
137
+ _REG_FIFO_CONTROL = 0x28
138
+ _REG_FIFO_SAMPLES = 0x29
139
+ _REG_INTMAP1 = 0x2A
140
+ _REG_INTMAP2 = 0x2B
141
+ _REG_FILTER_CTL = 0x2C
142
+ _REG_POWER_CTL = 0x2D
143
+ _REG_SELF_TEST = 0x2E
144
+
145
+ # Reset defaults.
146
+ _DEVID_AD_VALUE = 0xAD
147
+ _DEVID_MST_VALUE = 0x1D
148
+ _PARTID_VALUE = 0xF2
149
+
150
+ # FILTER_CTL reset value: RANGE=±2 g, HALF_BW=1, ODR=100 Hz.
151
+ _FILTER_CTL_DEFAULT = 0x13
152
+ # POWER_CTL measurement-mode value: MEASURE=10, all else 0.
153
+ _POWER_CTL_MEASURE = 0x02
154
+
155
+ # Status bits (1<<position).
156
+ _STATUS_DATA_READY = 0x01
157
+ _STATUS_FIFO_READY = 0x02
158
+ _STATUS_FIFO_WATERMARK = 0x04
159
+ _STATUS_FIFO_OVERRUN = 0x08
160
+ _STATUS_ACT = 0x10
161
+ _STATUS_INACT = 0x20
162
+ _STATUS_AWAKE = 0x40
163
+ _STATUS_ERR_USER_REGS = 0x80
164
+
165
+ def __init__(self, connection):
166
+ self._connection = connection
167
+ self._range_bits = 0x00 # ±2 g default
168
+ self._odr_hz = 100.0
169
+ self.init()
170
+
171
+ def init(self):
172
+ """Run the chip's full power-up sequence.
173
+
174
+ Verifies the triple device-ID read, writes FILTER_CTL and
175
+ POWER_CTL to known-good values, and waits the ODR turnaround
176
+ before returning so the caller can immediately read data.
177
+ """
178
+ # Power-up to standby turn-on time (datasheet: ≤5 ms typical at 100 Hz).
179
+ _delay_ms(5)
180
+
181
+ # Burst-read DEVID_AD, DEVID_MST, PARTID and verify the triple.
182
+ ids = self._read_burst(self._REG_DEVID_AD, 3)
183
+ if ids[0] != self._DEVID_AD_VALUE:
184
+ raise ValueError('ADXL362 DEVID_AD: expected 0x{:02X}, got 0x{:02X}'.format(
185
+ self._DEVID_AD_VALUE, ids[0]))
186
+ if ids[1] != self._DEVID_MST_VALUE:
187
+ raise ValueError('ADXL362 DEVID_MST: expected 0x{:02X}, got 0x{:02X}'.format(
188
+ self._DEVID_MST_VALUE, ids[1]))
189
+ if ids[2] != self._PARTID_VALUE:
190
+ raise ValueError('ADXL362 PARTID: expected 0x{:02X}, got 0x{:02X}'.format(
191
+ self._PARTID_VALUE, ids[2]))
192
+
193
+ self._write_reg(self._REG_FILTER_CTL, self._FILTER_CTL_DEFAULT)
194
+ self._write_reg(self._REG_POWER_CTL, self._POWER_CTL_MEASURE)
195
+
196
+ # Measurement-mode-instruction-to-valid-data: 4/ODR (40 ms at 100 Hz).
197
+ _delay_ms(40)
198
+
199
+ def _read_burst(self, reg, n):
200
+ """Read n bytes starting at ``reg`` with auto-incrementing pointer."""
201
+ cmd = bytes([_CMD_READ_REG, reg & 0x3F])
202
+ return self._connection.write_read(cmd, n)
203
+
204
+ def _read_fifo(self, n):
205
+ """Read n bytes from the FIFO (no address byte)."""
206
+ return self._connection.write_read(bytes([_CMD_READ_FIFO]), n)
207
+
208
+ def _write_reg(self, reg, value):
209
+ """Write a single register."""
210
+ self._connection.write(bytes([_CMD_WRITE_REG, reg & 0x3F, value & 0xFF]))
211
+
212
+ def _read_reg(self, reg):
213
+ """Read a single register."""
214
+ return self._read_burst(reg, 1)[0]
215
+
216
+ def _read_fifo_entries(self):
217
+ """Return the 10-bit FIFO entry count (0–512)."""
218
+ lo = self._read_reg(self._REG_FIFO_ENTRIES_L)
219
+ hi = self._read_reg(self._REG_FIFO_ENTRIES_H)
220
+ return (lo | ((hi & 0x03) << 8))
221
+
222
+ def _threshold_raw(self, threshold_g):
223
+ """Convert an acceleration threshold in *g* to a 10-bit raw value."""
224
+ sens = _sensitivity_for_range_bits(self._range_bits)
225
+ raw = int(round(threshold_g / sens))
226
+ if raw < 0:
227
+ raw = 0
228
+ if raw > 0x3FF:
229
+ raw = 0x3FF
230
+ return raw
231
+
232
+ def _time_inact_raw(self, samples):
233
+ """Convert a 16-bit sample count to two FIFO_INACT register bytes."""
234
+ if samples < 0:
235
+ samples = 0
236
+ if samples > 0xFFFF:
237
+ samples = 0xFFFF
238
+ return samples & 0xFF, (samples >> 8) & 0xFF
239
+
240
+ def read(self):
241
+ """Read 3-axis linear acceleration.
242
+
243
+ Burst-reads the 12-bit XDATA_L/H, YDATA_L/H, ZDATA_L/H register
244
+ sextet so the three samples are guaranteed to come from a single
245
+ measurement.
246
+
247
+ Returns:
248
+ tuple: (x, y, z) acceleration in *g*.
249
+ """
250
+ raw = self._read_burst(self._REG_XDATA_L, 6)
251
+ rx = _sign_extend_12((raw[1] & 0x0F) << 8 | raw[0])
252
+ ry = _sign_extend_12((raw[3] & 0x0F) << 8 | raw[2])
253
+ rz = _sign_extend_12((raw[5] & 0x0F) << 8 | raw[4])
254
+ sens = _sensitivity_for_range_bits(self._range_bits)
255
+ return (rx * sens, ry * sens, rz * sens)
256
+
257
+
258
+ class ADXL362Full(ADXL362Minimal):
259
+ """ADXL362 full interface — extends ADXL362Minimal with the full chip API.
260
+
261
+ Adds:
262
+ - Device-ID triple read (DEVID_AD/DEVID_MST/PARTID/REVID).
263
+ - Soft-reset (writes 0x52 to SOFT_RESET).
264
+ - Range, ODR, antialiasing, and noise-mode configuration.
265
+ - Wake-up mode (270 nA) and external clock / external sample sync.
266
+ - 8-bit low-resolution read (XDATA/YDATA/ZDATA).
267
+ - On-chip temperature sensor.
268
+ - STATUS accessors (awake, data_ready, FIFO flags).
269
+ - 512-sample FIFO (disabled / oldest-saved / stream / triggered,
270
+ optional temperature interleaving, 9-bit watermark).
271
+ - Activity / inactivity thresholds and timers (referenced or
272
+ absolute, default/linked/loop modes).
273
+ - Per-pin (INT1/INT2) source mapping and active-high/active-low
274
+ polarity.
275
+ - Self-test.
276
+
277
+ Args:
278
+ connection: Configured SPI connection pointing at the device.
279
+ """
280
+
281
+ # Interrupt source constants (used by set_interrupt).
282
+ SOURCE_DATA_READY = 0
283
+ SOURCE_FIFO_READY = 1
284
+ SOURCE_FIFO_WATERMARK = 2
285
+ SOURCE_FIFO_OVERRUN = 3
286
+ SOURCE_ACT = 4
287
+ SOURCE_INACT = 5
288
+ SOURCE_AWAKE = 6
289
+
290
+ # Noise mode constants (POWER_CTL.LOW_NOISE[5:4]).
291
+ NOISE_NORMAL = 0
292
+ NOISE_LOW = 1
293
+ NOISE_ULTRALOW = 2
294
+
295
+ # Link/loop mode constants (ACT_INACT_CTL.LINKLOOP[5:4]).
296
+ LINKLOOP_DEFAULT = 0
297
+ LINKLOOP_LINKED = 1
298
+ LINKLOOP_LOOP = 3
299
+
300
+ # FIFO mode constants (FIFO_CONTROL.FIFO_MODE[1:0]).
301
+ FIFO_DISABLED = 0
302
+ FIFO_OLDEST_SAVED = 1
303
+ FIFO_STREAM = 2
304
+ FIFO_TRIGGERED = 3
305
+
306
+ # FIFO entry-axis constants (top 2 bits of each 16-bit FIFO entry).
307
+ AXIS_X = 0
308
+ AXIS_Y = 1
309
+ AXIS_Z = 2
310
+ AXIS_TEMP = 3
311
+
312
+ # INTMAPx bit layout (mirrors the chip register, single source per bit).
313
+ _INTMAP_DATA_READY = 0x01
314
+ _INTMAP_FIFO_READY = 0x02
315
+ _INTMAP_FIFO_WATERMARK = 0x04
316
+ _INTMAP_FIFO_OVERRUN = 0x08
317
+ _INTMAP_ACT = 0x10
318
+ _INTMAP_INACT = 0x20
319
+ _INTMAP_AWAKE = 0x40
320
+ _INTMAP_INT_LOW = 0x80
321
+
322
+ # ACT_INACT_CTL bit layout.
323
+ _AIC_ACT_EN = 0x01
324
+ _AIC_ACT_REF = 0x02
325
+ _AIC_INACT_EN = 0x04
326
+ _AIC_INACT_REF = 0x08
327
+ _AIC_LINKLOOP_MASK = 0x30
328
+
329
+ # FILTER_CTL bit layout.
330
+ _FILTER_ODR_MASK = 0x07
331
+ _FILTER_EXT_SAMPLE = 0x08
332
+ _FILTER_HALF_BW = 0x10
333
+ _FILTER_RANGE_MASK = 0xC0
334
+
335
+ # POWER_CTL bit layout.
336
+ _POWER_MEASURE_MASK = 0x03
337
+ _POWER_AUTOSLEEP = 0x04
338
+ _POWER_WAKEUP = 0x08
339
+ _POWER_LOW_NOISE_MASK = 0x30
340
+ _POWER_EXT_CLK = 0x40
341
+
342
+ def __init__(self, connection):
343
+ super().__init__(connection)
344
+
345
+ def device_id(self):
346
+ """Return raw device-ID bytes.
347
+
348
+ Returns:
349
+ tuple: (DEVID_AD, DEVID_MST, PARTID, REVID).
350
+ """
351
+ ids = self._read_burst(self._REG_DEVID_AD, 4)
352
+ return (ids[0], ids[1], ids[2], ids[3])
353
+
354
+ def soft_reset(self):
355
+ """Soft-reset the chip.
356
+
357
+ Writes 0x52 (ASCII 'R') to SOFT_RESET and waits the documented
358
+ 0.5 ms latency. All registers return to reset defaults; caller
359
+ must re-run init() before continuing.
360
+ """
361
+ self._write_reg(self._REG_SOFT_RESET, _SOFT_RESET_KEY)
362
+ _delay_ms(1) # ≥0.5 ms per datasheet
363
+ # Reset clears cached state but doesn't reset cached Python state.
364
+ self._range_bits = 0x00
365
+ self._odr_hz = 100.0
366
+
367
+ def set_range(self, range_g):
368
+ """Set the measurement range.
369
+
370
+ Args:
371
+ range_g: 2, 4, or 8 (g).
372
+ """
373
+ code = {2: 0x00, 4: 0x40, 8: 0x80}.get(range_g)
374
+ if code is None:
375
+ raise ValueError('range_g must be one of 2, 4, 8')
376
+ f = self._read_reg(self._REG_FILTER_CTL)
377
+ f = (f & ~self._FILTER_RANGE_MASK) | (code & self._FILTER_RANGE_MASK)
378
+ self._write_reg(self._REG_FILTER_CTL, f)
379
+ self._range_bits = code
380
+ # Spec datasheet says no separate delay is required, but the ODR
381
+ # turnaround is observed in continuous mode; wait 1/ODR for safety.
382
+ if self._odr_hz > 0:
383
+ _delay_ms(1000.0 / self._odr_hz + 1)
384
+
385
+ def set_odr(self, odr_hz):
386
+ """Set the output data rate to the nearest supported value (12.5–400 Hz).
387
+
388
+ Args:
389
+ odr_hz: Requested ODR in Hz; clamped to the nearest of
390
+ 12.5, 25, 50, 100, 200, or 400 Hz.
391
+ """
392
+ best_code = _ODR_CODES[0][0]
393
+ best_rate = _ODR_CODES[0][1]
394
+ best_diff = abs(best_rate - odr_hz)
395
+ for code, actual in _ODR_CODES:
396
+ d = abs(actual - odr_hz)
397
+ if d < best_diff:
398
+ best_code, best_rate, best_diff = code, actual, d
399
+ f = self._read_reg(self._REG_FILTER_CTL)
400
+ f = (f & ~self._FILTER_ODR_MASK) | (best_code & self._FILTER_ODR_MASK)
401
+ self._write_reg(self._REG_FILTER_CTL, f)
402
+ self._odr_hz = best_rate
403
+
404
+ def set_half_bandwidth(self, enabled):
405
+ """Set FILTER_CTL.HALF_BW (antialiasing bandwidth = ODR/4 or ODR/2)."""
406
+ f = self._read_reg(self._REG_FILTER_CTL)
407
+ if enabled:
408
+ f |= self._FILTER_HALF_BW
409
+ else:
410
+ f &= ~self._FILTER_HALF_BW
411
+ self._write_reg(self._REG_FILTER_CTL, f)
412
+
413
+ def set_noise_mode(self, mode):
414
+ """Set POWER_CTL.LOW_NOISE (0=normal, 1=low, 2=ultralow noise)."""
415
+ if mode not in (self.NOISE_NORMAL, self.NOISE_LOW, self.NOISE_ULTRALOW):
416
+ raise ValueError('mode must be NOISE_NORMAL, NOISE_LOW, or NOISE_ULTRALOW')
417
+ p = self._read_reg(self._REG_POWER_CTL)
418
+ p = (p & ~self._POWER_LOW_NOISE_MASK) | ((mode << 4) & self._POWER_LOW_NOISE_MASK)
419
+ self._write_reg(self._REG_POWER_CTL, p)
420
+
421
+ def set_wakeup_mode(self, enabled):
422
+ """Enter wake-up mode (270 nA); ignored unless loop mode is engaged."""
423
+ p = self._read_reg(self._REG_POWER_CTL)
424
+ if enabled:
425
+ p |= self._POWER_WAKEUP
426
+ else:
427
+ p &= ~self._POWER_WAKEUP
428
+ self._write_reg(self._REG_POWER_CTL, p)
429
+
430
+ def set_autosleep(self, enabled):
431
+ """Set POWER_CTL.AUTOSLEEP; effective only in linked/loop mode."""
432
+ p = self._read_reg(self._REG_POWER_CTL)
433
+ if enabled:
434
+ p |= self._POWER_AUTOSLEEP
435
+ else:
436
+ p &= ~self._POWER_AUTOSLEEP
437
+ self._write_reg(self._REG_POWER_CTL, p)
438
+
439
+ def set_external_clock(self, enabled):
440
+ """Set POWER_CTL.EXT_CLK; INT1 is repurposed as clock input."""
441
+ p = self._read_reg(self._REG_POWER_CTL)
442
+ if enabled:
443
+ p |= self._POWER_EXT_CLK
444
+ else:
445
+ p &= ~self._POWER_EXT_CLK
446
+ self._write_reg(self._REG_POWER_CTL, p)
447
+
448
+ def set_external_sample_trigger(self, enabled):
449
+ """Set FILTER_CTL.EXT_SAMPLE; INT2 is repurposed as sync trigger input."""
450
+ f = self._read_reg(self._REG_FILTER_CTL)
451
+ if enabled:
452
+ f |= self._FILTER_EXT_SAMPLE
453
+ else:
454
+ f &= ~self._FILTER_EXT_SAMPLE
455
+ self._write_reg(self._REG_FILTER_CTL, f)
456
+
457
+ def read_8bit(self):
458
+ """Read 3-axis acceleration using the 8-bit XDATA/YDATA/ZDATA registers.
459
+
460
+ Returns:
461
+ tuple: (x, y, z) acceleration in *g*, ~16-LSB resolution.
462
+ """
463
+ raw = self._read_burst(self._REG_XDATA, 3)
464
+ # XDATA/YDATA/ZDATA are signed 8-bit two's complement; an LSB here
465
+ # represents 16 codes of the 12-bit scale.
466
+ def s8(v):
467
+ v &= 0xFF
468
+ return v - 0x100 if v & 0x80 else v
469
+ sens = _sensitivity_for_range_bits(self._range_bits) * 16
470
+ return (s8(raw[0]) * sens, s8(raw[1]) * sens, s8(raw[2]) * sens)
471
+
472
+ def temperature(self):
473
+ """Read the on-chip temperature sensor.
474
+
475
+ Uses the typical bias (350 LSB @ 25 °C) and sensitivity
476
+ (0.065 °C/LSB) from the datasheet; not part-calibrated.
477
+
478
+ Returns:
479
+ float: Temperature in °C.
480
+ """
481
+ raw = self._read_burst(self._REG_TEMP_L, 2)
482
+ raw12 = _sign_extend_12((raw[1] & 0x0F) << 8 | raw[0])
483
+ return _TEMP_BIAS_C + (raw12 - _TEMP_BIAS_LSB) * _TEMP_SCALE_C
484
+
485
+ def status(self):
486
+ """Return the raw STATUS register byte."""
487
+ return self._read_reg(self._REG_STATUS)
488
+
489
+ def awake(self):
490
+ """Return STATUS.AWAKE."""
491
+ return bool(self.status() & self._STATUS_AWAKE)
492
+
493
+ def data_ready(self):
494
+ """Return STATUS.DATA_READY (cleared by data-register or FIFO read)."""
495
+ return bool(self.status() & self._STATUS_DATA_READY)
496
+
497
+ def fifo_entries(self):
498
+ """Return the 10-bit FIFO entry count (0–512)."""
499
+ return self._read_fifo_entries()
500
+
501
+ def configure_fifo(self, mode, store_temp=False, watermark=128):
502
+ """Configure the FIFO mode, optional temperature storage, and watermark.
503
+
504
+ Args:
505
+ mode: One of FIFO_DISABLED (0), FIFO_OLDEST_SAVED (1),
506
+ FIFO_STREAM (2), FIFO_TRIGGERED (3).
507
+ store_temp: True to interleave temperature samples in the FIFO.
508
+ watermark: 9-bit watermark value (0–511).
509
+ """
510
+ if mode not in (self.FIFO_DISABLED, self.FIFO_OLDEST_SAVED,
511
+ self.FIFO_STREAM, self.FIFO_TRIGGERED):
512
+ raise ValueError('mode must be 0/1/2/3 (DISABLED/OLDEST_SAVED/STREAM/TRIGGERED)')
513
+ if watermark < 0 or watermark > 0x1FF:
514
+ raise ValueError('watermark must be 0–511')
515
+ fc = (mode & 0x03) | ((watermark >> 8) << 3) | (0x04 if store_temp else 0x00)
516
+ self._write_reg(self._REG_FIFO_CONTROL, fc)
517
+ self._write_reg(self._REG_FIFO_SAMPLES, watermark & 0xFF)
518
+
519
+ def read_fifo(self):
520
+ """Read all available FIFO entries.
521
+
522
+ Each 16-bit entry's top two bits encode the axis (0=X, 1=Y,
523
+ 2=Z, 3=temperature); the low 12 bits are signed axis/temperature
524
+ data, scaled with the current range.
525
+
526
+ Returns:
527
+ list[tuple[int, float]]: ``[(axis, value), ...]`` where axis
528
+ is one of AXIS_X/AXIS_Y/AXIS_Z/AXIS_TEMP and value is in
529
+ *g* (axes 0–2) or °C (axis 3).
530
+ """
531
+ n = self._read_fifo_entries()
532
+ if n == 0:
533
+ return []
534
+ # Always read an even byte count — each FIFO entry is 16 bits.
535
+ raw = self._read_fifo(n * 2)
536
+ sens = _sensitivity_for_range_bits(self._range_bits)
537
+ out = []
538
+ for i in range(n):
539
+ lo = raw[2 * i]
540
+ hi = raw[2 * i + 1]
541
+ raw16 = (hi << 8) | lo
542
+ axis = (raw16 >> 14) & 0x03
543
+ raw12 = _sign_extend_12(raw16 & 0x0FFF)
544
+ if axis == self.AXIS_TEMP:
545
+ value = _TEMP_BIAS_C + (raw12 - _TEMP_BIAS_LSB) * _TEMP_SCALE_C
546
+ else:
547
+ value = raw12 * sens
548
+ out.append((axis, value))
549
+ return out
550
+
551
+ def set_activity_threshold(self, threshold_g, referenced=False):
552
+ """Set the activity threshold in *g*.
553
+
554
+ Args:
555
+ threshold_g: Acceleration magnitude that triggers activity
556
+ detection, in *g*. Clamped to 10-bit range relative to
557
+ the current measurement range.
558
+ referenced: True for referenced (relative-to-orientation-at-
559
+ engagement) detection; False for absolute.
560
+ """
561
+ raw = self._threshold_raw(threshold_g)
562
+ self._write_reg(self._REG_THRESH_ACT_L, raw & 0xFF)
563
+ self._write_reg(self._REG_THRESH_ACT_H, (raw >> 8) & 0x03)
564
+ aic = self._read_reg(self._REG_ACT_INACT_CTL)
565
+ if referenced:
566
+ aic |= self._AIC_ACT_REF
567
+ else:
568
+ aic &= ~self._AIC_ACT_REF
569
+ self._write_reg(self._REG_ACT_INACT_CTL, aic)
570
+
571
+ def set_activity_time(self, samples):
572
+ """Set the activity-time filter (0–255 samples).
573
+
574
+ Args:
575
+ samples: Number of consecutive over-threshold samples
576
+ required to assert activity (0–255). Ignored in wake-up
577
+ mode, which is single-sample.
578
+ """
579
+ if samples < 0 or samples > 0xFF:
580
+ raise ValueError('samples must be 0–255')
581
+ self._write_reg(self._REG_TIME_ACT, samples & 0xFF)
582
+
583
+ def set_inactivity_threshold(self, threshold_g, referenced=False):
584
+ """Set the inactivity threshold in *g*.
585
+
586
+ Args:
587
+ threshold_g: Acceleration magnitude below which inactivity
588
+ is asserted, in *g*. Clamped to 10-bit range.
589
+ referenced: True for referenced (relative-to-orientation-at-
590
+ engagement) detection; False for absolute.
591
+ """
592
+ raw = self._threshold_raw(threshold_g)
593
+ self._write_reg(self._REG_THRESH_INACT_L, raw & 0xFF)
594
+ self._write_reg(self._REG_THRESH_INACT_H, (raw >> 8) & 0x03)
595
+ aic = self._read_reg(self._REG_ACT_INACT_CTL)
596
+ if referenced:
597
+ aic |= self._AIC_INACT_REF
598
+ else:
599
+ aic &= ~self._AIC_INACT_REF
600
+ self._write_reg(self._REG_ACT_INACT_CTL, aic)
601
+
602
+ def set_inactivity_time(self, samples):
603
+ """Set the inactivity-time filter (0–65535 samples).
604
+
605
+ Args:
606
+ samples: Number of consecutive under-threshold samples
607
+ required to assert inactivity (0–65535).
608
+ """
609
+ if samples < 0 or samples > 0xFFFF:
610
+ raise ValueError('samples must be 0–65535')
611
+ lo, hi = self._time_inact_raw(samples)
612
+ self._write_reg(self._REG_TIME_INACT_L, lo)
613
+ self._write_reg(self._REG_TIME_INACT_H, hi)
614
+
615
+ def enable_activity_detection(self, enabled):
616
+ """Set ACT_INACT_CTL.ACT_EN."""
617
+ aic = self._read_reg(self._REG_ACT_INACT_CTL)
618
+ if enabled:
619
+ aic |= self._AIC_ACT_EN
620
+ else:
621
+ aic &= ~self._AIC_ACT_EN
622
+ self._write_reg(self._REG_ACT_INACT_CTL, aic)
623
+
624
+ def enable_inactivity_detection(self, enabled):
625
+ """Set ACT_INACT_CTL.INACT_EN."""
626
+ aic = self._read_reg(self._REG_ACT_INACT_CTL)
627
+ if enabled:
628
+ aic |= self._AIC_INACT_EN
629
+ else:
630
+ aic &= ~self._AIC_INACT_EN
631
+ self._write_reg(self._REG_ACT_INACT_CTL, aic)
632
+
633
+ def set_link_loop_mode(self, mode):
634
+ """Set ACT_INACT_CTL.LINKLOOP[5:4].
635
+
636
+ Args:
637
+ mode: LINKLOOP_DEFAULT (0), LINKLOOP_LINKED (1), or
638
+ LINKLOOP_LOOP (3).
639
+ """
640
+ if mode not in (self.LINKLOOP_DEFAULT, self.LINKLOOP_LINKED, self.LINKLOOP_LOOP):
641
+ raise ValueError('mode must be LINKLOOP_DEFAULT/LINKED/LOOP')
642
+ aic = self._read_reg(self._REG_ACT_INACT_CTL)
643
+ aic = (aic & ~self._AIC_LINKLOOP_MASK) | ((mode << 4) & self._AIC_LINKLOOP_MASK)
644
+ self._write_reg(self._REG_ACT_INACT_CTL, aic)
645
+
646
+ def _intmap_bit(self, source):
647
+ return {
648
+ self.SOURCE_DATA_READY: self._INTMAP_DATA_READY,
649
+ self.SOURCE_FIFO_READY: self._INTMAP_FIFO_READY,
650
+ self.SOURCE_FIFO_WATERMARK: self._INTMAP_FIFO_WATERMARK,
651
+ self.SOURCE_FIFO_OVERRUN: self._INTMAP_FIFO_OVERRUN,
652
+ self.SOURCE_ACT: self._INTMAP_ACT,
653
+ self.SOURCE_INACT: self._INTMAP_INACT,
654
+ self.SOURCE_AWAKE: self._INTMAP_AWAKE,
655
+ }[source]
656
+
657
+ def set_interrupt(self, pin, source, enabled):
658
+ """Set or clear one interrupt source on the named INT pin.
659
+
660
+ Args:
661
+ pin: 1 or 2.
662
+ source: One of SOURCE_DATA_READY/SOURCE_FIFO_READY/...
663
+ enabled: True to map, False to clear.
664
+ """
665
+ if pin not in (1, 2):
666
+ raise ValueError('pin must be 1 or 2')
667
+ reg = self._REG_INTMAP1 if pin == 1 else self._REG_INTMAP2
668
+ cur = self._read_reg(reg)
669
+ bit = self._intmap_bit(source)
670
+ if enabled:
671
+ cur |= bit
672
+ else:
673
+ cur &= ~bit
674
+ self._write_reg(reg, cur)
675
+
676
+ def set_interrupt_polarity(self, pin, active_low):
677
+ """Set the active-low polarity for one INT pin.
678
+
679
+ Args:
680
+ pin: 1 or 2.
681
+ active_low: True for active-low, False for active-high.
682
+ """
683
+ if pin not in (1, 2):
684
+ raise ValueError('pin must be 1 or 2')
685
+ reg = self._REG_INTMAP1 if pin == 1 else self._REG_INTMAP2
686
+ cur = self._read_reg(reg)
687
+ if active_low:
688
+ cur |= self._INTMAP_INT_LOW
689
+ else:
690
+ cur &= ~self._INTMAP_INT_LOW
691
+ self._write_reg(reg, cur)
692
+
693
+ def self_test(self, enabled):
694
+ """Enable or disable the electrostatic self-test force on all axes."""
695
+ st = self._read_reg(self._REG_SELF_TEST)
696
+ if enabled:
697
+ st |= 0x01
698
+ else:
699
+ st &= ~0x01
700
+ self._write_reg(self._REG_SELF_TEST, st)
701
+ if enabled:
702
+ # Spec: wait 4/ODR after asserting or deasserting SELF_TEST.ST
703
+ if self._odr_hz > 0:
704
+ _delay_ms(4000.0 / self._odr_hz + 1)