Skip to content

Commit

Permalink
Version 1.0.0 - Add multiple device support and better error checking
Browse files Browse the repository at this point in the history
  • Loading branch information
johnbryanmoore committed Jan 30, 2017
1 parent 19f8462 commit 902a570
Show file tree
Hide file tree
Showing 6 changed files with 409 additions and 202 deletions.
23 changes: 19 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
# VL53L0X Python interface on Raspberry Pi

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

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

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.

(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).
Version 0.0.9:
- initial version and only supports 1 sensor with limited error checking.

Version 1.0.0:
- Add support for multiple sensors on the same bus utilizing the ST API call to change the address of the device.
- Add support for improved error checking such as I/O error detection on I2C access.

Notes on Multiple sensor support:
- 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.
- 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.
- Changes to the platform and python_lib c code allow for up to 16 sensors.
- Address changes are volatile so setting the shutdown pin low or removing power will change the address back to the default 0x29.

(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).


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

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

VL53L0X_example.py - This contains an example program that uses the interface.
VL53L0X_example.py - This contains an example that accesses a single sensor with the default address.

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.

Eventualy I will make this an installable extension.
Note: Eventualy I will make this an installable extension.

30 changes: 16 additions & 14 deletions platform/src/vl53l0x_platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,8 @@ static int (*i2c_read_func)(uint8_t address, uint8_t reg,
static int (*i2c_write_func)(uint8_t address, uint8_t reg,
uint8_t *list, uint8_t length) = NULL;

static VL53L0X_DEV current_Dev;

void VL53L0X_init(VL53L0X_DEV Dev)
{
current_Dev = Dev;
}

void VL53L0X_set_i2c(void *read_func, void *write_func)
Expand All @@ -50,19 +47,19 @@ void VL53L0X_set_i2c(void *read_func, void *write_func)
i2c_write_func = write_func;
}

VL53L0X_DEV VL53L0X_get_dev()
{
return current_Dev;
}

