Skip to content

Commit d8495ad

Browse files
committed
implements continuous mode, with an example
1 parent 87b8438 commit d8495ad

File tree

2 files changed

+179
-4
lines changed

2 files changed

+179
-4
lines changed

adafruit_vl53l0x.py

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
https://github.com/adafruit/circuitpython/releases
4646
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
4747
"""
48+
import atexit
4849
import math
4950
import time
5051

@@ -156,6 +157,9 @@ class VL53L0X:
156157
# thread safe!
157158
_BUFFER = bytearray(3)
158159

160+
# Is VL53L0X is currently continuous mode? (Needed by `range` property)
161+
_continuous_mode = False
162+
159163
def __init__(self, i2c, address=41, io_timeout_s=0):
160164
# pylint: disable=too-many-statements
161165
self._device = i2c_device.I2CDevice(i2c, address)
@@ -317,6 +321,7 @@ def __init__(self, i2c, address=41, io_timeout_s=0):
317321
self._perform_single_ref_calibration(0x00)
318322
# "restore the previous Sequence Config"
319323
self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xE8)
324+
atexit.register(self._cleanup)
320325

321326
def _read_u8(self, address):
322327
# Read an 8-bit unsigned value from the specified 8-bit address.
@@ -453,6 +458,11 @@ def _get_sequence_step_timeouts(self, pre_range):
453458
pre_range_mclks,
454459
)
455460

461+
def _cleanup(self):
462+
#when exiting, don't forget to also turn off continuous mode
463+
if (self._continuous_mode):
464+
self.stopContinuous()
465+
456466
@property
457467
def signal_rate_limit(self):
458468
"""The signal rate limit in mega counts per second."""
@@ -527,11 +537,21 @@ def measurement_timing_budget(self, budget_us):
527537

528538
@property
529539
def range(self):
530-
"""Perform a single reading of the range for an object in front of
531-
the sensor and return the distance in millimeters.
540+
"""Perform a single (or continuous if `startContinuous` called)
541+
reading of the range for an object in front of the sensor and
542+
return the distance in millimeters.
543+
"""
544+
# Adapted from readRangeSingleMillimeters in pololu code at:
545+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
546+
if (not self._continuous_mode):
547+
self.doRangeMeasurement()
548+
return self.readRange()
549+
550+
def doRangeMeasurement(self):
551+
"""Perform a single reading of the range for an object in front of the
552+
sensor, but without return the distance.
532553
"""
533-
# Adapted from readRangeSingleMillimeters &
534-
# readRangeContinuousMillimeters in pololu code at:
554+
# Adapted from readRangeSingleMillimeters in pololu code at:
535555
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
536556
for pair in (
537557
(0x80, 0x01),
@@ -551,6 +571,16 @@ def range(self):
551571
and (time.monotonic() - start) >= self.io_timeout_s
552572
):
553573
raise RuntimeError("Timeout waiting for VL53L0X!")
574+
575+
def readRange(self):
576+
"""Return a range reading in millimeters.
577+
578+
Note: Avoid calling this directly. If you do single mode, you need
579+
to call doRangeMeasurement first. Or your program will stuck or
580+
timeout occurred.
581+
"""
582+
# Adapted from readRangeContinuousMillimeters in pololu code at:
583+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
554584
start = time.monotonic()
555585
while (self._read_u8(_RESULT_INTERRUPT_STATUS) & 0x07) == 0:
556586
if (
@@ -564,6 +594,59 @@ def range(self):
564594
self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01)
565595
return range_mm
566596

