Skip to content

Commit 1b26498

Browse files
authored
Merge pull request #4 from FoamyGuy/remaining_features
Remaining Features and Properties
2 parents 3b141e5 + 243a50e commit 1b26498

File tree

3 files changed

+388
-27
lines changed

3 files changed

+388
-27
lines changed

adafruit_vcnl4200.py

Lines changed: 273 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark, Tim Cocks for Adafruit Industries
22
#
33
# SPDX-License-Identifier: MIT
4+
5+
# Written by Liz Clark & Tim Cocks (Adafruit Industries) with OpenAI ChatGPT 4o May 13, 2024 build
6+
# https://help.openai.com/en/articles/6825453-chatgpt-release-notes
7+
8+
# https://chatgpt.com/share/6720fdc1-2bc0-8006-8f7c-f0ff11189ea9
49
"""
510
`adafruit_vcnl4200`
611
================================================================================
@@ -141,71 +146,312 @@
141146

142147

143148
class Adafruit_VCNL4200:
149+
"""
150+
Driver for VCNL4200 Proximity & Light Sensor
151+
152+
:param i2c: I2C bus
153+
:param addr: I2C Address if not using default
154+
"""
155+
144156
# Device ID expected value for VCNL4200
145157
_DEVICE_ID = 0x1058
146158

147159
# Register for ALS configuration
148160
als_shutdown = RWBits(1, _ALS_CONF, 0) # ALS shutdown bit
149-
als_integration_time = RWBits(2, _ALS_CONF, 6) # ALS integration time bits
150-
als_persistance = RWBits(2, _ALS_CONF, 2) # ALS persistence bits
151-
als_low_threshold = UnaryStruct(_ALS_THDL, "<H")
152-
als_high_threshold = UnaryStruct(_ALS_THDH, "<H")
153-
prox_active_force = RWBit(_PS_CONF3MS, 3)
154-
prox_duty = RWBits(2, _PS_CONF12, 6)
161+
"""Enables or disables the ALS (Ambient Light Sensor) shutdown mode.
162+
Controls the power state of the ALS, allowing it to be shut down to conserve power.
163+
Set to true to enable shutdown mode (power off ALS), false to disable (power on ALS)."""
164+
als_threshold_low = UnaryStruct(_ALS_THDL, "<H")
165+
"""The 16-bit numerical low threshold for the ALS (Ambient Light Sensor) interrupt.
166+
If the ALS reading falls below this threshold and ALS interrupt is enabled, an interrupt
167+
will be triggered."""
168+
als_threshold_high = UnaryStruct(_ALS_THDH, "<H")
169+
"""The 16-bit numerical upper limit for proximity detection.If the proximity reading
170+
exceeds this threshold and the interrupt is enabled, an interrupt will be triggered."""
155171
prox_hd = RWBit(_PS_CONF12, 11)
156-
prox_integration_time = RWBits(3, _PS_CONF12, 1)
157-
prox_interrupt = RWBits(2, _PS_CONF12, 8)
158-
prox_persistence = RWBits(2, _PS_CONF12, 4)
172+
"""The proximity sensor (PS) resolution to high definition (HD). Set to True to enable
173+
high definition mode (16-bit resolution), False for standard resolution (12-bit)."""
159174
prox_shutdown = RWBit(_PS_CONF12, 0) # Bit 0: PS_SD (Proximity Sensor Shutdown)
175+
"""The proximity sensor (PS) shutdown mode. Set to True to enable shutdown mode
176+
(power off proximity sensor), false to disable (power on proximity sensor)."""
160177
proximity = ROUnaryStruct(_PS_DATA, "<H")
178+
"""The current proximity data from the proximity sensor (PS)."""
161179
lux = ROUnaryStruct(_ALS_DATA, "<H")
180+
"""The current lux data from ambient light sensor (ALS)"""
181+
_prox_multi_pulse = RWBits(2, _PS_CONF3MS, 5, register_width=2)
182+
_prox_interrupt = RWBits(2, _PS_CONF12, 8, register_width=2)
183+
_prox_duty = RWBits(2, _PS_CONF12, 6, register_width=2)
184+
_prox_integration_time = RWBits(3, _PS_CONF12, 1, register_width=2)
185+
_prox_persistence = RWBits(2, _PS_CONF12, 4, register_width=2)
186+
prox_sun_cancellation = RWBit(_PS_CONF3MS, 0, register_width=2)
187+
"""The sunlight cancellation feature for the proximity sensor (PS).
188+
Controls the sunlight cancellation feature, which improves proximity
189+
detection accuracy in bright or sunny conditions by mitigating background
190+
light interference. Set to true to enable sunlight cancellation, false to disable
191+
it."""
192+
prox_sunlight_double_immunity = RWBit(_PS_CONF3MS, 1, register_width=2)
193+
"""Double immunity mode to sunlight for the proximity
194+
sensor (PS). Configures an enhanced sunlight immunity mode, which increases the sensor’s
195+
ability to filter out interference from bright sunlight for improved proximity detection.
196+
Set to True to enable double sunlight immunity, False to disable it."""
197+
prox_active_force = RWBit(_PS_CONF3MS, 3, register_width=2)
198+
"""The active force mode for the proximity sensor (PS). Configures the proximity
199+
sensor to operate in active force mode, where measurements are taken only when
200+
manually triggered by `trigger_prox()`. Set to True to enable active force mode,
201+
False to disable it."""
202+
prox_smart_persistence = RWBit(_PS_CONF3MS, 4, register_width=2)
203+
"""The smart persistence mode for the proximity sensor (PS).
204+
Configures the smart persistence feature, which helps reduce false triggers
205+
by adjusting the persistence behavior based on ambient conditions.
206+
Set to True to enable smart persistence, False to disable it."""
207+
sun_protect_polarity = RWBit(_PS_CONF3MS, 3 + 8, register_width=2)
208+
"""The polarity of the sunlight protection output for the proximity sensor (PS).
209+
Configures the polarity of the sunlight protection output signal, which
210+
affects how sunlight interference is managed in proximity detection.
211+
Set to True for active high polarity, False for active low polarity."""
212+
prox_boost_typical_sunlight_capability = RWBit(_PS_CONF3MS, 4 + 8, register_width=2)
213+
"""The boosted sunlight protection capability for the proximity sensor (PS).
214+
Boosts the proximity sensor's resistance to typical sunlight interference for
215+
more reliable proximity measurements in bright ambient conditions.
216+
Set to true to enable boosted sunlight protection, false to disable it."""
217+
prox_interrupt_logic_mode = RWBit(_PS_CONF3MS, 7 + 8, register_width=2)
218+
"""The interrupt logic mode for the proximity sensor (PS). Configures
219+
the interrupt output logic mode for the proximity sensor, determining if
220+
the interrupt signal is active high or active low. Set to True for active
221+
high logic, False for active low logic."""
222+
prox_cancellation_level = UnaryStruct(
223+
_PS_CANC_LVL, "<H"
224+
) # 16-bit registor for cancellation level
225+
"""The 16-bit numerical proximity sensor (PS) cancellation level. Configures
226+
the cancellation level for the proximity sensor, which helps to
227+
reduce interference from background light by subtracting a baseline level."""
228+
prox_int_threshold_low = UnaryStruct(
229+
_PS_THDL, "<H"
230+
) # 16-bit register for proximity threshold low
231+
"""The 16-bit numerical low threshold for the proximity sensor (PS) interrupt.
232+
Configures the lower limit for proximity detection. If the proximity reading
233+
falls below this threshold and the interrupt is enabled, an interrupt will be
234+
triggered."""
235+
prox_int_threshold_high = UnaryStruct(
236+
_PS_THDH, "<H"
237+
) # 16-bit register for proximity threshold high
238+
"""The 16-bit numerical high threshold for the proximity sensor (PS) interrupt.
239+
Configures the upper limit for proximity detection. If the proximity reading
240+
exceeds this threshold and the interrupt is enabled, an interrupt will be
241+
triggered."""
242+
_interrupt_flags = RWBits(8, _INT_FLAG, 0, register_width=2)
243+
_prox_led_current = RWBits(3, _PS_CONF3MS, 8, register_width=2)
162244
white_light = ROUnaryStruct(_WHITE_DATA, "<H") # 16-bit register for white light data
245+
"""The current white light data. The 16-bit numerical raw white light measurement, representing
246+
the sensor’s sensitivity to white light."""
247+
_als_int_time = RWBits(2, _ALS_CONF, 6, register_width=2)
248+
_als_persistence = RWBits(2, _ALS_CONF, 2, register_width=2)
163249
_als_int_en = RWBits(1, _ALS_CONF, 1) # Bit 1: ALS interrupt enable
164250
_als_int_switch = RWBits(1, _ALS_CONF, 5) # Bit 5: ALS interrupt channel selection (white/ALS)
165251
_proximity_int_en = RWBits(1, _PS_CONF12, 0)
166252
_prox_trigger = RWBit(_PS_CONF3MS, 2)
253+
_device_id = UnaryStruct(_ID, "<H")
254+
_raw_interrupt_flags = UnaryStruct(_INT_FLAG, "<H") # 2-byte read, big endian
167255

168256
def __init__(self, i2c: I2C, addr: int = _I2C_ADDRESS) -> None:
169257
self.i2c_device = I2CDevice(i2c, addr)
170-
for _ in range(2):
171-
with self.i2c_device as i2c:
172-
# Manually read the device ID register (0x0E, 2 bytes)
173-
buffer = bytearray(2)
174-
i2c.write_then_readinto(bytes([_ID]), buffer)
175-
device_id = int.from_bytes(buffer, "little")
176-
# Check if it matches expected device ID
177-
if device_id == self._DEVICE_ID:
178-
break
179-
else:
180-
raise RuntimeError("Device ID mismatch.")
258+
if self._device_id != self._DEVICE_ID:
259+
raise RuntimeError("Device ID mismatch.")
181260
try:
182261
self.als_shutdown = False
183262
self.als_integration_time = ALS_IT["50MS"]
184263
self.als_persistence = ALS_PERS["1"]
185-
self.als_low_threshold = 0
186-
self.als_high_threshold = 0xFFFF
187-
self.set_interrupt(enabled=False, white_channel=False)
264+
self.als_threshold_low = 0
265+
self.als_threshold_high = 0xFFFF
266+
self.als_interrupt(enabled=True, white_channel=False)
188267
self.prox_duty = PS_DUTY["1_160"]
189268
self.prox_shutdown = False
190269
self.prox_integration_time = PS_IT["1T"]
191270
self.prox_persistence = PS_PERS["1"]
192271
except Exception as error:
193272
raise RuntimeError(f"Failed to initialize: {error}") from error
194273

195-
def set_interrupt(self, enabled, white_channel):
274+
def als_interrupt(self, enabled: bool, white_channel: bool) -> bool:
196275
"""Configure ALS interrupt settings, enabling or disabling
197-
the interrupt and selecting the interrupt channel."""
276+
the interrupt and selecting the interrupt channel.
277+
278+
:return bool: True if setting the values succeeded, False otherwise."""
198279
try:
199280
self._als_int_en = enabled
200281
self._als_int_switch = white_channel
201282
return True
202283
except OSError:
203284
return False
204285

