Skip to content

Commit e317dcb

Browse files
authored
Merge pull request #23 from JonasSchatz/main
Added support for continuous mode and history buffering
2 parents dd125e1 + 43b0735 commit e317dcb

File tree

6 files changed

+298
-24
lines changed

6 files changed

+298
-24
lines changed

adafruit_vl6180x.py

Lines changed: 117 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
CircuitPython module for the VL6180X distance sensor. See
1010
examples/simpletest.py for a demo of the usage.
1111
12-
* Author(s): Tony DiCola
12+
* Author(s): Tony DiCola, Jonas Schatz
1313
1414
Implementation Notes
1515
--------------------
@@ -26,12 +26,14 @@
2626
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
2727
"""
2828
import struct
29+
import time
30+
2931
from micropython import const
3032

3133
from adafruit_bus_device import i2c_device
3234

3335
try:
34-
import typing # pylint: disable=unused-import
36+
from typing import Optional, List
3537
from busio import I2C
3638
except ImportError:
3739
pass
@@ -40,23 +42,31 @@
4042
__version__ = "0.0.0-auto.0"
4143
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_VL6180X.git"
4244

43-
44-
# Internal constants:
45-
_VL6180X_DEFAULT_I2C_ADDR = const(0x29)
45+
# Registers
4646
_VL6180X_REG_IDENTIFICATION_MODEL_ID = const(0x000)
47+
48+
_VL6180X_REG_SYSTEM_HISTORY_CTRL = const(0x012)
4749
_VL6180X_REG_SYSTEM_INTERRUPT_CONFIG = const(0x014)
4850
_VL6180X_REG_SYSTEM_INTERRUPT_CLEAR = const(0x015)
4951
_VL6180X_REG_SYSTEM_FRESH_OUT_OF_RESET = const(0x016)
52+
5053
_VL6180X_REG_SYSRANGE_START = const(0x018)
54+
_VL6180X_REG_SYSRANGE_INTERMEASUREMENT_PERIOD = const(0x01B)
55+
_VL6180X_REG_SYSRANGE_PART_TO_PART_RANGE_OFFSET = const(0x024)
56+
5157
_VL6180X_REG_SYSALS_START = const(0x038)
5258
_VL6180X_REG_SYSALS_ANALOGUE_GAIN = const(0x03F)
5359
_VL6180X_REG_SYSALS_INTEGRATION_PERIOD_HI = const(0x040)
5460
_VL6180X_REG_SYSALS_INTEGRATION_PERIOD_LO = const(0x041)
55-
_VL6180X_REG_RESULT_ALS_VAL = const(0x050)
56-
_VL6180X_REG_RESULT_RANGE_VAL = const(0x062)
61+
5762
_VL6180X_REG_RESULT_RANGE_STATUS = const(0x04D)
5863
_VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO = const(0x04F)
59-
_VL6180X_REG_SYSRANGE_PART_TO_PART_RANGE_OFFSET = const(0x024)
64+
_VL6180X_REG_RESULT_ALS_VAL = const(0x050)
65+
_VL6180X_REG_RESULT_HISTORY_BUFFER_0 = const(0x052)
66+
_VL6180X_REG_RESULT_RANGE_VAL = const(0x062)
67+
68+
# Internal constants:
69+
_VL6180X_DEFAULT_I2C_ADDR = const(0x29)
6070

6171
# User-facing constants:
6272
ALS_GAIN_1 = const(0x06)
@@ -82,7 +92,7 @@
8292

8393

8494
class VL6180X:
85-
"""Create an instance of the VL6180X distance sensor. You must pass in
95+
"""Create an instance of the VL6180X distance sensor. You must pass in
8696
the following parameters:
8797
8898
:param i2c: An instance of the I2C bus connected to the sensor.
@@ -103,22 +113,85 @@ def __init__(
103113
self._write_8(_VL6180X_REG_SYSTEM_FRESH_OUT_OF_RESET, 0x00)
104114
self.offset = offset
105115

116+
# Reset a sensor that crashed while in continuous mode
117+
if self.continuous_mode_enabled:
118+
self.stop_range_continuous()
119+
time.sleep(0.1)
120+
121+
# Activate history buffer for range measurement
122+
self._write_8(_VL6180X_REG_SYSTEM_HISTORY_CTRL, 0x01)
123+
106124
@property
107125
def range(self) -> int:
108126
"""Read the range of an object in front of sensor and return it in mm."""
109-
# wait for device to be ready for range measurement
110-
while not self._read_8(_VL6180X_REG_RESULT_RANGE_STATUS) & 0x01:
111-
pass
112-
# Start a range measurement
113-
self._write_8(_VL6180X_REG_SYSRANGE_START, 0x01)
114-
# Poll until bit 2 is set
115-
while not self._read_8(_VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04:
116-
pass
117-
# read range in mm
118-
range_ = self._read_8(_VL6180X_REG_RESULT_RANGE_VAL)
119-
# clear interrupt
120-
self._write_8(_VL6180X_REG_SYSTEM_INTERRUPT_CLEAR, 0x07)
121-
return range_
127+
if self.continuous_mode_enabled:
128+
return self._read_range_continuous()
129+
return self._read_range_single()
130+
131+
@property
132+
def range_from_history(self) -> Optional[int]:
133+
"""Read the latest range data from history
134+
To do so, you don't have to wait for a complete measurement."""
135+
136+
if not self.range_history_enabled:
137+
return None
138+
139+
return self._read_8(_VL6180X_REG_RESULT_HISTORY_BUFFER_0)
140+
141+
@property
142+
def ranges_from_history(self) -> Optional[List[int]]:
143+
""" Read the last 16 range measurements from history """
144+
145+
if not self.range_history_enabled:
146+
return None
147+
148+
return [
149+
self._read_8(_VL6180X_REG_RESULT_HISTORY_BUFFER_0 + age)
150+
for age in range(16)
151+
]
152+
153+
@property
154+
def range_history_enabled(self) -> bool:
155+
""" Checks if history buffer stores range data """
156+
157+
history_ctrl: int = self._read_8(_VL6180X_REG_SYSTEM_HISTORY_CTRL)
158+
159+
if history_ctrl & 0x0:
160+
print("History buffering not enabled")
161+
return False
162+
163+
if (history_ctrl > 1) & 0x1:
164+
print("History buffer stores ALS data, not range")
165+
return False
166+
167+
return True
168+
169+
def start_range_continuous(self, period: int = 100) -> None:
170+
"""Start continuous range mode
171+
:param period: Time delay between measurements in ms
172+
"""
173+
# Set range between measurements
174+
period_reg: int = 0
175+
if period > 10:
176+
if period < 2250:
177+
period_reg = (period // 10) - 1
178+
else:
179+
period_reg = 254
180+
self._write_8(_VL6180X_REG_SYSRANGE_INTERMEASUREMENT_PERIOD, period_reg)
181+
182+
# Start continuous range measurement
183+
self._write_8(_VL6180X_REG_SYSRANGE_START, 0x03)
184+
185+
def stop_range_continuous(self) -> None:
186+
"""Stop continuous range mode. It is advised to wait for about 0.3s
187+
afterwards to avoid issues with the interrupt flags"""
188+
if self.continuous_mode_enabled:
189+
self._write_8(_VL6180X_REG_SYSRANGE_START, 0x01)
190+
191+
@property
192+
def continuous_mode_enabled(self) -> bool:
193+
""" Checks if continuous mode is enabled """
194+
return self._read_8(_VL6180X_REG_SYSRANGE_START) > 1 & 0x1
122195

123196
@property
124197
def offset(self) -> int:
@@ -132,6 +205,28 @@ def offset(self, offset: int) -> None:
132205
)
133206
self._offset = offset
134207

