Skip to content

Commit 2203552

Browse files
authored
Merge pull request #21 from Smankusors/master
Implements continuous mode
2 parents 7411ab7 + b3a1601 commit 2203552

File tree

3 files changed

+234
-4
lines changed

3 files changed

+234
-4
lines changed

adafruit_vl53l0x.py

+89-4
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ class VL53L0X:
137137
# thread safe!
138138
_BUFFER = bytearray(3)
139139

140+
# Is VL53L0X is currently continuous mode? (Needed by `range` property)
141+
_continuous_mode = False
142+
140143
def __init__(self, i2c, address=41, io_timeout_s=0):
141144
# pylint: disable=too-many-statements
142145
self._i2c = i2c
@@ -515,11 +518,21 @@ def distance(self):
515518

516519
@property
517520
def range(self):
518-
"""Perform a single reading of the range for an object in front of
519-
the sensor and return the distance in millimeters.
521+
"""Perform a single (or continuous if `start_continuous` called)
522+
reading of the range for an object in front of the sensor and
523+
return the distance in millimeters.
524+
"""
525+
# Adapted from readRangeSingleMillimeters in pololu code at:
526+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
527+
if not self._continuous_mode:
528+
self.do_range_measurement()
529+
return self.read_range()
530+
531+
def do_range_measurement(self):
532+
"""Perform a single reading of the range for an object in front of the
533+
sensor, but without return the distance.
520534
"""
521-
# Adapted from readRangeSingleMillimeters &
522-
# readRangeContinuousMillimeters in pololu code at:
535+
# Adapted from readRangeSingleMillimeters in pololu code at:
523536
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
524537
for pair in (
525538
(0x80, 0x01),
@@ -539,6 +552,16 @@ def range(self):
539552
and (time.monotonic() - start) >= self.io_timeout_s
540553
):
541554
raise RuntimeError("Timeout waiting for VL53L0X!")
555+
556+
def read_range(self):
557+
"""Return a range reading in millimeters.
558+
559+
Note: Avoid calling this directly. If you do single mode, you need
560+
to call `do_range_measurement` first. Or your program will stuck or
561+
timeout occurred.
562+
"""
563+
# Adapted from readRangeContinuousMillimeters in pololu code at:
564+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
542565
start = time.monotonic()
543566
while (self._read_u8(_RESULT_INTERRUPT_STATUS) & 0x07) == 0:
544567
if (
@@ -552,6 +575,68 @@ def range(self):
552575
self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01)
553576
return range_mm
554577

578+
@property
579+
def is_continuous_mode(self):
580+
"""Is the sensor currently in continuous mode?"""
581+
return self._continuous_mode
582+
583+
def continuous_mode(self):
584+
"""Activate the continuous mode manager"""
585+
return self
586+
587+
def __enter__(self):
588+
"""For continuous mode manager, called when used on `with` keyword"""
589+
self.start_continuous()
590+
return self
591+
592+
def __exit__(self, exc_type, exc_value, traceback):
593+
"""For continuous mode manager, called at the end of `with` scope"""
594+
self.stop_continuous()
595+
596+
def start_continuous(self):
597+
"""Perform a continuous reading of the range for an object in front of
598+
the sensor.
599+
"""
600+
# Adapted from startContinuous in pololu code at:
601+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
602+
for pair in (
603+
(0x80, 0x01),
604+
(0xFF, 0x01),
605+
(0x00, 0x00),
606+
(0x91, self._stop_variable),
607+
(0x00, 0x01),
608+
(0xFF, 0x00),
609+
(0x80, 0x00),
610+
(_SYSRANGE_START, 0x02),
611+
):
612+
self._write_u8(pair[0], pair[1])
613+
start = time.monotonic()
614+
while (self._read_u8(_SYSRANGE_START) & 0x01) > 0:
615+
if (
616+
self.io_timeout_s > 0
617+
and (time.monotonic() - start) >= self.io_timeout_s
618+
):
619+
raise RuntimeError("Timeout waiting for VL53L0X!")
620+
self._continuous_mode = True
621+
622+
def stop_continuous(self):
623+
"""Stop continuous readings."""
624+
# Adapted from stopContinuous in pololu code at:
625+
# https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
626+
for pair in (
627+
(_SYSRANGE_START, 0x01),
628+
(0xFF, 0x01),
629+
(0x00, 0x00),
630+
(0x91, 0x00),
631+
(0x00, 0x01),
632+
(0xFF, 0x00),
633+
):
634+
self._write_u8(pair[0], pair[1])
635+
self._continuous_mode = False
636+
637+
# restore the sensor to single ranging mode
638+
self.do_range_measurement()
639+
555640
def set_address(self, new_address):
556641
"""Set a new I2C address to the instantaited object. This is only called when using
557642
multiple VL53L0X sensors on the same I2C bus (SDA & SCL pins). See also the
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# SPDX-FileCopyrightText: 2021 Smankusors for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
"""
5+
Example of how to use the adafruit_vl53l0x library to change the assigned address of
6+
multiple VL53L0X sensors on the same I2C bus. This example only focuses on 2 VL53L0X
7+
sensors, but can be modified for more. BE AWARE: a multitude of sensors may require
8+
more current than the on-board 3V regulator can output (typical current consumption during
9+
active range readings is about 19 mA per sensor).
10+
11+
This example like vl53l0x_multiple_sensors, but this with sensors in continuous mode.
12+
So you don't need to wait the sensor to do range measurement and return the distance
13+
for you.
14+
15+
For example, you have 2 VL53L0X sensors, with timing budget of 200ms, on single mode.
16+
When you want to get distance from sensor #1, sensor #2 will idle because waiting
17+
for sensor #1 completes the range measurement. You could do multithreading so you
18+
can ask both the sensor at the same time, but it's quite expensive.
19+
20+
When you use continuous mode, the sensor will always do range measurement after it
21+
completes. So when you want to get the distance from both of the device, you don't
22+
need to wait 400ms, just 200ms for both of the sensors.
23+
"""
24+
import time
25+
import board
26+
from digitalio import DigitalInOut
27+
from adafruit_vl53l0x import VL53L0X
28+
29+
# declare the singleton variable for the default I2C bus
30+
i2c = board.I2C()
31+
32+
# declare the digital output pins connected to the "SHDN" pin on each VL53L0X sensor
33+
xshut = [
34+
DigitalInOut(board.D17),
35+
DigitalInOut(board.D18),
36+
# add more VL53L0X sensors by defining their SHDN pins here
37+
]
38+
39+
for power_pin in xshut:
40+
# make sure these pins are a digital output, not a digital input
41+
power_pin.switch_to_output(value=False)
42+
# These pins are active when Low, meaning:
43+
# if the output signal is LOW, then the VL53L0X sensor is off.
44+
# if the output signal is HIGH, then the VL53L0X sensor is on.
45+
# all VL53L0X sensors are now off
46+
47+
# initialize a list to be used for the array of VL53L0X sensors
48+
vl53 = []
49+
50+
# now change the addresses of the VL53L0X sensors
51+
for i, power_pin in enumerate(xshut):
52+
# turn on the VL53L0X to allow hardware check
53+
power_pin.value = True
54+
# instantiate the VL53L0X sensor on the I2C bus & insert it into the "vl53" list
55+
vl53.insert(i, VL53L0X(i2c)) # also performs VL53L0X hardware check
56+
57+
# start continous mode
58+
vl53[i].start_continous()
59+
60+
# you will see the benefit of continous mode if you set the measurement timing
61+
# budget very high.
62+
# vl53[i].measurement_timing_budget = 2000000
63+
64+
# no need to change the address of the last VL53L0X sensor
65+
if i < len(xshut) - 1:
66+
# default address is 0x29. Change that to something else
67+
vl53[i].set_address(i + 0x30) # address assigned should NOT be already in use
68+
# there is a helpful list of pre-designated I2C addresses for various I2C devices at
69+
# https://learn.adafruit.com/i2c-addresses/the-list
70+
# According to this list 0x30-0x34 are available, although the list may be incomplete.
71+
# In the python REPR, you can scan for all I2C devices that are attached and detirmine
72+
# their addresses using:
73+
# >>> import board
74+
# >>> i2c = board.I2C()
75+
# >>> if i2c.try_lock():
76+
# >>> [hex(x) for x in i2c.scan()]
77+
# >>> i2c.unlock()
78+
79+
80+
def detect_range(count=5):
81+
""" take count=5 samples """
82+
while count:
83+
for index, sensor in enumerate(vl53):
84+
print("Sensor {} Range: {}mm".format(index + 1, sensor.range))
85+
time.sleep(1.0)
86+
count -= 1
87+
88+
89+
def stop_continuous():
90+
"""this is not required, if you use XSHUT to reset the sensor.
91+
unless if you want to save some energy
92+
"""
93+
for sensor in vl53:
94+
sensor.stop_continuous()
95+
96+
97+
if __name__ == "__main__":
98+
detect_range()
99+
stop_continuous()
100+
else:
101+
print(
102+
"Multiple VL53L0X sensors' addresses are assigned properly\n"
103+
"execute detect_range() to read each sensors range readings.\n"
104+
"When you are done with readings, execute stop_continuous()\n"
105+
"to stop the continuous mode."
106+
)