205-
def trigger_prox(self):
206-
"""Triggers a single proximity measurement manually in active force mode."""
286+
def trigger_prox(self) -> bool:
287+
"""Triggers a single proximity measurement manually in active force mode.
288+
Initiates a one-time proximity measurement in active force mode. This can be
289+
used when proximity measurements are not continuous and need to be triggered
290+
individually.
291+
292+
:return bool: True if triggering succeeded, False otherwise."""
207293
try:
208294
self._prox_trigger = True
209295
return True
210296
except OSError:
211297
return False
298+
299+
@property
300+
def prox_interrupt(self) -> str:
301+
"""Interrupt mode for the proximity sensor
302+
Configures the interrupt condition for the proximity sensor, which determines
303+
when an interrupt will be triggered based on the detected proximity.
304+
305+
:return str: The interrupt mode for the proximity sensor.
306+
"""
307+
PS_INT_REVERSE = {value: key for key, value in PS_INT.items()}
308+
# Return the mode name if available, otherwise return "Unknown"
309+
return PS_INT_REVERSE.get(self._prox_interrupt, "Unknown")
310+
311+
@prox_interrupt.setter
312+
def prox_interrupt(self, mode: int) -> None:
313+
if mode not in PS_INT.values():
314+
raise ValueError("Invalid interrupt mode")
315+
self._prox_interrupt = mode
316+
317+
@property
318+
def prox_duty(self) -> str:
319+
"""Proximity sensor duty cycle setting.
320+
Configures the duty cycle of the infrared emitter for the proximity sensor,
321+
which affects power consumption and response time.
322+
323+
:return str: The duty cycle of the infrared emitter for the proximity sensor."""
324+
# Reverse lookup dictionary for PS_DUTY
325+
PS_DUTY_REVERSE = {value: key for key, value in PS_DUTY.items()}
326+
return PS_DUTY_REVERSE.get(self._prox_duty, "Unknown")
327+
328+
@prox_duty.setter
329+
def prox_duty(self, setting: int) -> None:
330+
if setting not in PS_DUTY.values():
331+
raise ValueError(f"Invalid proximity duty cycle setting: {setting}")
332+
self._prox_duty = setting
333+
334+
@property
335+
def als_integration_time(self) -> str:
336+
"""ALS integration time setting. Configures the integration time for
337+
the ambient light sensor to control sensitivity and range.
338+
339+
:return str: The ALS integration time setting"""
340+
# Reverse lookup dictionary for ALS_IT
341+
ALS_IT_REVERSE = {value: key for key, value in ALS_IT.items()}
342+
# Map the result to the setting name, defaulting to "Unknown" if unmatched
343+
return ALS_IT_REVERSE.get(self._als_int_time, "Unknown")
344+
345+
@als_integration_time.setter
346+
def als_integration_time(self, it: int) -> None:
347+
if it not in ALS_IT.values():
348+
raise ValueError(f"Invalid ALS integration time setting: {it}")
349+
self._als_int_time = it
350+
351+
@property
352+
def als_persistence(self) -> str:
353+
"""ALS persistence setting. Configures the persistence level
354+
of the ALS interrupt, which determines how many consecutive ALS threshold
355+
triggers are required to activate the interrupt.
356+
357+
:return str: The current persistence setting
358+
"""
359+
# Reverse lookup dictionary for ALS_PERS
360+
ALS_PERS_REVERSE = {value: key for key, value in ALS_PERS.items()}
361+
return ALS_PERS_REVERSE.get(self._als_persistence, "Unknown")
362+
363+
@als_persistence.setter
364+
def als_persistence(self, pers: int) -> None:
365+
if pers not in ALS_PERS.values():
366+
raise ValueError(f"Invalid ALS persistence setting: {pers}")
367+
self._als_persistence = pers
368+
369+
@property
370+
def prox_multi_pulse(self) -> str:
371+
"""Configures the number of infrared pulses used by the proximity sensor in a
372+
single measurement. Increasing the pulse count can improve accuracy,
373+
especially in high ambient light conditions.
374+
375+
:return str: The number of infrared pulses configured for the proximity sensor
376+
"""
377+
PS_MP_REVERSE = {value: key for key, value in PS_MPS.items()}
378+
return PS_MP_REVERSE.get(self._prox_multi_pulse, "Unknown")
379+
380+
@prox_multi_pulse.setter
381+
def prox_multi_pulse(self, setting: int) -> None:
382+
if setting not in PS_MPS.values():
383+
raise ValueError(f"Invalid PS_MPS setting: {setting}")
384+
self._prox_multi_pulse = setting
385+
386+
@property
387+
def prox_integration_time(self) -> str:
388+
"""Proximity sensor integration time. Configures the integration
389+
time for the proximity sensor, which affects the duration for which the
390+
sensor is sensitive to reflected light.
391+
392+
:return str: The current integration time
393+
"""
394+
# Reverse lookup dictionary for PS_IT
395+
PS_IT_REVERSE = {value: key for key, value in PS_IT.items()}
396+
return PS_IT_REVERSE.get(self._prox_integration_time, "Unknown")
397+
398+
@prox_integration_time.setter
399+
def prox_integration_time(self, setting: int) -> None:
400+
if setting not in PS_IT.values():
401+
raise ValueError(f"Invalid proximity integration time setting: {setting}")
402+
self._prox_integration_time = setting
403+
404+
@property
405+
def prox_persistence(self) -> str:
406+
"""Proximity sensor persistence setting. Configures the persistence
407+
level of the proximity sensor interrupt, defining how many consecutive
408+
threshold triggers are required to activate the interrupt.
409+
410+
:return str: The current persistence setting
411+
"""
412+
# Reverse lookup dictionary for PS_PERS
413+
PS_PERS_REVERSE = {value: key for key, value in PS_PERS.items()}
414+
return PS_PERS_REVERSE.get(self._prox_persistence, "Unknown")
415+
416+
@prox_persistence.setter
417+
def prox_persistence(self, setting: int) -> None:
418+
if setting not in PS_PERS.values():
419+
raise ValueError(f"Invalid proximity persistence setting: {setting}")
420+
self._prox_persistence = setting
421+
422+
@property
423+
def prox_led_current(self) -> str:
424+
"""IR LED current setting. Configures the driving current for the
425+
infrared LED used in proximity detection, which affects the range
426+
and power consumption of the sensor.
427+
428+
:return str: The LED current setting"""
429+
# Reverse lookup dictionary for PS_PERS
430+
LED_I_REVERSE = {value: key for key, value in LED_I.items()}
431+
return LED_I_REVERSE.get(self._prox_led_current, "Unknown")
432+
433+
@prox_led_current.setter
434+
def prox_led_current(self, setting: int) -> None:
435+
if setting not in LED_I.values():
436+
raise ValueError(f"Invalid proximity IR LED current setting: {setting}")
437+
self._prox_led_current = setting
438+
439+
@property
440+
def interrupt_flags(self) -> typing.Dict[str, bool]:
441+
"""The current interrupt flags from the sensor. Retrieves the current
442+
interrupt status flags, which indicate various sensor states, such as
443+
threshold crossings or sunlight protection events.
444+
445+
:return dict: The current interrupt flag values
446+
"""
447+
# Read the full 16-bit register value and isolate the high byte
448+
raw_value = (self._raw_interrupt_flags >> 8) & 0xFF
449+
# Interpret each flag based on the datasheet's bit definition
450+
return {
451+
"ALS_HIGH": bool(raw_value & _INTFLAG_ALS_HIGH),
452+
"PROX_CLOSE": bool(raw_value & _INTFLAG_PROX_CLOSE),
453+
"ALS_LOW": bool(raw_value & _INTFLAG_ALS_LOW),
454+
"PROX_AWAY": bool(raw_value & _INTFLAG_PROX_AWAY),
455+
"PROX_SPFLAG": bool(raw_value & _INTFLAG_PROX_SPFLAG),
456+
"PROX_UPFLAG": bool(raw_value & _INTFLAG_PROX_UPFLAG),
457+
}

0 commit comments

Comments
 (0)