adafruit-circuitpython-apds9999 1.0.0__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.
- adafruit_apds9999.py +825 -0
- adafruit_circuitpython_apds9999-1.0.0.dist-info/METADATA +159 -0
- adafruit_circuitpython_apds9999-1.0.0.dist-info/RECORD +6 -0
- adafruit_circuitpython_apds9999-1.0.0.dist-info/WHEEL +5 -0
- adafruit_circuitpython_apds9999-1.0.0.dist-info/licenses/LICENSE +21 -0
- adafruit_circuitpython_apds9999-1.0.0.dist-info/top_level.txt +1 -0
adafruit_apds9999.py
ADDED
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2026 Tim Cocks for Adafruit Industries
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
"""
|
|
5
|
+
`adafruit_apds9999`
|
|
6
|
+
================================================================================
|
|
7
|
+
|
|
8
|
+
CircuitPython driver for Broadcom APDS-9999 Light + RGB + Proximity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
* Author(s): Tim Cocks
|
|
12
|
+
|
|
13
|
+
Implementation Notes
|
|
14
|
+
--------------------
|
|
15
|
+
|
|
16
|
+
**Hardware:**
|
|
17
|
+
|
|
18
|
+
* `APDS9999 <https://www.adafruit.com/product/6461>`_
|
|
19
|
+
|
|
20
|
+
**Software and Dependencies:**
|
|
21
|
+
|
|
22
|
+
* Adafruit CircuitPython firmware for the supported boards:
|
|
23
|
+
https://circuitpython.org/downloads
|
|
24
|
+
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
|
|
25
|
+
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__version__ = "1.0.0"
|
|
29
|
+
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_APDS9999.git"
|
|
30
|
+
|
|
31
|
+
import time
|
|
32
|
+
|
|
33
|
+
from adafruit_bus_device import i2c_device
|
|
34
|
+
from adafruit_register.i2c_bit import RWBit
|
|
35
|
+
from adafruit_register.i2c_bits import ROBits, RWBits
|
|
36
|
+
from adafruit_register.i2c_struct import UnaryStruct
|
|
37
|
+
from adafruit_simplemath import map_range
|
|
38
|
+
from micropython import const
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
from typing import Optional, Tuple
|
|
42
|
+
|
|
43
|
+
import busio
|
|
44
|
+
except ImportError:
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
# Default I2C address
|
|
48
|
+
_APDS9999_DEFAULT_ADDR = const(0x52)
|
|
49
|
+
|
|
50
|
+
# Register addresses
|
|
51
|
+
_APDS9999_REG_MAIN_CTRL = const(0x00) # Main control register
|
|
52
|
+
_APDS9999_REG_PS_VCSEL = const(0x01) # PS VCSEL control register
|
|
53
|
+
_APDS9999_REG_PS_PULSES = const(0x02) # PS pulses control register
|
|
54
|
+
_APDS9999_REG_PS_MEAS_RATE = const(0x03) # PS measurement rate
|
|
55
|
+
_APDS9999_REG_LS_MEAS_RATE = const(0x04) # LS measurement rate
|
|
56
|
+
_APDS9999_REG_LS_GAIN = const(0x05) # LS gain control
|
|
57
|
+
_APDS9999_REG_PART_ID = const(0x06) # Part ID register
|
|
58
|
+
_APDS9999_REG_MAIN_STATUS = const(0x07) # Main status register
|
|
59
|
+
_APDS9999_REG_PS_DATA_0 = const(0x08) # PS data low byte
|
|
60
|
+
_APDS9999_REG_PS_DATA_1 = const(0x09) # PS data high byte
|
|
61
|
+
_APDS9999_REG_LS_DATA_IR_0 = const(0x0A) # IR data low byte
|
|
62
|
+
_APDS9999_REG_LS_DATA_IR_1 = const(0x0B) # IR data middle byte
|
|
63
|
+
_APDS9999_REG_LS_DATA_IR_2 = const(0x0C) # IR data high byte
|
|
64
|
+
_APDS9999_REG_LS_DATA_GREEN_0 = const(0x0D) # Green data low byte
|
|
65
|
+
_APDS9999_REG_LS_DATA_GREEN_1 = const(0x0E) # Green data middle byte
|
|
66
|
+
_APDS9999_REG_LS_DATA_GREEN_2 = const(0x0F) # Green data high byte
|
|
67
|
+
_APDS9999_REG_LS_DATA_BLUE_0 = const(0x10) # Blue data low byte
|
|
68
|
+
_APDS9999_REG_LS_DATA_BLUE_1 = const(0x11) # Blue data middle byte
|
|
69
|
+
_APDS9999_REG_LS_DATA_BLUE_2 = const(0x12) # Blue data high byte
|
|
70
|
+
_APDS9999_REG_LS_DATA_RED_0 = const(0x13) # Red data low byte
|
|
71
|
+
_APDS9999_REG_LS_DATA_RED_1 = const(0x14) # Red data middle byte
|
|
72
|
+
_APDS9999_REG_LS_DATA_RED_2 = const(0x15) # Red data high byte
|
|
73
|
+
_APDS9999_REG_INT_CFG = const(0x19) # Interrupt configuration
|
|
74
|
+
_APDS9999_REG_INT_PST = const(0x1A) # Interrupt persistence
|
|
75
|
+
_APDS9999_REG_PS_THRES_UP_0 = const(0x1B) # PS upper threshold low byte
|
|
76
|
+
_APDS9999_REG_PS_THRES_UP_1 = const(0x1C) # PS upper threshold high byte
|
|
77
|
+
_APDS9999_REG_PS_THRES_LOW_0 = const(0x1D) # PS lower threshold low byte
|
|
78
|
+
_APDS9999_REG_PS_THRES_LOW_1 = const(0x1E) # PS lower threshold high byte
|
|
79
|
+
_APDS9999_REG_PS_CAN_0 = const(0x1F) # PS cancellation level low byte
|
|
80
|
+
_APDS9999_REG_PS_CAN_1 = const(0x20) # PS cancellation level high byte
|
|
81
|
+
_APDS9999_REG_LS_THRES_UP_0 = const(0x21) # LS upper threshold low byte
|
|
82
|
+
_APDS9999_REG_LS_THRES_UP_1 = const(0x22) # LS upper threshold middle byte
|
|
83
|
+
_APDS9999_REG_LS_THRES_UP_2 = const(0x23) # LS upper threshold high byte
|
|
84
|
+
_APDS9999_REG_LS_THRES_LOW_0 = const(0x24) # LS lower threshold low byte
|
|
85
|
+
_APDS9999_REG_LS_THRES_LOW_1 = const(0x25) # LS lower threshold middle byte
|
|
86
|
+
_APDS9999_REG_LS_THRES_LOW_2 = const(0x26) # LS lower threshold high byte
|
|
87
|
+
_APDS9999_REG_LS_THRES_VAR = const(0x27) # LS variance threshold
|
|
88
|
+
|
|
89
|
+
# Expected value of the full PART_ID register (0x06) upper nibble 0xC = part,
|
|
90
|
+
# lower nibble 0x2 = revision.
|
|
91
|
+
_APDS9999_PART_ID = const(0xC2)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class CV:
|
|
95
|
+
"""Constant value class helper for enums."""
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def is_valid(cls, value: int) -> bool:
|
|
99
|
+
"""Validate that a given value is a member."""
|
|
100
|
+
IGNORE_LIST = [cls.__module__, cls.__name__]
|
|
101
|
+
if value in cls.__dict__.values() and value not in IGNORE_LIST:
|
|
102
|
+
return True
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def get_name(cls, value: int) -> str:
|
|
107
|
+
"""Get the name for a given value."""
|
|
108
|
+
name_dict = {}
|
|
109
|
+
for _key, _value in cls.__dict__.items():
|
|
110
|
+
name_dict[_value] = _key
|
|
111
|
+
return name_dict[value]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class LightResolution(CV):
|
|
115
|
+
"""Light sensor ADC resolution settings for the LS_MEAS_RATE register (0x04), bits 6:4.
|
|
116
|
+
|
|
117
|
+
Higher resolution increases accuracy but also increases conversion time.
|
|
118
|
+
|
|
119
|
+
+--------------------------------------+-------------+------------------+
|
|
120
|
+
| Setting | Resolution | Conversion time |
|
|
121
|
+
+======================================+=============+==================+
|
|
122
|
+
| :py:const:`LightResolution.RES_20BIT`| 20-bit | 400 ms |
|
|
123
|
+
+--------------------------------------+-------------+------------------+
|
|
124
|
+
| :py:const:`LightResolution.RES_19BIT`| 19-bit | 200 ms |
|
|
125
|
+
+--------------------------------------+-------------+------------------+
|
|
126
|
+
| :py:const:`LightResolution.RES_18BIT`| 18-bit | 100 ms |
|
|
127
|
+
+--------------------------------------+-------------+------------------+
|
|
128
|
+
| :py:const:`LightResolution.RES_17BIT`| 17-bit | 50 ms |
|
|
129
|
+
+--------------------------------------+-------------+------------------+
|
|
130
|
+
| :py:const:`LightResolution.RES_16BIT`| 16-bit | 25 ms |
|
|
131
|
+
+--------------------------------------+-------------+------------------+
|
|
132
|
+
| :py:const:`LightResolution.RES_13BIT`| 13-bit | 3.125 ms |
|
|
133
|
+
+--------------------------------------+-------------+------------------+
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
RES_20BIT = 0x00 # 20-bit resolution (400 ms conversion)
|
|
137
|
+
RES_19BIT = 0x01 # 19-bit resolution (200 ms conversion)
|
|
138
|
+
RES_18BIT = 0x02 # 18-bit resolution (100 ms conversion)
|
|
139
|
+
RES_17BIT = 0x03 # 17-bit resolution (50 ms conversion)
|
|
140
|
+
RES_16BIT = 0x04 # 16-bit resolution (25 ms conversion)
|
|
141
|
+
RES_13BIT = 0x05 # 13-bit resolution (3.125 ms conversion)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class LightVariance(CV):
|
|
145
|
+
"""Light sensor variance threshold settings for the LS_THRES_VAR register (0x27), bits 2:0.
|
|
146
|
+
|
|
147
|
+
Sets the count variance required to trigger a light interrupt when
|
|
148
|
+
:attr:`light_variance_mode` is enabled.
|
|
149
|
+
|
|
150
|
+
+------------------------------------+-----------------+
|
|
151
|
+
| Setting | Variance count |
|
|
152
|
+
+====================================+=================+
|
|
153
|
+
| :py:const:`LightVariance.VAR_8` | 8 |
|
|
154
|
+
+------------------------------------+-----------------+
|
|
155
|
+
| :py:const:`LightVariance.VAR_16` | 16 |
|
|
156
|
+
+------------------------------------+-----------------+
|
|
157
|
+
| :py:const:`LightVariance.VAR_32` | 32 |
|
|
158
|
+
+------------------------------------+-----------------+
|
|
159
|
+
| :py:const:`LightVariance.VAR_64` | 64 |
|
|
160
|
+
+------------------------------------+-----------------+
|
|
161
|
+
| :py:const:`LightVariance.VAR_128` | 128 |
|
|
162
|
+
+------------------------------------+-----------------+
|
|
163
|
+
| :py:const:`LightVariance.VAR_256` | 256 |
|
|
164
|
+
+------------------------------------+-----------------+
|
|
165
|
+
| :py:const:`LightVariance.VAR_512` | 512 |
|
|
166
|
+
+------------------------------------+-----------------+
|
|
167
|
+
| :py:const:`LightVariance.VAR_1024` | 1024 |
|
|
168
|
+
+------------------------------------+-----------------+
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
VAR_8 = 0x00 # 8 count variance
|
|
172
|
+
VAR_16 = 0x01 # 16 count variance
|
|
173
|
+
VAR_32 = 0x02 # 32 count variance
|
|
174
|
+
VAR_64 = 0x03 # 64 count variance
|
|
175
|
+
VAR_128 = 0x04 # 128 count variance
|
|
176
|
+
VAR_256 = 0x05 # 256 count variance
|
|
177
|
+
VAR_512 = 0x06 # 512 count variance
|
|
178
|
+
VAR_1024 = 0x07 # 1024 count variance
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class LightInterruptChannel(CV):
|
|
182
|
+
"""Light sensor interrupt channel settings for the INT_CFG register (0x19), bits 5:4.
|
|
183
|
+
|
|
184
|
+
Selects which light sensor channel is compared against the interrupt thresholds.
|
|
185
|
+
|
|
186
|
+
+-----------------------------------------------+-----------------+
|
|
187
|
+
| Setting | Channel |
|
|
188
|
+
+===============================================+=================+
|
|
189
|
+
| :py:const:`LightInterruptChannel.IR` | IR channel |
|
|
190
|
+
+-----------------------------------------------+-----------------+
|
|
191
|
+
| :py:const:`LightInterruptChannel.GREEN` | Green channel |
|
|
192
|
+
+-----------------------------------------------+-----------------+
|
|
193
|
+
| :py:const:`LightInterruptChannel.RED` | Red channel |
|
|
194
|
+
+-----------------------------------------------+-----------------+
|
|
195
|
+
| :py:const:`LightInterruptChannel.BLUE` | Blue channel |
|
|
196
|
+
+-----------------------------------------------+-----------------+
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
IR = 0x00 # IR channel
|
|
200
|
+
GREEN = 0x01 # Green channel
|
|
201
|
+
RED = 0x02 # Red channel
|
|
202
|
+
BLUE = 0x03 # Blue channel
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class LedCurrent(CV):
|
|
206
|
+
"""LED drive current settings for the PS_VCSEL register (0x01), bits 2:0.
|
|
207
|
+
|
|
208
|
+
Controls the IR LED current used during proximity measurements.
|
|
209
|
+
|
|
210
|
+
+----------------------------------+------------+
|
|
211
|
+
| Setting | Current |
|
|
212
|
+
+==================================+============+
|
|
213
|
+
| :py:const:`LedCurrent.MA_10` | 10 mA |
|
|
214
|
+
+----------------------------------+------------+
|
|
215
|
+
| :py:const:`LedCurrent.MA_25` | 25 mA |
|
|
216
|
+
+----------------------------------+------------+
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
MA_10 = 0x02 # 10 mA
|
|
220
|
+
MA_25 = 0x03 # 25 mA
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class LedFrequency(CV):
|
|
224
|
+
"""LED pulse frequency settings for the PS_VCSEL register (0x01), bits 6:4.
|
|
225
|
+
|
|
226
|
+
Controls the modulation frequency of the IR LED pulses used during
|
|
227
|
+
proximity measurements.
|
|
228
|
+
|
|
229
|
+
+----------------------------------+------------+
|
|
230
|
+
| Setting | Frequency |
|
|
231
|
+
+==================================+============+
|
|
232
|
+
| :py:const:`LedFrequency.KHZ_60` | 60 kHz |
|
|
233
|
+
+----------------------------------+------------+
|
|
234
|
+
| :py:const:`LedFrequency.KHZ_70` | 70 kHz |
|
|
235
|
+
+----------------------------------+------------+
|
|
236
|
+
| :py:const:`LedFrequency.KHZ_80` | 80 kHz |
|
|
237
|
+
+----------------------------------+------------+
|
|
238
|
+
| :py:const:`LedFrequency.KHZ_90` | 90 kHz |
|
|
239
|
+
+----------------------------------+------------+
|
|
240
|
+
| :py:const:`LedFrequency.KHZ_100` | 100 kHz |
|
|
241
|
+
+----------------------------------+------------+
|
|
242
|
+
"""
|
|
243
|
+
|
|
244
|
+
KHZ_60 = 0x03 # 60 kHz
|
|
245
|
+
KHZ_70 = 0x04 # 70 kHz
|
|
246
|
+
KHZ_80 = 0x05 # 80 kHz
|
|
247
|
+
KHZ_90 = 0x06 # 90 kHz
|
|
248
|
+
KHZ_100 = 0x07 # 100 kHz
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class LightMeasurementRate(CV):
|
|
252
|
+
"""Light sensor measurement rate settings for the LS_MEAS_RATE register (0x04), bits 2:0.
|
|
253
|
+
|
|
254
|
+
Controls how frequently the light sensor takes a reading. The measurement
|
|
255
|
+
rate should be set equal to or slower than the resolution's conversion time
|
|
256
|
+
to avoid reading stale data.
|
|
257
|
+
|
|
258
|
+
+-----------------------------------------------+-------------------+
|
|
259
|
+
| Setting | Rate |
|
|
260
|
+
+===============================================+===================+
|
|
261
|
+
| :py:const:`LightMeasurementRate.RATE_25MS` | 25 ms |
|
|
262
|
+
+-----------------------------------------------+-------------------+
|
|
263
|
+
| :py:const:`LightMeasurementRate.RATE_50MS` | 50 ms |
|
|
264
|
+
+-----------------------------------------------+-------------------+
|
|
265
|
+
| :py:const:`LightMeasurementRate.RATE_100MS` | 100 ms (default) |
|
|
266
|
+
+-----------------------------------------------+-------------------+
|
|
267
|
+
| :py:const:`LightMeasurementRate.RATE_200MS` | 200 ms |
|
|
268
|
+
+-----------------------------------------------+-------------------+
|
|
269
|
+
| :py:const:`LightMeasurementRate.RATE_500MS` | 500 ms |
|
|
270
|
+
+-----------------------------------------------+-------------------+
|
|
271
|
+
| :py:const:`LightMeasurementRate.RATE_1000MS` | 1000 ms |
|
|
272
|
+
+-----------------------------------------------+-------------------+
|
|
273
|
+
| :py:const:`LightMeasurementRate.RATE_2000MS` | 2000 ms |
|
|
274
|
+
+-----------------------------------------------+-------------------+
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
RATE_25MS = 0x00 # 25 ms measurement rate
|
|
278
|
+
RATE_50MS = 0x01 # 50 ms measurement rate
|
|
279
|
+
RATE_100MS = 0x02 # 100 ms measurement rate (default)
|
|
280
|
+
RATE_200MS = 0x03 # 200 ms measurement rate
|
|
281
|
+
RATE_500MS = 0x04 # 500 ms measurement rate
|
|
282
|
+
RATE_1000MS = 0x05 # 1000 ms measurement rate
|
|
283
|
+
RATE_2000MS = 0x06 # 2000 ms measurement rate
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class ProximityResolution(CV):
|
|
287
|
+
"""Proximity sensor ADC resolution settings for the PS_MEAS_RATE register (0x03), bits 4:3.
|
|
288
|
+
|
|
289
|
+
Higher resolution increases accuracy at the cost of a longer conversion time.
|
|
290
|
+
|
|
291
|
+
+------------------------------------------+-------------+
|
|
292
|
+
| Setting | Resolution |
|
|
293
|
+
+==========================================+=============+
|
|
294
|
+
| :py:const:`ProximityResolution.RES_8BIT` | 8-bit |
|
|
295
|
+
+------------------------------------------+-------------+
|
|
296
|
+
| :py:const:`ProximityResolution.RES_9BIT` | 9-bit |
|
|
297
|
+
+------------------------------------------+-------------+
|
|
298
|
+
| :py:const:`ProximityResolution.RES_10BIT`| 10-bit |
|
|
299
|
+
+------------------------------------------+-------------+
|
|
300
|
+
| :py:const:`ProximityResolution.RES_11BIT`| 11-bit |
|
|
301
|
+
+------------------------------------------+-------------+
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
RES_8BIT = 0x00 # 8-bit resolution (hardware default)
|
|
305
|
+
RES_9BIT = 0x01 # 9-bit resolution
|
|
306
|
+
RES_10BIT = 0x02 # 10-bit resolution
|
|
307
|
+
RES_11BIT = 0x03 # 11-bit resolution
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class LightGain(CV):
|
|
311
|
+
"""Light sensor gain settings for the LS_GAIN register (0x05).
|
|
312
|
+
|
|
313
|
+
Controls the analogue gain applied to the light sensor channels.
|
|
314
|
+
|
|
315
|
+
+--------------------------------+----------+
|
|
316
|
+
| Setting | Gain |
|
|
317
|
+
+================================+==========+
|
|
318
|
+
| :py:const:`LightGain.GAIN_1X` | 1x gain |
|
|
319
|
+
+--------------------------------+----------+
|
|
320
|
+
| :py:const:`LightGain.GAIN_3X` | 3x gain |
|
|
321
|
+
+--------------------------------+----------+
|
|
322
|
+
| :py:const:`LightGain.GAIN_6X` | 6x gain |
|
|
323
|
+
+--------------------------------+----------+
|
|
324
|
+
| :py:const:`LightGain.GAIN_9X` | 9x gain |
|
|
325
|
+
+--------------------------------+----------+
|
|
326
|
+
| :py:const:`LightGain.GAIN_18X` | 18x gain |
|
|
327
|
+
+--------------------------------+----------+
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
GAIN_1X = 0x00 # 1x gain
|
|
331
|
+
GAIN_3X = 0x01 # 3x gain
|
|
332
|
+
GAIN_6X = 0x02 # 6x gain
|
|
333
|
+
GAIN_9X = 0x03 # 9x gain
|
|
334
|
+
GAIN_18X = 0x04 # 18x gain
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class APDS9999:
|
|
338
|
+
"""CircuitPython driver for the Broadcom APDS-9999 Digital Proximity and
|
|
339
|
+
RGB Color Sensor.
|
|
340
|
+
|
|
341
|
+
:param ~busio.I2C i2c_bus: The I2C bus the device is connected to.
|
|
342
|
+
:param int address: The I2C device address. Defaults to :const:`0x52`.
|
|
343
|
+
|
|
344
|
+
**Quickstart: Importing and using the device**
|
|
345
|
+
|
|
346
|
+
Here is an example of using the :class:`APDS9999`.
|
|
347
|
+
First you will need to import the libraries to use the sensor
|
|
348
|
+
|
|
349
|
+
.. code-block:: python
|
|
350
|
+
|
|
351
|
+
import board
|
|
352
|
+
from adafruit_apds9999 import APDS9999
|
|
353
|
+
|
|
354
|
+
Once this is done you can define your `board.I2C` object and define your sensor object
|
|
355
|
+
|
|
356
|
+
.. code-block:: python
|
|
357
|
+
|
|
358
|
+
i2c = board.I2C() # uses board.SCL and board.SDA
|
|
359
|
+
sensor = APDS9999(i2c)
|
|
360
|
+
|
|
361
|
+
Now you have access to the sensor data
|
|
362
|
+
|
|
363
|
+
.. code-block:: python
|
|
364
|
+
|
|
365
|
+
r, g, b, ir = sensor.color_data
|
|
366
|
+
proximity = sensor.proximity
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
# PART_ID register (0x06) full 8-bit read-only value.
|
|
370
|
+
# Upper nibble (bits 7:4) = part number (0xC for APDS-9999).
|
|
371
|
+
# Lower nibble (bits 3:0) = revision ID.
|
|
372
|
+
_part_id = ROBits(8, _APDS9999_REG_PART_ID, 0)
|
|
373
|
+
|
|
374
|
+
# MAIN_CTRL register (0x00), bit 4 software reset
|
|
375
|
+
_reset = RWBit(_APDS9999_REG_MAIN_CTRL, 4)
|
|
376
|
+
|
|
377
|
+
# MAIN_CTRL register (0x00), bit 1 enables the light (ALS/RGB) sensor.
|
|
378
|
+
light_sensor_enabled = RWBit(_APDS9999_REG_MAIN_CTRL, 1)
|
|
379
|
+
|
|
380
|
+
# MAIN_CTRL register (0x00), bit 0 enables the proximity sensor.
|
|
381
|
+
proximity_sensor_enabled = RWBit(_APDS9999_REG_MAIN_CTRL, 0)
|
|
382
|
+
|
|
383
|
+
# MAIN_CTRL register (0x00), bit 2 selects RGB mode (True) vs ALS mode (False).
|
|
384
|
+
rgb_mode = RWBit(_APDS9999_REG_MAIN_CTRL, 2)
|
|
385
|
+
|
|
386
|
+
# MAIN_CTRL register (0x00), bit 6 sleep after proximity interrupt.
|
|
387
|
+
proximity_sleep_after_interrupt = RWBit(_APDS9999_REG_MAIN_CTRL, 6)
|
|
388
|
+
|
|
389
|
+
# MAIN_CTRL register (0x00), bit 5 sleep after light interrupt.
|
|
390
|
+
light_sleep_after_interrupt = RWBit(_APDS9999_REG_MAIN_CTRL, 5)
|
|
391
|
+
|
|
392
|
+
# LS_GAIN register (0x05), bits 2:0 analogue gain for the light sensor channels.
|
|
393
|
+
_light_gain = RWBits(3, _APDS9999_REG_LS_GAIN, 0)
|
|
394
|
+
|
|
395
|
+
# LS_MEAS_RATE register (0x04), bits 6:4 ADC resolution of the light sensor.
|
|
396
|
+
_light_resolution = RWBits(3, _APDS9999_REG_LS_MEAS_RATE, 4)
|
|
397
|
+
|
|
398
|
+
# LS_MEAS_RATE register (0x04), bits 2:0 how frequently the light sensor measures.
|
|
399
|
+
_light_measurement_rate = RWBits(3, _APDS9999_REG_LS_MEAS_RATE, 0)
|
|
400
|
+
|
|
401
|
+
# PS_MEAS_RATE register (0x03), bits 4:3 ADC resolution of the proximity sensor.
|
|
402
|
+
_proximity_resolution = RWBits(2, _APDS9999_REG_PS_MEAS_RATE, 3)
|
|
403
|
+
|
|
404
|
+
# PS_DATA_0/1 registers (0x08-0x09) raw 16-bit proximity data word, little-endian.
|
|
405
|
+
# Bits 10:0 are the 11-bit proximity count; bit 11 is the overflow flag.
|
|
406
|
+
_proximity_data = UnaryStruct(_APDS9999_REG_PS_DATA_0, "<H")
|
|
407
|
+
|
|
408
|
+
# INT_CFG register (0x19), bit 0 enables the proximity sensor interrupt.
|
|
409
|
+
proximity_interrupt_enabled = RWBit(_APDS9999_REG_INT_CFG, 0)
|
|
410
|
+
|
|
411
|
+
# INT_CFG register (0x19), bit 1 proximity logic mode
|
|
412
|
+
# (True = inside window, False = outside window).
|
|
413
|
+
proximity_logic_mode = RWBit(_APDS9999_REG_INT_CFG, 1)
|
|
414
|
+
|
|
415
|
+
# INT_CFG register (0x19), bit 2 enables the light sensor interrupt.
|
|
416
|
+
light_interrupt_enabled = RWBit(_APDS9999_REG_INT_CFG, 2)
|
|
417
|
+
|
|
418
|
+
# INT_CFG register (0x19), bit 3 selects variance mode
|
|
419
|
+
# (True) vs threshold mode (False) for the light interrupt.
|
|
420
|
+
light_variance_mode = RWBit(_APDS9999_REG_INT_CFG, 3)
|
|
421
|
+
|
|
422
|
+
# INT_CFG register (0x19), bits 5:4 selects which light channel
|
|
423
|
+
# is compared against the interrupt thresholds.
|
|
424
|
+
_light_interrupt_channel = RWBits(2, _APDS9999_REG_INT_CFG, 4)
|
|
425
|
+
|
|
426
|
+
# INT_PST register (0x1A), bits 3:0 number of consecutive out-of-threshold proximity
|
|
427
|
+
# readings required before the interrupt flag is asserted (0-15).
|
|
428
|
+
_proximity_persistence = RWBits(4, _APDS9999_REG_INT_PST, 0)
|
|
429
|
+
|
|
430
|
+
# INT_PST register (0x1A), bits 7:4 number of consecutive out-of-threshold light
|
|
431
|
+
# readings required before the interrupt flag is asserted (0-15).
|
|
432
|
+
_light_persistence = RWBits(4, _APDS9999_REG_INT_PST, 4)
|
|
433
|
+
|
|
434
|
+
# PS_THRES_UP_0/1 registers (0x1B-0x1C) 11-bit upper proximity interrupt threshold.
|
|
435
|
+
proximity_threshold_high = RWBits(11, _APDS9999_REG_PS_THRES_UP_0, 0, register_width=2)
|
|
436
|
+
|
|
437
|
+
# PS_THRES_LOW_0/1 registers (0x1D-0x1E) 11-bit lower proximity interrupt threshold.
|
|
438
|
+
proximity_threshold_low = RWBits(11, _APDS9999_REG_PS_THRES_LOW_0, 0, register_width=2)
|
|
439
|
+
|
|
440
|
+
# LS_THRES_UP_0/1/2 registers (0x21-0x23) 20-bit upper light sensor interrupt threshold.
|
|
441
|
+
light_threshold_high = RWBits(20, _APDS9999_REG_LS_THRES_UP_0, 0, register_width=3)
|
|
442
|
+
|
|
443
|
+
# LS_THRES_LOW_0/1/2 registers (0x24-0x26) 20-bit lower light sensor interrupt threshold.
|
|
444
|
+
light_threshold_low = RWBits(20, _APDS9999_REG_LS_THRES_LOW_0, 0, register_width=3)
|
|
445
|
+
|
|
446
|
+
# LS_THRES_VAR register (0x27), bits 2:0 variance threshold for light interrupt.
|
|
447
|
+
_light_variance = RWBits(3, _APDS9999_REG_LS_THRES_VAR, 0)
|
|
448
|
+
|
|
449
|
+
# PS_CAN_0/1 registers (0x1F-0x20), bits 10:0 11-bit digital proximity cancellation
|
|
450
|
+
# level. Bits 7:3 of PS_CAN_1 (analog cancellation) are preserved by the RWBits
|
|
451
|
+
# read-modify-write.
|
|
452
|
+
proximity_cancellation = RWBits(11, _APDS9999_REG_PS_CAN_0, 0, register_width=2)
|
|
453
|
+
|
|
454
|
+
# PS_CAN_1 register (0x20), bits 7:3 5-bit analog proximity cancellation level.
|
|
455
|
+
# Bits 2:0 of PS_CAN_1 (digital cancellation high bits) are preserved by RWBits.
|
|
456
|
+
proximity_analog_cancellation = RWBits(5, _APDS9999_REG_PS_CAN_1, 3)
|
|
457
|
+
|
|
458
|
+
# MAIN_STATUS register (0x07), bits 5:0 all status flags in one read.
|
|
459
|
+
# Reading this register clears all status bits.
|
|
460
|
+
_main_status = ROBits(6, _APDS9999_REG_MAIN_STATUS, 0)
|
|
461
|
+
|
|
462
|
+
# PS_VCSEL register (0x01), bits 2:0 IR LED drive current.
|
|
463
|
+
_led_current = RWBits(3, _APDS9999_REG_PS_VCSEL, 0)
|
|
464
|
+
|
|
465
|
+
# PS_VCSEL register (0x01), bits 6:4 IR LED pulse modulation frequency.
|
|
466
|
+
_led_frequency = RWBits(3, _APDS9999_REG_PS_VCSEL, 4)
|
|
467
|
+
|
|
468
|
+
# PS_PULSES register (0x02) number of LED pulses emitted per proximity measurement.
|
|
469
|
+
led_pulses = UnaryStruct(_APDS9999_REG_PS_PULSES, "B")
|
|
470
|
+
|
|
471
|
+
# LS_DATA registers (0x0A-0x15) all 12 bytes of light sensor data read as a
|
|
472
|
+
# single 96-bit value. With lsb_first=True the channels sit at:
|
|
473
|
+
# bits 0-23 : IR (3 bytes, 20-bit significant)
|
|
474
|
+
# bits 24-47 : Green
|
|
475
|
+
# bits 48-71 : Blue
|
|
476
|
+
# bits 72-95 : Red
|
|
477
|
+
_ls_data_raw = ROBits(96, _APDS9999_REG_LS_DATA_IR_0, 0, register_width=12)
|
|
478
|
+
|
|
479
|
+
def __init__(self, i2c_bus: "busio.I2C", address: int = _APDS9999_DEFAULT_ADDR) -> None:
|
|
480
|
+
self.i2c_device = i2c_device.I2CDevice(i2c_bus, address)
|
|
481
|
+
|
|
482
|
+
# Verify we are talking to the correct chip.
|
|
483
|
+
if self._part_id != _APDS9999_PART_ID:
|
|
484
|
+
raise RuntimeError(
|
|
485
|
+
"Failed to find APDS9999 check your wiring! "
|
|
486
|
+
+ f"Expected PART_ID 0x{_APDS9999_PART_ID:02X}, "
|
|
487
|
+
+ f"got 0x{self._part_id:02X}."
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
# Stores the overflow bit captured during the last proximity read.
|
|
491
|
+
self._proximity_read_overflow = False
|
|
492
|
+
|
|
493
|
+
@property
|
|
494
|
+
def light_gain(self) -> int:
|
|
495
|
+
"""The analogue gain applied to all light sensor channels.
|
|
496
|
+
|
|
497
|
+
Must be a :class:`LightGain` value:
|
|
498
|
+
|
|
499
|
+
* ``LightGain.GAIN_1X``
|
|
500
|
+
* ``LightGain.GAIN_3X``
|
|
501
|
+
* ``LightGain.GAIN_6X``
|
|
502
|
+
* ``LightGain.GAIN_9X``
|
|
503
|
+
* ``LightGain.GAIN_18X``
|
|
504
|
+
"""
|
|
505
|
+
return self._light_gain
|
|
506
|
+
|
|
507
|
+
@light_gain.setter
|
|
508
|
+
def light_gain(self, value: int) -> None:
|
|
509
|
+
if not LightGain.is_valid(value):
|
|
510
|
+
raise ValueError("light_gain must be a LightGain value")
|
|
511
|
+
self._light_gain = value
|
|
512
|
+
|
|
513
|
+
@property
|
|
514
|
+
def light_resolution(self) -> int:
|
|
515
|
+
"""The ADC resolution of the light sensor channels.
|
|
516
|
+
|
|
517
|
+
Must be a :class:`LightResolution` value:
|
|
518
|
+
|
|
519
|
+
* ``LightResolution.RES_20BIT`` 20-bit, 400 ms conversion
|
|
520
|
+
* ``LightResolution.RES_19BIT`` 19-bit, 200 ms conversion
|
|
521
|
+
* ``LightResolution.RES_18BIT`` 18-bit, 100 ms conversion
|
|
522
|
+
* ``LightResolution.RES_17BIT`` 17-bit, 50 ms conversion
|
|
523
|
+
* ``LightResolution.RES_16BIT`` 16-bit, 25 ms conversion
|
|
524
|
+
* ``LightResolution.RES_13BIT`` 13-bit, 3.125 ms conversion
|
|
525
|
+
"""
|
|
526
|
+
return self._light_resolution
|
|
527
|
+
|
|
528
|
+
@light_resolution.setter
|
|
529
|
+
def light_resolution(self, value: int) -> None:
|
|
530
|
+
if not LightResolution.is_valid(value):
|
|
531
|
+
raise ValueError("light_resolution must be a LightResolution value")
|
|
532
|
+
self._light_resolution = value
|
|
533
|
+
|
|
534
|
+
@property
|
|
535
|
+
def light_measurement_rate(self) -> int:
|
|
536
|
+
"""How frequently the light sensor takes a reading.
|
|
537
|
+
|
|
538
|
+
Must be a :class:`LightMeasurementRate` value:
|
|
539
|
+
|
|
540
|
+
* ``LightMeasurementRate.RATE_25MS``
|
|
541
|
+
* ``LightMeasurementRate.RATE_50MS``
|
|
542
|
+
* ``LightMeasurementRate.RATE_100MS`` (default)
|
|
543
|
+
* ``LightMeasurementRate.RATE_200MS``
|
|
544
|
+
* ``LightMeasurementRate.RATE_500MS``
|
|
545
|
+
* ``LightMeasurementRate.RATE_1000MS``
|
|
546
|
+
* ``LightMeasurementRate.RATE_2000MS``
|
|
547
|
+
"""
|
|
548
|
+
return self._light_measurement_rate
|
|
549
|
+
|
|
550
|
+
@light_measurement_rate.setter
|
|
551
|
+
def light_measurement_rate(self, value: int) -> None:
|
|
552
|
+
if not LightMeasurementRate.is_valid(value):
|
|
553
|
+
raise ValueError("light_measurement_rate must be a LightMeasurementRate value")
|
|
554
|
+
self._light_measurement_rate = value
|
|
555
|
+
|
|
556
|
+
@property
|
|
557
|
+
def proximity_resolution(self) -> int:
|
|
558
|
+
"""The ADC resolution of the proximity sensor.
|
|
559
|
+
|
|
560
|
+
Must be a :class:`ProximityResolution` value:
|
|
561
|
+
|
|
562
|
+
* ``ProximityResolution.RES_8BIT`` (hardware default)
|
|
563
|
+
* ``ProximityResolution.RES_9BIT``
|
|
564
|
+
* ``ProximityResolution.RES_10BIT``
|
|
565
|
+
* ``ProximityResolution.RES_11BIT``
|
|
566
|
+
"""
|
|
567
|
+
return self._proximity_resolution
|
|
568
|
+
|
|
569
|
+
@proximity_resolution.setter
|
|
570
|
+
def proximity_resolution(self, value: int) -> None:
|
|
571
|
+
if not ProximityResolution.is_valid(value):
|
|
572
|
+
raise ValueError("proximity_resolution must be a ProximityResolution value")
|
|
573
|
+
self._proximity_resolution = value
|
|
574
|
+
|
|
575
|
+
@property
|
|
576
|
+
def proximity(self) -> int:
|
|
577
|
+
"""The current proximity sensor reading as an 11-bit count (0–2047).
|
|
578
|
+
|
|
579
|
+
Each time this property is read the overflow flag is captured and can be
|
|
580
|
+
checked immediately afterward via :attr:`proximity_read_overflow`.
|
|
581
|
+
"""
|
|
582
|
+
raw = self._proximity_data
|
|
583
|
+
self._proximity_read_overflow = bool(raw & 0x0800)
|
|
584
|
+
return raw & 0x07FF
|
|
585
|
+
|
|
586
|
+
def calculate_lux(self, green_count: int) -> float:
|
|
587
|
+
"""Calculate illuminance in lux from a raw green channel count.
|
|
588
|
+
|
|
589
|
+
Reads the current :attr:`light_gain` and :attr:`light_resolution`
|
|
590
|
+
settings from the device and applies the appropriate scale factor from
|
|
591
|
+
the lookup table below. ``RES_13BIT`` is not supported and will raise
|
|
592
|
+
a ``ValueError``.
|
|
593
|
+
|
|
594
|
+
Lux factor table (rows = gain, columns = resolution):
|
|
595
|
+
|
|
596
|
+
+------------------+---------+---------+---------+---------+---------+
|
|
597
|
+
| | 20-bit | 19-bit | 18-bit | 17-bit | 16-bit |
|
|
598
|
+
+==================+=========+=========+=========+=========+=========+
|
|
599
|
+
| ``GAIN_1X`` | 0.136 | 0.273 | 0.548 | 1.099 | 2.193 |
|
|
600
|
+
+------------------+---------+---------+---------+---------+---------+
|
|
601
|
+
| ``GAIN_3X`` | 0.045 | 0.090 | 0.180 | 0.359 | 0.722 |
|
|
602
|
+
+------------------+---------+---------+---------+---------+---------+
|
|
603
|
+
| ``GAIN_6X`` | 0.022 | 0.045 | 0.090 | 0.179 | 0.360 |
|
|
604
|
+
+------------------+---------+---------+---------+---------+---------+
|
|
605
|
+
| ``GAIN_9X`` | 0.015 | 0.030 | 0.059 | 0.119 | 0.239 |
|
|
606
|
+
+------------------+---------+---------+---------+---------+---------+
|
|
607
|
+
| ``GAIN_18X`` | 0.007 | 0.015 | 0.029 | 0.059 | 0.117 |
|
|
608
|
+
+------------------+---------+---------+---------+---------+---------+
|
|
609
|
+
|
|
610
|
+
:param int green_count: Raw green channel value from :attr:`rgb_ir`.
|
|
611
|
+
:return: Illuminance in lux.
|
|
612
|
+
:rtype: float
|
|
613
|
+
"""
|
|
614
|
+
# Rows indexed by LightGain value (GAIN_1X=0 … GAIN_18X=4).
|
|
615
|
+
# Columns indexed by LightResolution value (RES_20BIT=0 … RES_16BIT=4).
|
|
616
|
+
# RES_13BIT (0x05) is not included, lux calculation is unsupported at
|
|
617
|
+
# that resolution.
|
|
618
|
+
_LUX_FACTORS = (
|
|
619
|
+
# 20-bit 19-bit 18-bit 17-bit 16-bit
|
|
620
|
+
(0.136, 0.273, 0.548, 1.099, 2.193), # GAIN_1X
|
|
621
|
+
(0.045, 0.090, 0.180, 0.359, 0.722), # GAIN_3X
|
|
622
|
+
(0.022, 0.045, 0.090, 0.179, 0.360), # GAIN_6X
|
|
623
|
+
(0.015, 0.030, 0.059, 0.119, 0.239), # GAIN_9X
|
|
624
|
+
(0.007, 0.015, 0.029, 0.059, 0.117), # GAIN_18X
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
gain = self.light_gain # 0–4, directly usable as a row index
|
|
628
|
+
res = self.light_resolution # 0–5, 5 = RES_13BIT (unsupported)
|
|
629
|
+
|
|
630
|
+
if res == LightResolution.RES_13BIT:
|
|
631
|
+
raise ValueError("calculate_lux() does not support LightResolution.RES_13BIT")
|
|
632
|
+
|
|
633
|
+
return green_count * _LUX_FACTORS[gain][res]
|
|
634
|
+
|
|
635
|
+
@property
|
|
636
|
+
def led_current(self) -> int:
|
|
637
|
+
"""The IR LED drive current used during proximity measurements.
|
|
638
|
+
|
|
639
|
+
Must be a :class:`LedCurrent` value:
|
|
640
|
+
|
|
641
|
+
* ``LedCurrent.MA_10`` 10 mA
|
|
642
|
+
* ``LedCurrent.MA_25`` 25 mA
|
|
643
|
+
"""
|
|
644
|
+
return self._led_current
|
|
645
|
+
|
|
646
|
+
@property
|
|
647
|
+
def light_interrupt_channel(self) -> int:
|
|
648
|
+
"""The light sensor channel compared against the interrupt thresholds.
|
|
649
|
+
|
|
650
|
+
Must be a :class:`LightInterruptChannel` value:
|
|
651
|
+
|
|
652
|
+
* ``LightInterruptChannel.IR``
|
|
653
|
+
* ``LightInterruptChannel.GREEN``
|
|
654
|
+
* ``LightInterruptChannel.RED``
|
|
655
|
+
* ``LightInterruptChannel.BLUE``
|
|
656
|
+
"""
|
|
657
|
+
return self._light_interrupt_channel
|
|
658
|
+
|
|
659
|
+
@light_interrupt_channel.setter
|
|
660
|
+
def light_interrupt_channel(self, value: int) -> None:
|
|
661
|
+
if not LightInterruptChannel.is_valid(value):
|
|
662
|
+
raise ValueError("light_interrupt_channel must be a LightInterruptChannel value")
|
|
663
|
+
self._light_interrupt_channel = value
|
|
664
|
+
|
|
665
|
+
@property
|
|
666
|
+
def proximity_persistence(self) -> int:
|
|
667
|
+
"""Number of consecutive out-of-threshold proximity readings before the
|
|
668
|
+
interrupt flag is asserted.
|
|
669
|
+
|
|
670
|
+
Valid range is 0–15.
|
|
671
|
+
"""
|
|
672
|
+
return self._proximity_persistence
|
|
673
|
+
|
|
674
|
+
@proximity_persistence.setter
|
|
675
|
+
def proximity_persistence(self, value: int) -> None:
|
|
676
|
+
if not 0 <= value <= 15:
|
|
677
|
+
raise ValueError("proximity_persistence must be 0–15")
|
|
678
|
+
self._proximity_persistence = value
|
|
679
|
+
|
|
680
|
+
@property
|
|
681
|
+
def light_persistence(self) -> int:
|
|
682
|
+
"""Number of consecutive out-of-threshold light readings before the
|
|
683
|
+
interrupt flag is asserted.
|
|
684
|
+
|
|
685
|
+
Valid range is 0–15.
|
|
686
|
+
"""
|
|
687
|
+
return self._light_persistence
|
|
688
|
+
|
|
689
|
+
@light_persistence.setter
|
|
690
|
+
def light_persistence(self, value: int) -> None:
|
|
691
|
+
if not 0 <= value <= 15:
|
|
692
|
+
raise ValueError("light_persistence must be 0–15")
|
|
693
|
+
self._light_persistence = value
|
|
694
|
+
|
|
695
|
+
@property
|
|
696
|
+
def light_variance(self) -> int:
|
|
697
|
+
"""The count variance required to trigger a light interrupt when
|
|
698
|
+
:attr:`light_variance_mode` is enabled.
|
|
699
|
+
|
|
700
|
+
Must be a :class:`LightVariance` value:
|
|
701
|
+
|
|
702
|
+
* ``LightVariance.VAR_8``
|
|
703
|
+
* ``LightVariance.VAR_16``
|
|
704
|
+
* ``LightVariance.VAR_32``
|
|
705
|
+
* ``LightVariance.VAR_64``
|
|
706
|
+
* ``LightVariance.VAR_128``
|
|
707
|
+
* ``LightVariance.VAR_256``
|
|
708
|
+
* ``LightVariance.VAR_512``
|
|
709
|
+
* ``LightVariance.VAR_1024``
|
|
710
|
+
"""
|
|
711
|
+
return self._light_variance
|
|
712
|
+
|
|
713
|
+
@light_variance.setter
|
|
714
|
+
def light_variance(self, value: int) -> None:
|
|
715
|
+
if not LightVariance.is_valid(value):
|
|
716
|
+
raise ValueError("light_variance must be a LightVariance value")
|
|
717
|
+
self._light_variance = value
|
|
718
|
+
|
|
719
|
+
@led_current.setter
|
|
720
|
+
def led_current(self, value: int) -> None:
|
|
721
|
+
if not LedCurrent.is_valid(value):
|
|
722
|
+
raise ValueError("led_current must be a LedCurrent value")
|
|
723
|
+
self._led_current = value
|
|
724
|
+
|
|
725
|
+
@property
|
|
726
|
+
def led_frequency(self) -> int:
|
|
727
|
+
"""The IR LED pulse modulation frequency used during proximity measurements.
|
|
728
|
+
|
|
729
|
+
Must be a :class:`LedFrequency` value:
|
|
730
|
+
|
|
731
|
+
* ``LedFrequency.KHZ_60``
|
|
732
|
+
* ``LedFrequency.KHZ_70``
|
|
733
|
+
* ``LedFrequency.KHZ_80``
|
|
734
|
+
* ``LedFrequency.KHZ_90``
|
|
735
|
+
* ``LedFrequency.KHZ_100``
|
|
736
|
+
"""
|
|
737
|
+
return self._led_frequency
|
|
738
|
+
|
|
739
|
+
@led_frequency.setter
|
|
740
|
+
def led_frequency(self, value: int) -> None:
|
|
741
|
+
if not LedFrequency.is_valid(value):
|
|
742
|
+
raise ValueError("led_frequency must be a LedFrequency value")
|
|
743
|
+
self._led_frequency = value
|
|
744
|
+
|
|
745
|
+
@property
|
|
746
|
+
def proximity_read_overflow(self) -> bool:
|
|
747
|
+
"""``True`` if the proximity sensor was saturated during the last read of
|
|
748
|
+
:attr:`proximity`. Updated on every :attr:`proximity` read; read-only.
|
|
749
|
+
"""
|
|
750
|
+
return self._proximity_read_overflow
|
|
751
|
+
|
|
752
|
+
def reset(self):
|
|
753
|
+
"""
|
|
754
|
+
Software reset the APDS9999
|
|
755
|
+
"""
|
|
756
|
+
self._reset = True
|
|
757
|
+
time.sleep(0.01)
|
|
758
|
+
|
|
759
|
+
@property
|
|
760
|
+
def main_status(self) -> Tuple[bool, bool, bool, bool, bool, bool]:
|
|
761
|
+
"""Read all status flags from the MAIN_STATUS register in a single I2C transaction.
|
|
762
|
+
|
|
763
|
+
.. warning::
|
|
764
|
+
Reading this register clears all status bits on the device.
|
|
765
|
+
|
|
766
|
+
Returns a tuple of six booleans:
|
|
767
|
+
``(proximity_data_ready, proximity_interrupt, proximity_logic,
|
|
768
|
+
light_data_ready, light_interrupt, power_on_reset)``
|
|
769
|
+
|
|
770
|
+
* ``proximity_data_ready`` new proximity data is available
|
|
771
|
+
* ``proximity_interrupt`` proximity interrupt has been triggered
|
|
772
|
+
* ``proximity_logic`` current state of the proximity logic signal
|
|
773
|
+
* ``light_data_ready`` new light sensor data is available
|
|
774
|
+
* ``light_interrupt`` light sensor interrupt has been triggered
|
|
775
|
+
* ``power_on_reset`` a power-on reset has occurred since last read
|
|
776
|
+
"""
|
|
777
|
+
raw = self._main_status
|
|
778
|
+
return (
|
|
779
|
+
bool(raw & 0x01), # proximity_data_ready (bit 0)
|
|
780
|
+
bool(raw & 0x02), # proximity_interrupt (bit 1)
|
|
781
|
+
bool(raw & 0x04), # proximity_logic (bit 2)
|
|
782
|
+
bool(raw & 0x08), # light_data_ready (bit 3)
|
|
783
|
+
bool(raw & 0x10), # light_interrupt (bit 4)
|
|
784
|
+
bool(raw & 0x20), # power_on_reset (bit 5)
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
@property
|
|
788
|
+
def rgb_ir(self) -> Tuple[int, int, int, int]:
|
|
789
|
+
"""All four light sensor channels as a tuple ``(red, green, blue, ir)``.
|
|
790
|
+
|
|
791
|
+
Each value is up to 20-bit count (0–1048575) read in a single 12-byte
|
|
792
|
+
burst starting at the IR data register. See ``light_resolution`` for
|
|
793
|
+
bit depth setting, lower resolution will result in lower maximum values.
|
|
794
|
+
"""
|
|
795
|
+
raw = self._ls_data_raw
|
|
796
|
+
ir = raw & 0x0FFFFF
|
|
797
|
+
g = (raw >> 24) & 0x0FFFFF
|
|
798
|
+
b = (raw >> 48) & 0x0FFFFF
|
|
799
|
+
r = (raw >> 72) & 0x0FFFFF
|
|
800
|
+
return r, g, b, ir
|
|
801
|
+
|
|
802
|
+
@property
|
|
803
|
+
def rgb(self) -> Tuple[int, int, int]:
|
|
804
|
+
"""The RGB light sensor channels as a tuple ``(red, green, blue)``
|
|
805
|
+
mapped to the range 0-255. For full 20bit resolution set
|
|
806
|
+
``light_resolution`` to ``LightResolution.RES_20BIT`` and use
|
|
807
|
+
``rgb_ir`` instead of ``rgb```
|
|
808
|
+
|
|
809
|
+
:return: (red, gree, blue)
|
|
810
|
+
"""
|
|
811
|
+
r, g, b, _ = self.rgb_ir
|
|
812
|
+
|
|
813
|
+
max_lut = {
|
|
814
|
+
LightResolution.RES_20BIT: 0b11111111111111111111,
|
|
815
|
+
LightResolution.RES_19BIT: 0b1111111111111111111,
|
|
816
|
+
LightResolution.RES_18BIT: 0b111111111111111111,
|
|
817
|
+
LightResolution.RES_17BIT: 0b11111111111111111,
|
|
818
|
+
LightResolution.RES_16BIT: 0b1111111111111111,
|
|
819
|
+
LightResolution.RES_13BIT: 0b1111111111111,
|
|
820
|
+
}
|
|
821
|
+
in_max = max_lut[self.light_resolution]
|
|
822
|
+
r = int(map_range(r, 0, in_max, 0, 255))
|
|
823
|
+
g = int(map_range(g, 0, in_max, 0, 255))
|
|
824
|
+
b = int(map_range(b, 0, in_max, 0, 255))
|
|
825
|
+
return r, g, b
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: adafruit-circuitpython-apds9999
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: CircuitPython driver for Broadcom APDS-9999 Light + RGB + Proximity
|
|
5
|
+
Author-email: Adafruit Industries <circuitpython@adafruit.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/adafruit/Adafruit_CircuitPython_APDS9999
|
|
8
|
+
Keywords: adafruit,blinka,circuitpython,micropython,apds9999,rgb,ir,light,sensor,proximity
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
11
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
12
|
+
Classifier: Topic :: System :: Hardware
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Description-Content-Type: text/x-rst
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: Adafruit-Blinka
|
|
18
|
+
Requires-Dist: adafruit-circuitpython-busdevice
|
|
19
|
+
Requires-Dist: adafruit-circuitpython-register
|
|
20
|
+
Requires-Dist: adafruit-circuitpython-simplemath
|
|
21
|
+
Provides-Extra: optional
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
Introduction
|
|
25
|
+
============
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
.. image:: https://readthedocs.org/projects/adafruit-circuitpython-apds9999/badge/?version=latest
|
|
29
|
+
:target: https://docs.circuitpython.org/projects/apds9999/en/latest/
|
|
30
|
+
:alt: Documentation Status
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg
|
|
34
|
+
:target: https://adafru.it/discord
|
|
35
|
+
:alt: Discord
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
.. image:: https://github.com/adafruit/Adafruit_CircuitPython_APDS9999/workflows/Build%20CI/badge.svg
|
|
39
|
+
:target: https://github.com/adafruit/Adafruit_CircuitPython_APDS9999/actions
|
|
40
|
+
:alt: Build Status
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json
|
|
44
|
+
:target: https://github.com/astral-sh/ruff
|
|
45
|
+
:alt: Code Style: Ruff
|
|
46
|
+
|
|
47
|
+
CircuitPython driver for Broadcom APDS-9999 Light + RGB + Proximity
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
Dependencies
|
|
51
|
+
=============
|
|
52
|
+
This driver depends on:
|
|
53
|
+
|
|
54
|
+
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
|
|
55
|
+
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
|
|
56
|
+
* `Register <https://github.com/adafruit/Adafruit_CircuitPython_Register>`_
|
|
57
|
+
|
|
58
|
+
Please ensure all dependencies are available on the CircuitPython filesystem.
|
|
59
|
+
This is easily achieved by downloading
|
|
60
|
+
`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_
|
|
61
|
+
or individual libraries can be installed using
|
|
62
|
+
`circup <https://github.com/adafruit/circup>`_.
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
Adafruit APDS9999 breakout
|
|
67
|
+
|
|
68
|
+
`Purchase one from the Adafruit shop <http://www.adafruit.com/products/6461>`_
|
|
69
|
+
|
|
70
|
+
Installing from PyPI
|
|
71
|
+
=====================
|
|
72
|
+
|
|
73
|
+
On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
|
|
74
|
+
PyPI <https://pypi.org/project/adafruit-circuitpython-apds9999/>`_.
|
|
75
|
+
To install for current user:
|
|
76
|
+
|
|
77
|
+
.. code-block:: shell
|
|
78
|
+
|
|
79
|
+
pip3 install adafruit-circuitpython-apds9999
|
|
80
|
+
|
|
81
|
+
To install system-wide (this may be required in some cases):
|
|
82
|
+
|
|
83
|
+
.. code-block:: shell
|
|
84
|
+
|
|
85
|
+
sudo pip3 install adafruit-circuitpython-apds9999
|
|
86
|
+
|
|
87
|
+
To install in a virtual environment in your current project:
|
|
88
|
+
|
|
89
|
+
.. code-block:: shell
|
|
90
|
+
|
|
91
|
+
mkdir project-name && cd project-name
|
|
92
|
+
python3 -m venv .venv
|
|
93
|
+
source .env/bin/activate
|
|
94
|
+
pip3 install adafruit-circuitpython-apds9999
|
|
95
|
+
|
|
96
|
+
Installing to a Connected CircuitPython Device with Circup
|
|
97
|
+
==========================================================
|
|
98
|
+
|
|
99
|
+
Make sure that you have ``circup`` installed in your Python environment.
|
|
100
|
+
Install it with the following command if necessary:
|
|
101
|
+
|
|
102
|
+
.. code-block:: shell
|
|
103
|
+
|
|
104
|
+
pip3 install circup
|
|
105
|
+
|
|
106
|
+
With ``circup`` installed and your CircuitPython device connected use the
|
|
107
|
+
following command to install:
|
|
108
|
+
|
|
109
|
+
.. code-block:: shell
|
|
110
|
+
|
|
111
|
+
circup install adafruit_apds9999
|
|
112
|
+
|
|
113
|
+
Or the following command to update an existing version:
|
|
114
|
+
|
|
115
|
+
.. code-block:: shell
|
|
116
|
+
|
|
117
|
+
circup update
|
|
118
|
+
|
|
119
|
+
Usage Example
|
|
120
|
+
=============
|
|
121
|
+
|
|
122
|
+
.. code-block:: python
|
|
123
|
+
|
|
124
|
+
import time
|
|
125
|
+
|
|
126
|
+
import board
|
|
127
|
+
|
|
128
|
+
from adafruit_apds9999 import APDS9999
|
|
129
|
+
|
|
130
|
+
apds_sensor = APDS9999(board.I2C())
|
|
131
|
+
|
|
132
|
+
apds_sensor.light_sensor_enabled = True
|
|
133
|
+
apds_sensor.proximity_sensor_enabled = True
|
|
134
|
+
apds_sensor.rgb_mode = True
|
|
135
|
+
|
|
136
|
+
while True:
|
|
137
|
+
time.sleep(1)
|
|
138
|
+
print(apds_sensor.main_status)
|
|
139
|
+
r, g, b, ir = apds_sensor.rgb_ir
|
|
140
|
+
|
|
141
|
+
print(
|
|
142
|
+
f"r: {r}, g: {g}, b: {b}, ir: {ir} "
|
|
143
|
+
f"lux: {apds_sensor.calculate_lux(g)} proximity: {apds_sensor.proximity}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
Documentation
|
|
148
|
+
=============
|
|
149
|
+
API documentation for this library can be found on `Read the Docs <https://docs.circuitpython.org/projects/apds9999/en/latest/>`_.
|
|
150
|
+
|
|
151
|
+
For information on building library documentation, please check out
|
|
152
|
+
`this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
|
|
153
|
+
|
|
154
|
+
Contributing
|
|
155
|
+
============
|
|
156
|
+
|
|
157
|
+
Contributions are welcome! Please read our `Code of Conduct
|
|
158
|
+
<https://github.com/adafruit/Adafruit_CircuitPython_APDS9999/blob/HEAD/CODE_OF_CONDUCT.md>`_
|
|
159
|
+
before contributing to help this project stay welcoming.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
adafruit_apds9999.py,sha256=9tgudw9nVWl76ulzX3n_dTsG-Yui4_3CDTDe3NCWXxI,34792
|
|
2
|
+
adafruit_circuitpython_apds9999-1.0.0.dist-info/licenses/LICENSE,sha256=IrDmGxfX4xdm-jZka6acotFMss63SMR3pHWyFp7jA3o,1100
|
|
3
|
+
adafruit_circuitpython_apds9999-1.0.0.dist-info/METADATA,sha256=ydtvHnCFVxg5wI1yHjFKDU9Ch8hFzFXIXJ8aC482t2o,4967
|
|
4
|
+
adafruit_circuitpython_apds9999-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
5
|
+
adafruit_circuitpython_apds9999-1.0.0.dist-info/top_level.txt,sha256=pNcrBM-SSYjLsH1aB01i3ZWkysLpycmw8SiJwX8GEzo,18
|
|
6
|
+
adafruit_circuitpython_apds9999-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tim Cocks 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 all
|
|
13
|
+
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 THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
adafruit_apds9999
|