Skip to content

Commit 902a570

Browse files
Version 1.0.0 - Add multiple device support and better error checking
1 parent 19f8462 commit 902a570

6 files changed

Lines changed: 409 additions & 202 deletions

File tree

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
11
# VL53L0X Python interface on Raspberry Pi
22

3-
This project provides a limited python interface on Raspberry Pi to the VL53L0X API (ST Microelectronics).
3+
This project provides a simplified python interface on Raspberry Pi to the ST VL53L0X API (ST Microelectronics).
44

55
Patterned after the cassou/VL53L0X_rasp repository (https://github.com/cassou/VL53L0X_rasp.git)
66

77
In order to be able to share the i2c bus with other python code that uses the i2c bus, this library implements the VL53L0X platform specific i2c functions through callbacks to the python smbus interface.
88

9-
(Please note that while the author is an embedded software engineer, this is a first attempt at extending python and the author only started learning python less than 2 months ago so any improvement suggestions are appreciated).
9+
Version 0.0.9:
10+
- initial version and only supports 1 sensor with limited error checking.
11+
12+
Version 1.0.0:
13+
- Add support for multiple sensors on the same bus utilizing the ST API call to change the address of the device.
14+
- Add support for improved error checking such as I/O error detection on I2C access.
15+
16+
Notes on Multiple sensor support:
17+
- In order to have multiple sensors on the same bus, you must have the shutdown pin of each sensor tied to individual GPIO's so that they can be individually enabled and the addresses set.
18+
- Both the Adafruit and Pololu boards for VL53L0X have I2C pull ups on the board. Because of this, the number of boards that can be added will be limited to only about 5 or 6 before the pull-up becomes too strong.
19+
- Changes to the platform and python_lib c code allow for up to 16 sensors.
20+
- Address changes are volatile so setting the shutdown pin low or removing power will change the address back to the default 0x29.
21+
22+
(Please note that while the author is an embedded software engineer, this is a first attempt at extending python and the author is by no means a python expert so any improvement suggestions are appreciated).
1023

1124

1225
### Installation
@@ -23,7 +36,9 @@ API_DIR=path/to/the/api/dir make
2336

2437
VL53L0X.py - This contains the python ctypes interface to the ST Library
2538

26-
VL53L0X_example.py - This contains an example program that uses the interface.
39+
VL53L0X_example.py - This contains an example that accesses a single sensor with the default address.
40+
41+
VL53L0X_multi_example.py - This contains an example that accesses 2 sensors, setting the first to address 0x2B an the second to address 0x2D. It uses GPIOs 20 and 16 connected to the shutdown pins on the 2 sensors to control sensor activation.
2742

28-
Eventualy I will make this an installable extension.
43+
Note: Eventualy I will make this an installable extension.
2944

platform/src/vl53l0x_platform.c

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,8 @@ static int (*i2c_read_func)(uint8_t address, uint8_t reg,
3737
static int (*i2c_write_func)(uint8_t address, uint8_t reg,
3838
uint8_t *list, uint8_t length) = NULL;
3939

40-
static VL53L0X_DEV current_Dev;
41-
4240
void VL53L0X_init(VL53L0X_DEV Dev)
4341
{
44-
current_Dev = Dev;
4542
}
4643

4744
void VL53L0X_set_i2c(void *read_func, void *write_func)
@@ -50,19 +47,19 @@ void VL53L0X_set_i2c(void *read_func, void *write_func)
5047
i2c_write_func = write_func;
5148
}
5249

53-
VL53L0X_DEV VL53L0X_get_dev()
54-
{
55-
return current_Dev;
56-
}
57-
5850
static int i2c_write(VL53L0X_DEV Dev, uint8_t cmd,
5951
uint8_t *data, uint8_t len)
6052
{
6153
if (i2c_write_func != NULL)
6254
{
63-
i2c_write_func(Dev->I2cDevAddr, cmd, data, len);
64-
65-
return VL53L0X_ERROR_NONE;
55+
if (i2c_write_func(Dev->I2cDevAddr, cmd, data, len) < 0)
56+
{
57+
return VL53L0X_ERROR_CONTROL_INTERFACE;
58+
}
59+
else
60+
{
61+
return VL53L0X_ERROR_NONE;
62+
}
6663
}
6764
else
6865
{
@@ -76,9 +73,14 @@ static int i2c_read(VL53L0X_DEV Dev, uint8_t cmd,
7673
{
7774
if (i2c_read_func != NULL)
7875
{
79-
i2c_read_func(Dev->I2cDevAddr, cmd, data, len);
80-
81-
return VL53L0X_ERROR_NONE;
76+
if (i2c_read_func(Dev->I2cDevAddr, cmd, data, len) < 0)
77+
{
78+
return VL53L0X_ERROR_CONTROL_INTERFACE;
79+
}
80+
else
81+
{
82+
return VL53L0X_ERROR_NONE;
83+
}
8284
}
8385
else
8486
{

python/VL53L0X.py

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,36 @@
3636

3737
# i2c bus read callback
3838
def i2c_read(address, reg, data_p, length):
39-
result = i2cbus.read_i2c_block_data(address, reg, length)
39+
ret_val = 0;
40+
result = []
41+
42+
try:
43+
result = i2cbus.read_i2c_block_data(address, reg, length)
44+
except IOError:
45+
ret_val = -1;
4046

41-
for index in range(length):
42-
data_p[index] = result[index]
47+
if (ret_val == 0):
48+
for index in range(length):
49+
data_p[index] = result[index]
4350

44-
return 0
51+
return ret_val
4552

4653
# i2c bus write callback
4754
def i2c_write(address, reg, data_p, length):
55+
ret_val = 0;
4856
data = []
57+
4958
for index in range(length):
5059
data.append(data_p[index])
60+
try:
61+
i2cbus.write_i2c_block_data(address, reg, data)
62+
except IOError:
63+
ret_val = -1;
5164

52-
i2cbus.write_i2c_block_data(address, reg, data)
53-
54-
return 0
65+
return ret_val
5566

5667
# Load VL53L0X shared lib
57-
tof = CDLL("../bin/vl53l0x_python.so")
68+
tof_lib = CDLL("../bin/vl53l0x_python.so")
5869

5970
# Create read function pointer
6071
READFUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte)
@@ -65,39 +76,39 @@ def i2c_write(address, reg, data_p, length):
6576
write_func = WRITEFUNC(i2c_write)
6677

6778
# pass i2c read and write function pointers to VL53L0X library
68-
tof.VL53L0X_set_i2c(read_func, write_func)
79+
tof_lib.VL53L0X_set_i2c(read_func, write_func)
6980

7081
class VL53L0X(object):
7182
"""VL53L0X ToF."""
7283

73-
device_address = 0x29
84+
object_number = 0
7485

7586
def __init__(self, address=0x29, **kwargs):
7687
"""Initialize the VL53L0X ToF Sensor from ST"""
7788
self.device_address = address
78-
# If address passed is not 0x29, must change address
79-
# in the device itself
89+
self.my_object_number = VL53L0X.object_number
90+
VL53L0X.object_number += 1
8091

8192
def start_ranging(self, mode = VL53L0X_GOOD_ACCURACY_MODE):
8293
"""Start VL53L0X ToF Sensor Ranging"""
83-
tof.startRanging(mode)
84-
94+
tof_lib.startRanging(self.my_object_number, mode, self.device_address)
95+
8596
def stop_ranging(self):
8697
"""Stop VL53L0X ToF Sensor Ranging"""
87-
tof.stopRanging()
98+
tof_lib.stopRanging(self.my_object_number)
8899

89100
def get_distance(self):
90101
"""Get distance from VL53L0X ToF Sensor"""
91-
return tof.getDistance()
102+
return tof_lib.getDistance(self.my_object_number)
92103

93104
# This function included to show how to access the ST library directly
94105
# from python instead of through the simplified interface
95106
def get_timing(self):
96107
Dev = POINTER(c_void_p)
97-
Dev = tof.VL53L0X_get_dev()
108+
Dev = tof_lib.getDev(self.my_object_number)
98109
budget = c_uint(0)
99110
budget_p = pointer(budget)
100-
Status = tof.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(Dev, budget_p)
111+
Status = tof_lib.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(Dev, budget_p)
101112
if (Status == 0):
102113
return (budget.value + 1000)
103114
else:

python/VL53L0X_example.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,22 @@
2525
import time
2626
import VL53L0X
2727

28+
# Create a VL53L0X object
2829
tof = VL53L0X.VL53L0X()
29-
cumul = 0
30-
last = 0
3130

31+
# Start ranging
3232
tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)
3333

3434
timing = tof.get_timing()
35+
if (timing < 20000):
36+
timing = 20000
3537
print ("Timing %d ms" % (timing/1000))
3638

3739
for count in range(1,101):
3840
distance = tof.get_distance()
3941
if (distance > 0):
40-
cumul += distance
41-
print ("%d mm, %d cm, %.2f avg, %d diff, %.2f from avg, %d count" % (distance, (distance/10), (float(cumul)/count), (last - distance), (float(cumul)/count)-distance, count))
42-
last = distance
42+
print ("%d mm, %d cm, %d" % (distance, (distance/10), count))
43+
4344
time.sleep(timing/1000000.00)
4445

4546
tof.stop_ranging()

python/VL53L0X_multi_example.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/python
2+
3+
# MIT License
4+
#
5+
# Copyright (c) 2017 John Bryan Moore
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
# SOFTWARE.
24+
25+
import time
26+
import VL53L0X
27+
import RPi.GPIO as GPIO
28+
29+
# GPIO for Sensor 1 shutdown pin
30+
sensor1_shutdown = 20
31+
# GPIO for Sensor 2 shutdown pin
32+
sensor2_shutdown = 16
33+
34+
GPIO.setwarnings(False)
35+
36+
# Setup GPIO for shutdown pins on each VL53L0X
37+
GPIO.setmode(GPIO.BCM)
38+
GPIO.setup(sensor1_shutdown, GPIO.OUT)
39+
GPIO.setup(sensor2_shutdown, GPIO.OUT)
40+
41+
# Set all shutdown pins low to turn off each VL53L0X
42+
GPIO.output(sensor1_shutdown, GPIO.LOW)
43+
GPIO.output(sensor2_shutdown, GPIO.LOW)
44+
45+
# Keep all low for 500 ms or so to make sure they reset
46+
time.sleep(0.50)
47+
48+
# Create one object per VL53L0X passing the address to give to
49+
# each.
50+
tof = VL53L0X.VL53L0X(address=0x2B)
51+
tof1 = VL53L0X.VL53L0X(address=0x2D)
52+
53+
# Set shutdown pin high for the first VL53L0X then
54+
# call to start ranging
55+
GPIO.output(sensor1_shutdown, GPIO.HIGH)
56+
time.sleep(0.50)
57+
tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)
58+
59+
# Set shutdown pin high for the second VL53L0X then
60+
# call to start ranging
61+
GPIO.output(sensor2_shutdown, GPIO.HIGH)
62+
time.sleep(0.50)
63+
tof1.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)
64+
65+
timing = tof.get_timing()
66+
if (timing < 20000):
67+
timing = 20000
68+
print ("Timing %d ms" % (timing/1000))
69+
70+
for count in range(1,101):
71+
distance = tof.get_distance()
72+
if (distance > 0):
73+
print ("sensor %d - %d mm, %d cm, iteration %d" % (tof.my_object_number, distance, (distance/10), count))
74+
else:
75+
print ("%d - Error" % tof.my_object_number)
76+
77+
distance = tof1.get_distance()
78+
if (distance > 0):
79+
print ("sensor %d - %d mm, %d cm, iteration %d" % (tof1.my_object_number, distance, (distance/10), count))
80+
else:
81+
print ("%d - Error" % tof.my_object_number)
82+
83+
time.sleep(timing/1000000.00)
84+
85+
tof1.stop_ranging()
86+
GPIO.output(sensor2_shutdown, GPIO.LOW)
87+
tof.stop_ranging()
88+
GPIO.output(sensor1_shutdown, GPIO.LOW)
89+

0 commit comments

Comments
 (0)