examples/vl53l0x_simplecontinuous.py

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# SPDX-FileCopyrightText: 2021 Smankusors for Adafruit Industries
2+
# SPDX-License-Identifier: MIT
3+
4+
# Simple demo of the VL53L0X distance sensor with continuous mode.
5+
# Will print the sensed range/distance as fast as possible.
6+
import time
7+
8+
import board
9+
import busio
10+
11+
import adafruit_vl53l0x
12+
13+
# Initialize I2C bus and sensor.
14+
i2c = busio.I2C(board.SCL, board.SDA)
15+
vl53 = adafruit_vl53l0x.VL53L0X(i2c)
16+
17+
# Optionally adjust the measurement timing budget to change speed and accuracy.
18+
# See the example here for more details:
19+
# https://github.com/pololu/vl53l0x-arduino/blob/master/examples/Single/Single.ino
20+
# For example a higher speed but less accurate timing budget of 20ms:
21+
# vl53.measurement_timing_budget = 20000
22+
# Or a slower but more accurate timing budget of 200ms:
23+
vl53.measurement_timing_budget = 200000
24+
# The default timing budget is 33ms, a good compromise of speed and accuracy.
25+
26+
# You will see the benefit of continous mode if you set the measurement timing
27+
# budget very high, while your program doing something else. When your program done
28+
# with something else, and the sensor already calculated the distance, the result
29+
# will return instantly, instead of waiting the sensor measuring first.
30+
31+
# Main loop will read the range and print it every second.
32+
with vl53.continuous_mode():
33+
while True:
34+
# try to adjust the sleep time (simulating program doing something else)
35+
# and see how fast the sensor returns the range
36+
time.sleep(0.1)
37+
38+
curTime = time.time()
39+
print("Range: {0}mm ({1:.2f}ms)".format(vl53.range, time.time() - curTime))

0 commit comments

Comments
 (0)