static int i2c_write(VL53L0X_DEV Dev, uint8_t cmd,
uint8_t *data, uint8_t len)
{
if (i2c_write_func != NULL)
{
i2c_write_func(Dev->I2cDevAddr, cmd, data, len);

return VL53L0X_ERROR_NONE;
if (i2c_write_func(Dev->I2cDevAddr, cmd, data, len) < 0)
{
return VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
return VL53L0X_ERROR_NONE;
}
}
else
{
Expand All @@ -76,9 +73,14 @@ static int i2c_read(VL53L0X_DEV Dev, uint8_t cmd,
{
if (i2c_read_func != NULL)
{
i2c_read_func(Dev->I2cDevAddr, cmd, data, len);

return VL53L0X_ERROR_NONE;
if (i2c_read_func(Dev->I2cDevAddr, cmd, data, len) < 0)
{
return VL53L0X_ERROR_CONTROL_INTERFACE;
}
else
{
return VL53L0X_ERROR_NONE;
}
}
else
{
Expand Down
47 changes: 29 additions & 18 deletions python/VL53L0X.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,36 @@

# i2c bus read callback
def i2c_read(address, reg, data_p, length):
result = i2cbus.read_i2c_block_data(address, reg, length)
ret_val = 0;
result = []

try:
result = i2cbus.read_i2c_block_data(address, reg, length)
except IOError:
ret_val = -1;

for index in range(length):
data_p[index] = result[index]
if (ret_val == 0):
for index in range(length):
data_p[index] = result[index]

return 0
return ret_val

# i2c bus write callback
def i2c_write(address, reg, data_p, length):
ret_val = 0;
data = []

for index in range(length):
data.append(data_p[index])
try:
i2cbus.write_i2c_block_data(address, reg, data)
except IOError:
ret_val = -1;

i2cbus.write_i2c_block_data(address, reg, data)

return 0
return ret_val

# Load VL53L0X shared lib
tof = CDLL("../bin/vl53l0x_python.so")
tof_lib = CDLL("../bin/vl53l0x_python.so")

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

# pass i2c read and write function pointers to VL53L0X library
tof.VL53L0X_set_i2c(read_func, write_func)
tof_lib.VL53L0X_set_i2c(read_func, write_func)

class VL53L0X(object):
"""VL53L0X ToF."""

device_address = 0x29
object_number = 0

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

def start_ranging(self, mode = VL53L0X_GOOD_ACCURACY_MODE):
"""Start VL53L0X ToF Sensor Ranging"""
tof.startRanging(mode)

tof_lib.startRanging(self.my_object_number, mode, self.device_address)
def stop_ranging(self):
"""Stop VL53L0X ToF Sensor Ranging"""
tof.stopRanging()
tof_lib.stopRanging(self.my_object_number)

def get_distance(self):
"""Get distance from VL53L0X ToF Sensor"""
return tof.getDistance()
return tof_lib.getDistance(self.my_object_number)

# This function included to show how to access the ST library directly
# from python instead of through the simplified interface
def get_timing(self):
Dev = POINTER(c_void_p)
Dev = tof.VL53L0X_get_dev()
Dev = tof_lib.getDev(self.my_object_number)
budget = c_uint(0)
budget_p = pointer(budget)
Status = tof.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(Dev, budget_p)
Status = tof_lib.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(Dev, budget_p)
if (Status == 0):
return (budget.value + 1000)
else:
Expand Down
11 changes: 6 additions & 5 deletions python/VL53L0X_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@
import time
import VL53L0X

# Create a VL53L0X object
tof = VL53L0X.VL53L0X()
cumul = 0
last = 0

# Start ranging
tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)

timing = tof.get_timing()
if (timing < 20000):
timing = 20000
print ("Timing %d ms" % (timing/1000))

for count in range(1,101):
distance = tof.get_distance()
if (distance > 0):
cumul += distance
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))
last = distance
print ("%d mm, %d cm, %d" % (distance, (distance/10), count))

time.sleep(timing/1000000.00)

tof.stop_ranging()
Expand Down
89 changes: 89 additions & 0 deletions python/VL53L0X_multi_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/python

# MIT License
#
# Copyright (c) 2017 John Bryan Moore
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import time
import VL53L0X
import RPi.GPIO as GPIO

# GPIO for Sensor 1 shutdown pin
sensor1_shutdown = 20
# GPIO for Sensor 2 shutdown pin
sensor2_shutdown = 16

GPIO.setwarnings(False)

# Setup GPIO for shutdown pins on each VL53L0X
GPIO.setmode(GPIO.BCM)
GPIO.setup(sensor1_shutdown, GPIO.OUT)
GPIO.setup(sensor2_shutdown, GPIO.OUT)

# Set all shutdown pins low to turn off each VL53L0X
GPIO.output(sensor1_shutdown, GPIO.LOW)
GPIO.output(sensor2_shutdown, GPIO.LOW)

# Keep all low for 500 ms or so to make sure they reset
time.sleep(0.50)

# Create one object per VL53L0X passing the address to give to
# each.
tof = VL53L0X.VL53L0X(address=0x2B)
tof1 = VL53L0X.VL53L0X(address=0x2D)

# Set shutdown pin high for the first VL53L0X then
# call to start ranging
GPIO.output(sensor1_shutdown, GPIO.HIGH)
time.sleep(0.50)
tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)

# Set shutdown pin high for the second VL53L0X then
# call to start ranging
GPIO.output(sensor2_shutdown, GPIO.HIGH)
time.sleep(0.50)
tof1.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE)

timing = tof.get_timing()
if (timing < 20000):
timing = 20000
print ("Timing %d ms" % (timing/1000))

for count in range(1,101):
distance = tof.get_distance()
if (distance > 0):
print ("sensor %d - %d mm, %d cm, iteration %d" % (tof.my_object_number, distance, (distance/10), count))
else:
print ("%d - Error" % tof.my_object_number)

distance = tof1.get_distance()
if (distance > 0):
print ("sensor %d - %d mm, %d cm, iteration %d" % (tof1.my_object_number, distance, (distance/10), count))
else:
print ("%d - Error" % tof.my_object_number)

time.sleep(timing/1000000.00)

tof1.stop_ranging()
GPIO.output(sensor2_shutdown, GPIO.LOW)
tof.stop_ranging()
GPIO.output(sensor1_shutdown, GPIO.LOW)

Loading

0 comments on commit 902a570

Please sign in to comment.