597+
@property
598+
def continuous_mode(self):
599+
return self._continuous_mode
600+
601+
@continuous_mode.setter
602+
def continuous_mode(self, enabled):
603+
if (enabled):
604+
self.startContinuous()
605+
else:
606+
self.stopContinuous()
607+
608+
def startContinuous(self):
609+
"""Perform a continuous reading of the range for an object in front of
610+
the sensor.
611+
"""
612+
# Adapted from startContinuous in pololu code at:
613+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
614+
for pair in (
615+
(0x80, 0x01),
616+
(0xFF, 0x01),
617+
(0x00, 0x00),
618+
(0x91, self._stop_variable),
619+
(0x00, 0x01),
620+
(0xFF, 0x00),
621+
(0x80, 0x00),
622+
(_SYSRANGE_START, 0x02),
623+
):
624+
self._write_u8(pair[0], pair[1])
625+
start = time.monotonic()
626+
while (self._read_u8(_SYSRANGE_START) & 0x01) > 0:
627+
if (
628+
self.io_timeout_s > 0
629+
and (time.monotonic() - start) >= self.io_timeout_s
630+
):
631+
raise RuntimeError("Timeout waiting for VL53L0X!")
632+
self._continuous_mode = True
633+
634+
def stopContinuous(self):
635+
"""Stop continuous readings.
636+
"""
637+
# Adapted from stopContinuous in pololu code at:
638+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
639+
for pair in (
640+
(_SYSRANGE_START, 0x01),
641+
(0xFF, 0x01),
642+
(0x00, 0x00),
643+
(0x91, 0x00),
644+
(0x00, 0x01),
645+
(0xFF, 0x00)
646+
):
647+
self._write_u8(pair[0], pair[1])
648+
self._continuous_mode = False
649+
567650
def set_address(self, new_address):
568651
"""Set a new I2C address to the instantaited object. This is only called when using
569652
multiple VL53L0X sensors on the same I2C bus (SDA & SCL pins). See also the

examples/vl53l0x_continuous.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Example of how to use the adafruit_vl53l0x library to change the assigned address of
3+
multiple VL53L0X sensors on the same I2C bus. This example only focuses on 2 VL53L0X
4+
sensors, but can be modified for more. BE AWARE: a multitude of sensors may require
5+
more current than the on-board 3V regulator can output (typical current consumption during
6+
active range readings is about 19 mA per sensor).
7+
8+
This example like vl53l0x_multiple_sensors, but this with sensors in continuous mode.
9+
So you don't need to wait the sensor to do range measurement and return the distance
10+
for you.
11+
12+
For example, you have 2 VL53L0X sensors, with timing budget of 200ms, on single mode.
13+
When you want to get distance from sensor #1, sensor #2 will idle because waiting
14+
for sensor #1 completes the range measurement. You could do multithreading so you
15+
can ask both the sensor at the same time, but it's quite expensive.
16+
17+
When you use continuous mode, the sensor will always do range measurement after it
18+
completes. So when you want to get the distance from both of the device, you don't
19+
need to wait 400ms, just 200ms for both of the sensors.
20+
"""
21+
import time
22+
import board
23+
from digitalio import DigitalInOut
24+
from adafruit_vl53l0x import VL53L0X
25+
26+
# declare the singleton variable for the default I2C bus
27+
i2c = board.I2C()
28+
29+
# declare the digital output pins connected to the "SHDN" pin on each VL53L0X sensor
30+
xshut = [
31+
DigitalInOut(board.D17),
32+
DigitalInOut(board.D18),
33+
# add more VL53L0X sensors by defining their SHDN pins here
34+
]
35+
36+
for power_pin in xshut:
37+
# make sure these pins are a digital output, not a digital input
38+
power_pin.switch_to_output(value=False)
39+
# These pins are active when Low, meaning:
40+
# if the output signal is LOW, then the VL53L0X sensor is off.
41+
# if the output signal is HIGH, then the VL53L0X sensor is on.
42+
# all VL53L0X sensors are now off
43+
44+
# initialize a list to be used for the array of VL53L0X sensors
45+
vl53 = []
46+
47+
# now change the addresses of the VL53L0X sensors
48+
for i, power_pin in enumerate(xshut):
49+
# turn on the VL53L0X to allow hardware check
50+
power_pin.value = True
51+
# instantiate the VL53L0X sensor on the I2C bus & insert it into the "vl53" list
52+
vl53.insert(i, VL53L0X(i2c)) # also performs VL53L0X hardware check
53+
54+
# start continous mode
55+
vl53[i].continuous_mode = True
56+
# or alternatively with this
57+
# vl53[i].startContinous()
58+
59+
# you will see the benefit of continous mode if you set the measurement timing
60+
# budget very high.
61+
#vl53[i].measurement_timing_budget = 2000000
62+
63+
# no need to change the address of the last VL53L0X sensor
64+
if i < len(xshut) - 1:
65+
# default address is 0x29. Change that to something else
66+
vl53[i].set_address(i + 0x30) # address assigned should NOT be already in use
67+
# there is a helpful list of pre-designated I2C addresses for various I2C devices at
68+
# https://learn.adafruit.com/i2c-addresses/the-list
69+
# According to this list 0x30-0x34 are available, although the list may be incomplete.
70+
# In the python REPR, you can scan for all I2C devices that are attached and detirmine
71+
# their addresses using:
72+
# >>> import board
73+
# >>> i2c = board.I2C()
74+
# >>> if i2c.try_lock():
75+
# >>> [hex(x) for x in i2c.scan()]
76+
# >>> i2c.unlock()
77+
78+
def detect_range(count=5):
79+
""" take count=5 samples """
80+
while count:
81+
for index, sensor in enumerate(vl53):
82+
print("Sensor {} Range: {}mm".format(index + 1, sensor.range))
83+
time.sleep(1.0)
84+
count -= 1
85+
86+
if __name__ == "__main__":
87+
detect_range()
88+
else:
89+
print(
90+
"Multiple VL53L0X sensors' addresses are assigned properly\n"
91+
"execute detect_range() to read each sensors range readings"
92+
)

0 commit comments

Comments
 (0)