208+
def _read_range_single(self) -> int:
209+
""" Read the range when in single-shot mode"""
210+
while not self._read_8(_VL6180X_REG_RESULT_RANGE_STATUS) & 0x01:
211+
pass
212+
self._write_8(_VL6180X_REG_SYSRANGE_START, 0x01)
213+
return self._read_range_continuous()
214+
215+
def _read_range_continuous(self) -> int:
216+
""" Read the range when in continuous mode"""
217+
218+
# Poll until bit 2 is set
219+
while not self._read_8(_VL6180X_REG_RESULT_INTERRUPT_STATUS_GPIO) & 0x04:
220+
pass
221+
222+
# read range in mm
223+
range_ = self._read_8(_VL6180X_REG_RESULT_RANGE_VAL)
224+
225+
# clear interrupt
226+
self._write_8(_VL6180X_REG_SYSTEM_INTERRUPT_CLEAR, 0x07)
227+
228+
return range_
229+
135230
def read_lux(self, gain: int) -> float:
136231
"""Read the lux (light value) from the sensor and return it. Must
137232
specify the gain value to use for the lux reading:

docs/examples.rst

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,48 @@
1-
Simple test
1+
Simple Test
22
------------
33

44
Ensure your device works with this simple test.
55

66
.. literalinclude:: ../examples/vl6180x_simpletest.py
77
:caption: examples/vl6180x_simpletest.py
88
:linenos:
9+
10+
11+
Calibration Test
12+
-----------------
13+
14+
Demo of calibrating the part to part range offset per Application Note 4545 for the VL6180X sensor.
15+
16+
.. literalinclude:: ../examples/vl6180x_calibrationtest.py
17+
:caption: examples/vl6180x_calibrationtest.py
18+
:linenos:
19+
20+
21+
Continuous Test
22+
----------------
23+
24+
Demo of reading the range from the VL6180x distance sensor in continuous mode.
25+
26+
.. literalinclude:: ../examples/vl6180x_continuoustest.py
27+
:caption: examples/vl6180x_continuoustest.py
28+
:linenos:
29+
30+
31+
History Test
32+
-------------
33+
34+
Demo of reading the range from the history buffer of the VL6180x distance sensor.
35+
36+
.. literalinclude:: ../examples/vl6180x_historytest.py
37+
:caption: examples/vl6180x_historytest.py
38+
:linenos:
39+
40+
41+
Performance Test
42+
-----------------
43+
44+
Demo of reading the range from the VL6180x distance sensor in different access modes (single shot, continuous, history).
45+
46+
.. literalinclude:: ../examples/vl6180x_performancetest.py
47+
:caption: examples/vl6180x_performancetest.py
48+
:linenos:

examples/vl6180x_calibrationtest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
sensor = adafruit_vl6180x.VL6180X(i2c, offset=0)
2020

2121
# Place a target at 50mm away from VL6180X Collect a number of range measurements
22-
# with the target in place and calculate mean of the range reseults. For a
22+
# with the target in place and calculate mean of the range results. For a
2323
# reliable measurement, take at least 10 measurements.
2424
measurements = []
2525
for msmt in range(10):

examples/vl6180x_continuoustest.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SPDX-FileCopyrightText: 2018 Jonas Schatz
2+
# SPDX-License-Identifier: MIT
3+
4+
# Demo of reading the range from the VL6180x distance sensor in
5+
# continuous mode
6+
7+
import time
8+
9+
import board
10+
import busio
11+
12+
import adafruit_vl6180x
13+
14+
15+
# Create I2C bus.
16+
i2c = busio.I2C(board.SCL, board.SDA)
17+
18+
# Create sensor instance.
19+
sensor = adafruit_vl6180x.VL6180X(i2c)
20+
21+
# Starting continuous mode
22+
print("Starting continuous mode")
23+
sensor.start_range_continuous(20)
24+
25+
# Main loop prints the range and lux every 0.01 seconds
26+
for _ in range(100):
27+
# Read the range in millimeters and print it.
28+
range_mm = sensor.range
29+
print("Range: {0}mm".format(range_mm))
30+
31+
# Delay for 10 ms
32+
time.sleep(0.01)
33+
34+
# Stop continuous mode. This is advised as the sensor
35+
# wouldn't stop measuring after the program has ended
36+
sensor.stop_range_continuous()

examples/vl6180x_historytest.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# SPDX-FileCopyrightText: 2022 Jonas Schatz
2+
# SPDX-License-Identifier: MIT
3+
4+
# Demo of reading the range from the history buffer of the VL6180x
5+
# distance sensor
6+
7+
import time
8+
9+
import board
10+
import busio
11+
12+
import adafruit_vl6180x
13+
14+
15+
# Create I2C bus.
16+
i2c = busio.I2C(board.SCL, board.SDA)
17+
18+
# Create sensor instance.
19+
sensor = adafruit_vl6180x.VL6180X(i2c)
20+
21+
# Starting continuous mode
22+
print("Starting continuous mode")
23+
sensor.start_range_continuous()
24+
25+
# Main loop prints the ranges every 0.01 seconds for about 5 seconds
26+
# You should see changes 'ripple through' the history array
27+
for _ in range(500):
28+
# Read the last 16 ranges from the history buffer as a List[int]
29+
ranges_mm = sensor.ranges_from_history
30+
print(ranges_mm)
31+
32+
# Delay for 10 ms so that the loop is not too fast
33+
time.sleep(0.01)
34+
35+
# Stop continuous mode. This is advised as the sensor
36+
# wouldn't stop measuring after the program has ended
37+
sensor.stop_range_continuous()

0 commit comments

Comments
 (0)