diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0c2994a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build +obj +bin +*.pyc +.idea diff --git a/README.md b/README.md index f7fb983..7e3f7a6 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,18 @@ Notes on Multiple sensor support: Notes on using TCA9548A I2C Multiplexer: - If limited on GPIO's that would be needed to set a new addresses for each sensor, using a TCA9548A I2C Multiplexer is a good option since it allows using up to 8 sensors without using GPIO's. - The TCA9548A is also a good option if using multiple boards on the same I2C bus and the total of all the combined I2C pullups would cause the bus not to function. -- Theoretically you can connect mutltiple TCA9548A Multiplexers, each with up to 8 sensors as long each TCA9548A has a different address. This has not been tested but should work in theory. +- Theoretically you can connect multiple TCA9548A Multiplexers, each with up to 8 sensors as long each TCA9548A has a different address. This has not been tested but should work in theory. (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 - +```bash +# Python2 +pip2 install git+https://github.com/grantramsay/VL53L0X_rasp_python.git +# Python3 +pip3 install git+https://github.com/grantramsay/VL53L0X_rasp_python.git +``` ### Compilation @@ -49,7 +54,7 @@ sudo apt-get install build-essential python-dev Then use following commands to clone the repository and compile: ```bash cd your_git_directory -git clone https://github.com/johnbryanmoore/VL53L0X_rasp_python.git +git clone https://github.com/grantramsay/VL53L0X_rasp_python.git cd VL53L0X_rasp_python make ``` @@ -64,9 +69,9 @@ VL53L0X_example_livegraph.py - This example plots the distance data from a singl VL53L0X_multi_example.py - This example accesses 2 sensors, setting the first to address 0x2B and the second to address 0x2D. It uses GPIOs 20 and 16 connected to the shutdown pins on the 2 sensors to control sensor activation. -![VL53L0X_multi_example.py Diagram](https://raw.githubusercontent.com/johnbryanmoore/VL53L0X_rasp_python/master/VL53L0X_Mutli_Rpi3_bb.jpg "Fritzing Diagram for VL53L0X_multi_example.py") +![VL53L0X_multi_example.py Diagram](https://raw.githubusercontent.com/grantramsay/VL53L0X_rasp_python/master/VL53L0X_Mutli_Rpi3_bb.jpg "Fritzing Diagram for VL53L0X_multi_example.py") VL53L0X_TCA9548A_example.py - This example accesses 2 sensors through a TCA9548A I2C Multiplexer with the first connected to bus 1 and the second on bus 2 on the TCA9548A. -![VL53L0X_TCA9548A_example.py Diagram](https://raw.githubusercontent.com/johnbryanmoore/VL53L0X_rasp_python/master/VL53L0X_TCA9548A_Rpi3_bb.jpg "Fritzing Diagram for VL53L0X_TCA9548A_example.py") +![VL53L0X_TCA9548A_example.py Diagram](https://raw.githubusercontent.com/grantramsay/VL53L0X_rasp_python/master/VL53L0X_TCA9548A_Rpi3_bb.jpg "Fritzing Diagram for VL53L0X_TCA9548A_example.py") diff --git a/python/VL53L0X.py b/python/VL53L0X.py index 07c35ab..c2f21f2 100755 --- a/python/VL53L0X.py +++ b/python/VL53L0X.py @@ -21,97 +21,169 @@ # 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 os +from ctypes import (CDLL, CFUNCTYPE, POINTER, + c_int, pointer, c_ubyte, c_uint8, c_uint32) +import smbus2 as smbus -import time -from ctypes import * -import smbus +THIS_FILE_DIR = os.path.dirname(os.path.abspath(__file__)) -VL53L0X_GOOD_ACCURACY_MODE = 0 # Good Accuracy mode -VL53L0X_BETTER_ACCURACY_MODE = 1 # Better Accuracy mode -VL53L0X_BEST_ACCURACY_MODE = 2 # Best Accuracy mode -VL53L0X_LONG_RANGE_MODE = 3 # Longe Range mode -VL53L0X_HIGH_SPEED_MODE = 4 # High Speed mode -i2cbus = smbus.SMBus(1) +class Vl53l0xError(RuntimeError): + pass -# i2c bus read callback -def i2c_read(address, reg, data_p, length): - ret_val = 0; - result = [] - - try: - result = i2cbus.read_i2c_block_data(address, reg, length) - except IOError: - ret_val = -1; - if (ret_val == 0): - for index in range(length): - data_p[index] = result[index] +class Vl53l0xAccuracyMode: + GOOD = 0 # 33 ms timing budget 1.2m range + BETTER = 1 # 66 ms timing budget 1.2m range + BEST = 2 # 200 ms 1.2m range + LONG_RANGE = 3 # 33 ms timing budget 2m range + HIGH_SPEED = 4 # 20 ms timing budget 1.2m range - return ret_val -# i2c bus write callback -def i2c_write(address, reg, data_p, length): - ret_val = 0; - data = [] +class Vl53l0xDeviceMode: + SINGLE_RANGING = 0 + CONTINUOUS_RANGING = 1 + SINGLE_HISTOGRAM = 2 + CONTINUOUS_TIMED_RANGING = 3 + SINGLE_ALS = 10 + GPIO_DRIVE = 20 + GPIO_OSC = 21 - for index in range(length): - data.append(data_p[index]) - try: - i2cbus.write_i2c_block_data(address, reg, data) - except IOError: - ret_val = -1; - return ret_val +class Vl53l0xGpioAlarmType: + OFF = 0 + THRESHOLD_CROSSED_LOW = 1 + THRESHOLD_CROSSED_HIGH = 2 + THRESHOLD_CROSSED_OUT = 3 + NEW_MEASUREMENT_READY = 4 -# Load VL53L0X shared lib -tof_lib = CDLL("../bin/vl53l0x_python.so") -# Create read function pointer -READFUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) -read_func = READFUNC(i2c_read) +class Vl53l0xInterruptPolarity: + LOW = 0 + HIGH = 1 -# Create write function pointer -WRITEFUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) -write_func = WRITEFUNC(i2c_write) -# pass i2c read and write function pointers to VL53L0X library -tof_lib.VL53L0X_set_i2c(read_func, write_func) +# Read/write function pointer types. +_I2C_READ_FUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) +_I2C_WRITE_FUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) -class VL53L0X(object): - """VL53L0X ToF.""" +# Load VL53L0X shared lib +# If built locally it will be in bin folder, if installed it will be next to the this file. +_POSSIBLE_LIBRARY_LOCATIONS = [THIS_FILE_DIR, os.path.join(THIS_FILE_DIR, '..', 'bin')] +for lib_location in _POSSIBLE_LIBRARY_LOCATIONS: + tof_lib = os.path.join(lib_location, "vl53l0x_python.so") + if os.path.isfile(tof_lib): + _TOF_LIBRARY = CDLL(tof_lib) + break +else: + raise FileNotFoundError('Could not find vl53l0x_python.so (have you built it?)') - object_number = 0 - def __init__(self, address=0x29, TCA9548A_Num=255, TCA9548A_Addr=0, **kwargs): +class VL53L0X: + """VL53L0X ToF.""" + def __init__(self, i2c_bus=1, i2c_address=0x29, tca9548a_num=255, tca9548a_addr=0): """Initialize the VL53L0X ToF Sensor from ST""" - self.device_address = address - self.TCA9548A_Device = TCA9548A_Num - self.TCA9548A_Address = TCA9548A_Addr - self.my_object_number = VL53L0X.object_number - VL53L0X.object_number += 1 - - def start_ranging(self, mode = VL53L0X_GOOD_ACCURACY_MODE): + self._i2c_bus = i2c_bus + self.i2c_address = i2c_address + self._tca9548a_num = tca9548a_num + self._tca9548a_addr = tca9548a_addr + self._i2c = smbus.SMBus() + self._dev = None + + def open(self): + self._i2c.open(bus=self._i2c_bus) + self._configure_i2c_library_functions() + self._dev = _TOF_LIBRARY.initialise(self.i2c_address, self._tca9548a_num, self._tca9548a_addr) + + def close(self): + self._i2c.close() + self._dev = None + + def _configure_i2c_library_functions(self): + # I2C bus read callback for low level library. + def _i2c_read(address, reg, data_p, length): + ret_val = 0 + result = [] + + try: + result = self._i2c.read_i2c_block_data(address, reg, length) + except IOError: + ret_val = -1 + + if ret_val == 0: + for index in range(length): + data_p[index] = result[index] + + return ret_val + + # I2C bus write callback for low level library. + def _i2c_write(address, reg, data_p, length): + ret_val = 0 + data = [] + + for index in range(length): + data.append(data_p[index]) + try: + self._i2c.write_i2c_block_data(address, reg, data) + except IOError: + ret_val = -1 + + return ret_val + + # Pass i2c read/write function pointers to VL53L0X library. + self._i2c_read_func = _I2C_READ_FUNC(_i2c_read) + self._i2c_write_func = _I2C_WRITE_FUNC(_i2c_write) + _TOF_LIBRARY.VL53L0X_set_i2c(self._i2c_read_func, self._i2c_write_func) + + def start_ranging(self, mode=Vl53l0xAccuracyMode.GOOD): """Start VL53L0X ToF Sensor Ranging""" - tof_lib.startRanging(self.my_object_number, mode, self.device_address, self.TCA9548A_Device, self.TCA9548A_Address) - + _TOF_LIBRARY.startRanging(self._dev, mode) + def stop_ranging(self): """Stop VL53L0X ToF Sensor Ranging""" - tof_lib.stopRanging(self.my_object_number) + _TOF_LIBRARY.stopRanging(self._dev) def get_distance(self): """Get distance from VL53L0X ToF Sensor""" - return tof_lib.getDistance(self.my_object_number) + return _TOF_LIBRARY.getDistance(self._dev) # 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_lib.getDev(self.my_object_number) - budget = c_uint(0) + budget = c_uint32(0) budget_p = pointer(budget) - Status = tof_lib.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(Dev, budget_p) - if (Status == 0): - return (budget.value + 1000) + status = _TOF_LIBRARY.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(self._dev, budget_p) + if status == 0: + return budget.value + 1000 else: return 0 + + def configure_gpio_interrupt( + self, proximity_alarm_type=Vl53l0xGpioAlarmType.THRESHOLD_CROSSED_LOW, + interrupt_polarity=Vl53l0xInterruptPolarity.HIGH, threshold_low_mm=250, threshold_high_mm=500): + """ + Configures a GPIO interrupt from device, be sure to call "clear_interrupt" after interrupt is processed. + """ + pin = c_uint8(0) # 0 is only GPIO pin. + device_mode = c_uint8(Vl53l0xDeviceMode.CONTINUOUS_RANGING) + functionality = c_uint8(proximity_alarm_type) + polarity = c_uint8(interrupt_polarity) + status = _TOF_LIBRARY.VL53L0X_SetGpioConfig(self._dev, pin, device_mode, functionality, polarity) + if status != 0: + raise Vl53l0xError('Error setting VL53L0X GPIO config') + + threshold_low = c_uint32(threshold_low_mm << 16) + threshold_high = c_uint32(threshold_high_mm << 16) + status = _TOF_LIBRARY.VL53L0X_SetInterruptThresholds(self._dev, device_mode, threshold_low, threshold_high) + if status != 0: + raise Vl53l0xError('Error setting VL53L0X thresholds') + + # Ensure any pending interrupts are cleared. + self.clear_interrupt() + + def clear_interrupt(self): + mask = c_uint32(0) + status = _TOF_LIBRARY.VL53L0X_ClearInterruptMask(self._dev, mask) + if status != 0: + raise Vl53l0xError('Error clearing VL53L0X interrupt') diff --git a/python/VL53L0X_TCA9548A_example.py b/python/VL53L0X_TCA9548A_example.py index ece11ea..550dfb8 100755 --- a/python/VL53L0X_TCA9548A_example.py +++ b/python/VL53L0X_TCA9548A_example.py @@ -26,33 +26,37 @@ import VL53L0X # Create a VL53L0X object for device on TCA9548A bus 1 -tof1 = VL53L0X.VL53L0X(TCA9548A_Num=1, TCA9548A_Addr=0x70) +tof1 = VL53L0X.VL53L0X(tca9548a_num=1, tca9548a_addr=0x70) # Create a VL53L0X object for device on TCA9548A bus 2 -tof2 = VL53L0X.VL53L0X(TCA9548A_Num=2, TCA9548A_Addr=0x70) +tof2 = VL53L0X.VL53L0X(tca9548a_num=2, tca9548a_addr=0x70) +tof1.open() +tof2.open() # Start ranging on TCA9548A bus 1 -tof1.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE) +tof1.start_ranging(VL53L0X.Vl53l0xAccuracyMode.BETTER) # Start ranging on TCA9548A bus 2 -tof2.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE) +tof2.start_ranging(VL53L0X.Vl53l0xAccuracyMode.BETTER) timing = tof1.get_timing() -if (timing < 20000): +if timing < 20000: timing = 20000 -print ("Timing %d ms" % (timing/1000)) +print("Timing %d ms" % (timing/1000)) -for count in range(1,101): +for count in range(1, 101): # Get distance from VL53L0X on TCA9548A bus 1 distance = tof1.get_distance() - if (distance > 0): - print ("1: %d mm, %d cm, %d" % (distance, (distance/10), count)) + if distance > 0: + print("1: %d mm, %d cm, %d" % (distance, (distance/10), count)) # Get distance from VL53L0X on TCA9548A bus 2 distance = tof2.get_distance() - if (distance > 0): - print ("2: %d mm, %d cm, %d" % (distance, (distance/10), count)) + if distance > 0: + print("2: %d mm, %d cm, %d" % (distance, (distance/10), count)) time.sleep(timing/1000000.00) tof1.stop_ranging() tof2.stop_ranging() +tof1.close() +tof2.close() diff --git a/python/VL53L0X_example.py b/python/VL53L0X_example.py index 50631f5..6f99ec2 100755 --- a/python/VL53L0X_example.py +++ b/python/VL53L0X_example.py @@ -27,21 +27,22 @@ # Create a VL53L0X object tof = VL53L0X.VL53L0X() - +tof.open() # Start ranging -tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE) +tof.start_ranging(VL53L0X.Vl53l0xAccuracyMode.BETTER) timing = tof.get_timing() -if (timing < 20000): +if timing < 20000: timing = 20000 -print ("Timing %d ms" % (timing/1000)) +print("Timing %d ms" % (timing/1000)) -for count in range(1,101): +for count in range(1, 101): distance = tof.get_distance() - if (distance > 0): - print ("%d mm, %d cm, %d" % (distance, (distance/10), count)) + if distance > 0: + print("%d mm, %d cm, %d" % (distance, (distance/10), count)) time.sleep(timing/1000000.00) tof.stop_ranging() +tof.close() diff --git a/python/VL53L0X_example_livegraph.py b/python/VL53L0X_example_livegraph.py index 9194365..1072442 100755 --- a/python/VL53L0X_example_livegraph.py +++ b/python/VL53L0X_example_livegraph.py @@ -28,12 +28,13 @@ import VL53L0X fig = plt.figure() -ax1 = fig.add_subplot(1,1,1) +ax1 = fig.add_subplot(1, 1, 1) xarr = [] yarr = [] count = 0 + def animate(i): global count distance = tof.get_distance() @@ -42,22 +43,24 @@ def animate(i): yarr.append(distance) time.sleep(timing/1000000.00) ax1.clear() - ax1.plot(xarr,yarr) + ax1.plot(xarr, yarr) + # Create a VL53L0X object tof = VL53L0X.VL53L0X() - +tof.open() # Start ranging -tof.start_ranging(VL53L0X.VL53L0X_BETTER_ACCURACY_MODE) +tof.start_ranging(VL53L0X.Vl53l0xAccuracyMode.BETTER) timing = tof.get_timing() -if (timing < 20000): +if timing < 20000: timing = 20000 -print ("Timing %d ms" % (timing/1000)) +print("Timing %d ms" % (timing/1000)) -print ("Press ctrl-c to exit") +print("Press ctrl-c to exit") ani = animation.FuncAnimation(fig, animate, interval=100) plt.show() tof.stop_ranging() +tof.close() diff --git a/python/VL53L0X_multi_example.py b/python/VL53L0X_multi_example.py index 4f44e42..436beea 100755 --- a/python/VL53L0X_multi_example.py +++ b/python/VL53L0X_multi_example.py @@ -47,38 +47,40 @@ # Create one object per VL53L0X passing the address to give to # each. -tof = VL53L0X.VL53L0X(address=0x2B) -tof1 = VL53L0X.VL53L0X(address=0x2D) +tof = VL53L0X.VL53L0X(i2c_address=0x2B) +tof1 = VL53L0X.VL53L0X(i2c_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) +tof.open() +tof.start_ranging(VL53L0X.Vl53l0xAccuracyMode.BETTER) # 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) +tof1.open() +tof1.start_ranging(VL53L0X.Vl53l0xAccuracyMode.BETTER) timing = tof.get_timing() -if (timing < 20000): +if timing < 20000: timing = 20000 -print ("Timing %d ms" % (timing/1000)) +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)) + if distance > 0: + print("sensor %d - %d mm, %d cm, iteration %d" % (1, distance, (distance/10), count)) else: - print ("%d - Error" % tof.my_object_number) + print("%d - Error" % 1) distance = tof1.get_distance() - if (distance > 0): - print ("sensor %d - %d mm, %d cm, iteration %d" % (tof1.my_object_number, distance, (distance/10), count)) + if distance > 0: + print("sensor %d - %d mm, %d cm, iteration %d" % (2, distance, (distance/10), count)) else: - print ("%d - Error" % tof.my_object_number) + print("%d - Error" % 2) time.sleep(timing/1000000.00) @@ -87,3 +89,5 @@ tof.stop_ranging() GPIO.output(sensor1_shutdown, GPIO.LOW) +tof.close() +tof1.close() diff --git a/python_lib/vl53l0x_python.c b/python_lib/vl53l0x_python.c index 20495aa..bd0e9d6 100644 --- a/python_lib/vl53l0x_python.c +++ b/python_lib/vl53l0x_python.c @@ -45,9 +45,6 @@ SOFTWARE. #define VL53L0X_LONG_RANGE_MODE 3 // Longe Range mode #define VL53L0X_HIGH_SPEED_MODE 4 // High Speed mode -#define MAX_DEVICES 16 - -static VL53L0X_Dev_t *pMyDevice[MAX_DEVICES]; static VL53L0X_RangingMeasurementData_t RangingMeasurementData; static VL53L0X_RangingMeasurementData_t *pRangingMeasurementData = &RangingMeasurementData; @@ -121,32 +118,15 @@ VL53L0X_Error WaitStopCompleted(VL53L0X_DEV Dev) } /****************************************************************************** - * @brief Start Ranging - * @param mode - ranging mode - * 0 - Good Accuracy mode - * 1 - Better Accuracy mode - * 2 - Best Accuracy mode - * 3 - Longe Range mode - * 4 - High Speed mode - * @note Mode Definitions - * Good Accuracy mode - * 33 ms timing budget 1.2m range - * Better Accuracy mode - * 66 ms timing budget 1.2m range - * Best Accuracy mode - * 200 ms 1.2m range - * Long Range mode (indoor,darker conditions) - * 33 ms timing budget 2m range - * High Speed Mode (decreased accuracy) - * 20 ms timing budget 1.2m range + * @brief Initialises the device. * @param i2c_address - I2C Address to set for this device * @param TCA9548A_Device - Device number on TCA9548A I2C multiplexer if * being used. If not being used, set to 255. * @param TCA9548A_Address - Address of TCA9548A I2C multiplexer if * being used. If not being used, set to 0. - * + * @retval The Dev Object to pass to other library functions. *****************************************************************************/ -void startRanging(int object_number, int mode, uint8_t i2c_address, uint8_t TCA9548A_Device, uint8_t TCA9548A_Address) +VL53L0X_Dev_t *initialise(uint8_t i2c_address, uint8_t TCA9548A_Device, uint8_t TCA9548A_Address) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; uint32_t refSpadCount; @@ -160,382 +140,370 @@ void startRanging(int object_number, int mode, uint8_t i2c_address, uint8_t TCA9 if (TCA9548A_Device < 8) { - printf ("VL53L0X Start Ranging Object %d Address 0x%02X TCA9548A Device %d TCA9548A Address 0x%02X\n\n", - object_number, i2c_address, TCA9548A_Device, TCA9548A_Address); + printf ("VL53L0X Start Ranging Address 0x%02X TCA9548A Device %d TCA9548A Address 0x%02X\n\n", + i2c_address, TCA9548A_Device, TCA9548A_Address); } else { - printf ("VL53L0X Start Ranging Object %d Address 0x%02X\n\n", object_number, i2c_address); + printf ("VL53L0X Start Ranging Address 0x%02X\n\n", i2c_address); } - if (mode >= VL53L0X_GOOD_ACCURACY_MODE && - mode <= VL53L0X_HIGH_SPEED_MODE && - object_number < MAX_DEVICES) + VL53L0X_Dev_t *dev = (VL53L0X_Dev_t *)malloc(sizeof(VL53L0X_Dev_t)); + memset(dev, 0, sizeof(VL53L0X_Dev_t)); + + if (dev != NULL) { - pMyDevice[object_number] = (VL53L0X_Dev_t *)malloc(sizeof(VL53L0X_Dev_t)); - memset(pMyDevice[object_number], 0, sizeof(VL53L0X_Dev_t)); + // Initialize Comms to the default address to start + dev->I2cDevAddr = VL53L0X_DEFAULT_ADDRESS; + dev->TCA9548A_Device = TCA9548A_Device; + dev->TCA9548A_Address = TCA9548A_Address; + + VL53L0X_init(dev); + /* + * Get the version of the VL53L0X API running in the firmware + */ + + // If the requested address is not the default, change it in the device + if (i2c_address != VL53L0X_DEFAULT_ADDRESS) + { + printf("Setting I2C Address to 0x%02X\n", i2c_address); + // Address requested not default so set the address. + // This assumes that the shutdown pin has been controlled + // externally to this function. + // TODO: Why does this function divide the address by 2? To get + // the address we want we have to mutiply by 2 in the call so + // it gets set right + Status = VL53L0X_SetDeviceAddress(dev, (i2c_address * 2)); + dev->I2cDevAddr = i2c_address; + } - if (pMyDevice[object_number] != NULL) + if (Status == VL53L0X_ERROR_NONE) { - // Initialize Comms to the default address to start - pMyDevice[object_number]->I2cDevAddr = VL53L0X_DEFAULT_ADDRESS; - pMyDevice[object_number]->TCA9548A_Device = TCA9548A_Device; - pMyDevice[object_number]->TCA9548A_Address = TCA9548A_Address; - - VL53L0X_init(pMyDevice[object_number]); - /* - * Get the version of the VL53L0X API running in the firmware - */ - - // If the requested address is not the default, change it in the device - if (i2c_address != VL53L0X_DEFAULT_ADDRESS) + status_int = VL53L0X_GetVersion(pVersion); + if (status_int == 0) { - printf("Setting I2C Address to 0x%02X\n", i2c_address); - // Address requested not default so set the address. - // This assumes that the shutdown pin has been controlled - // externally to this function. - // TODO: Why does this function divide the address by 2? To get - // the address we want we have to mutiply by 2 in the call so - // it gets set right - Status = VL53L0X_SetDeviceAddress(pMyDevice[object_number], (i2c_address * 2)); - pMyDevice[object_number]->I2cDevAddr = i2c_address; - } + /* + * Verify the version of the VL53L0X API running in the firmrware + */ + + // Check the Api version. If it is not correct, put out a warning + if( pVersion->major != VERSION_REQUIRED_MAJOR || + pVersion->minor != VERSION_REQUIRED_MINOR || + pVersion->build != VERSION_REQUIRED_BUILD ) + { + printf("VL53L0X API Version Warning: Your firmware %d.%d.%d (revision %d). This requires %d.%d.%d.\n", + pVersion->major, pVersion->minor, pVersion->build, pVersion->revision, + VERSION_REQUIRED_MAJOR, VERSION_REQUIRED_MINOR, VERSION_REQUIRED_BUILD); + } + // End of implementation specific - if (Status == VL53L0X_ERROR_NONE) - { - status_int = VL53L0X_GetVersion(pVersion); - if (status_int == 0) + Status = VL53L0X_DataInit(dev); // Data initialization + if(Status == VL53L0X_ERROR_NONE) { - /* - * Verify the version of the VL53L0X API running in the firmrware - */ - - // Check the Api version. If it is not correct, put out a warning - if( pVersion->major != VERSION_REQUIRED_MAJOR || - pVersion->minor != VERSION_REQUIRED_MINOR || - pVersion->build != VERSION_REQUIRED_BUILD ) + Status = VL53L0X_GetDeviceInfo(dev, &DeviceInfo); + if(Status == VL53L0X_ERROR_NONE) { - printf("VL53L0X API Version Warning: Your firmware %d.%d.%d (revision %d). This requires %d.%d.%d.\n", - pVersion->major, pVersion->minor, pVersion->build, pVersion->revision, - VERSION_REQUIRED_MAJOR, VERSION_REQUIRED_MINOR, VERSION_REQUIRED_BUILD); + printf("VL53L0X_GetDeviceInfo:\n"); + printf("Device Name : %s\n", DeviceInfo.Name); + printf("Device Type : %s\n", DeviceInfo.Type); + printf("Device ID : %s\n", DeviceInfo.ProductId); + printf("ProductRevisionMajor : %d\n", DeviceInfo.ProductRevisionMajor); + printf("ProductRevisionMinor : %d\n", DeviceInfo.ProductRevisionMinor); + + if ((DeviceInfo.ProductRevisionMajor != 1) && (DeviceInfo.ProductRevisionMinor != 1)) { + printf("Error expected cut 1.1 but found cut %d.%d\n", + DeviceInfo.ProductRevisionMajor, DeviceInfo.ProductRevisionMinor); + Status = VL53L0X_ERROR_NOT_SUPPORTED; + } } - // End of implementation specific - Status = VL53L0X_DataInit(pMyDevice[object_number]); // Data initialization if(Status == VL53L0X_ERROR_NONE) { - Status = VL53L0X_GetDeviceInfo(pMyDevice[object_number], &DeviceInfo); - if(Status == VL53L0X_ERROR_NONE) - { - printf("VL53L0X_GetDeviceInfo:\n"); - printf("Device Name : %s\n", DeviceInfo.Name); - printf("Device Type : %s\n", DeviceInfo.Type); - printf("Device ID : %s\n", DeviceInfo.ProductId); - printf("ProductRevisionMajor : %d\n", DeviceInfo.ProductRevisionMajor); - printf("ProductRevisionMinor : %d\n", DeviceInfo.ProductRevisionMinor); - - if ((DeviceInfo.ProductRevisionMajor != 1) && (DeviceInfo.ProductRevisionMinor != 1)) { - printf("Error expected cut 1.1 but found cut %d.%d\n", - DeviceInfo.ProductRevisionMajor, DeviceInfo.ProductRevisionMinor); - Status = VL53L0X_ERROR_NOT_SUPPORTED; - } - } + Status = VL53L0X_StaticInit(dev); // Device Initialization + // StaticInit will set interrupt by default if(Status == VL53L0X_ERROR_NONE) { - Status = VL53L0X_StaticInit(pMyDevice[object_number]); // Device Initialization - // StaticInit will set interrupt by default + Status = VL53L0X_PerformRefCalibration(dev, + &VhvSettings, &PhaseCal); // Device Initialization if(Status == VL53L0X_ERROR_NONE) { - Status = VL53L0X_PerformRefCalibration(pMyDevice[object_number], - &VhvSettings, &PhaseCal); // Device Initialization + Status = VL53L0X_PerformRefSpadManagement(dev, + &refSpadCount, &isApertureSpads); // Device Initialization - if(Status == VL53L0X_ERROR_NONE) + if(Status != VL53L0X_ERROR_NONE) { - Status = VL53L0X_PerformRefSpadManagement(pMyDevice[object_number], - &refSpadCount, &isApertureSpads); // Device Initialization - - if(Status == VL53L0X_ERROR_NONE) - { - // Setup in continuous ranging mode - Status = VL53L0X_SetDeviceMode(pMyDevice[object_number], VL53L0X_DEVICEMODE_CONTINUOUS_RANGING); - - if(Status == VL53L0X_ERROR_NONE) - { - // Set accuracy mode - switch (mode) - { - case VL53L0X_BEST_ACCURACY_MODE: - printf("VL53L0X_BEST_ACCURACY_MODE\n"); - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetLimitCheckValue(pMyDevice[object_number], - VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, - (FixPoint1616_t)(0.25*65536)); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetLimitCheckValue(pMyDevice[object_number], - VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, - (FixPoint1616_t)(18*65536)); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = - VL53L0X_SetMeasurementTimingBudgetMicroSeconds(pMyDevice[object_number], 200000); - } - } - } - break; - - case VL53L0X_LONG_RANGE_MODE: - printf("VL53L0X_LONG_RANGE_MODE\n"); - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetLimitCheckValue(pMyDevice[object_number], - VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, - (FixPoint1616_t)(0.1*65536)); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetLimitCheckValue(pMyDevice[object_number], - VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, - (FixPoint1616_t)(60*65536)); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = - VL53L0X_SetMeasurementTimingBudgetMicroSeconds(pMyDevice[object_number], 33000); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetVcselPulsePeriod(pMyDevice[object_number], - VL53L0X_VCSEL_PERIOD_PRE_RANGE, 18); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetVcselPulsePeriod(pMyDevice[object_number], - VL53L0X_VCSEL_PERIOD_FINAL_RANGE, 14); - } - } - } - } - } - break; - - case VL53L0X_HIGH_SPEED_MODE: - printf("VL53L0X_HIGH_SPEED_MODE\n"); - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetLimitCheckValue(pMyDevice[object_number], - VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, - (FixPoint1616_t)(0.25*65536)); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_SetLimitCheckValue(pMyDevice[object_number], - VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, - (FixPoint1616_t)(32*65536)); - - if (Status == VL53L0X_ERROR_NONE) - { - Status = - VL53L0X_SetMeasurementTimingBudgetMicroSeconds(pMyDevice[object_number], 20000); - } - } - } - break; - - case VL53L0X_BETTER_ACCURACY_MODE: - printf("VL53L0X_BETTER_ACCURACY_MODE\n"); - if (Status == VL53L0X_ERROR_NONE) - { - Status = - VL53L0X_SetMeasurementTimingBudgetMicroSeconds(pMyDevice[object_number], 66000); - } - break; - - case VL53L0X_GOOD_ACCURACY_MODE: - default: - printf("VL53L0X_GOOD_ACCURACY_MODE\n"); - if (Status == VL53L0X_ERROR_NONE) - { - Status = - VL53L0X_SetMeasurementTimingBudgetMicroSeconds(pMyDevice[object_number], 33000); - } - break; - } - - if(Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_StartMeasurement(pMyDevice[object_number]); - } - else - { - printf("Set Accuracy\n"); - } - } - else - { - printf ("Call of VL53L0X_SetDeviceMode\n"); - } - } - else - { - printf ("Call of VL53L0X_PerformRefSpadManagement\n"); - } - } - else - { - printf ("Call of VL53L0X_PerformRefCalibration\n"); + printf ("Call of VL53L0X_PerformRefSpadManagement\n"); } } else { - printf ("Call of VL53L0X_StaticInit\n"); + printf ("Call of VL53L0X_PerformRefCalibration\n"); } } else { - printf ("Invalid Device Info\n"); + printf ("Call of VL53L0X_StaticInit\n"); } } else { - printf ("Call of VL53L0X_DataInit\n"); + printf ("Invalid Device Info\n"); } } else { - Status = VL53L0X_ERROR_CONTROL_INTERFACE; - printf("Call of VL53L0X_GetVersion\n"); + printf ("Call of VL53L0X_DataInit\n"); } } else { - printf("Call of VL53L0X_SetAddress\n"); + Status = VL53L0X_ERROR_CONTROL_INTERFACE; + printf("Call of VL53L0X_GetVersion\n"); } - - print_pal_error(Status); } else { - printf("Object %d not initialized\n", object_number); + printf("Call of VL53L0X_SetAddress\n"); } + + print_pal_error(Status); } else { - if (object_number >= MAX_DEVICES) - { - printf("Max objects Exceeded\n"); - } - else - { - printf("Invalid mode %d specified\n", mode); - } + printf("Memory allocation failure\n"); } + + return dev; } /****************************************************************************** - * @brief Get current distance in mm - * @return Current distance in mm or -1 on error + * @brief Start Ranging + * @param mode - ranging mode + * 0 - Good Accuracy mode + * 1 - Better Accuracy mode + * 2 - Best Accuracy mode + * 3 - Longe Range mode + * 4 - High Speed mode + * @note Mode Definitions + * Good Accuracy mode + * 33 ms timing budget 1.2m range + * Better Accuracy mode + * 66 ms timing budget 1.2m range + * Best Accuracy mode + * 200 ms 1.2m range + * Long Range mode (indoor,darker conditions) + * 33 ms timing budget 2m range + * High Speed Mode (decreased accuracy) + * 20 ms timing budget 1.2m range + * @retval Error code, 0 for success. *****************************************************************************/ -int32_t getDistance(int object_number) +VL53L0X_Error startRanging(VL53L0X_Dev_t *dev, int mode) { - VL53L0X_Error Status = VL53L0X_ERROR_NONE; - int32_t current_distance = -1; + if (mode < VL53L0X_GOOD_ACCURACY_MODE && + mode > VL53L0X_HIGH_SPEED_MODE) + { + printf("Invalid mode %d specified\n", mode); + return -1; + } - if (object_number < MAX_DEVICES) + // Setup in continuous ranging mode + VL53L0X_Error Status = VL53L0X_SetDeviceMode(dev, VL53L0X_DEVICEMODE_CONTINUOUS_RANGING); + + if(Status == VL53L0X_ERROR_NONE) { - if (pMyDevice[object_number] != NULL) + // Set accuracy mode + switch (mode) { - Status = WaitMeasurementDataReady(pMyDevice[object_number]); + case VL53L0X_BEST_ACCURACY_MODE: + printf("VL53L0X_BEST_ACCURACY_MODE\n"); + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetLimitCheckValue(dev, + VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t)(0.25*65536)); - if(Status == VL53L0X_ERROR_NONE) - { - Status = VL53L0X_GetRangingMeasurementData(pMyDevice[object_number], - pRangingMeasurementData); - if(Status == VL53L0X_ERROR_NONE) + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetLimitCheckValue(dev, + VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t)(18*65536)); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = + VL53L0X_SetMeasurementTimingBudgetMicroSeconds(dev, 200000); + } + } + } + break; + + case VL53L0X_LONG_RANGE_MODE: + printf("VL53L0X_LONG_RANGE_MODE\n"); + if (Status == VL53L0X_ERROR_NONE) { - current_distance = pRangingMeasurementData->RangeMilliMeter; + Status = VL53L0X_SetLimitCheckValue(dev, + VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t)(0.1*65536)); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetLimitCheckValue(dev, + VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t)(60*65536)); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = + VL53L0X_SetMeasurementTimingBudgetMicroSeconds(dev, 33000); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetVcselPulsePeriod(dev, + VL53L0X_VCSEL_PERIOD_PRE_RANGE, 18); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetVcselPulsePeriod(dev, + VL53L0X_VCSEL_PERIOD_FINAL_RANGE, 14); + } + } + } + } } + break; - // Clear the interrupt - VL53L0X_ClearInterruptMask(pMyDevice[object_number], - VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); - // VL53L0X_PollingDelay(pMyDevice[object_number]); - } + case VL53L0X_HIGH_SPEED_MODE: + printf("VL53L0X_HIGH_SPEED_MODE\n"); + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetLimitCheckValue(dev, + VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t)(0.25*65536)); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_SetLimitCheckValue(dev, + VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t)(32*65536)); + + if (Status == VL53L0X_ERROR_NONE) + { + Status = + VL53L0X_SetMeasurementTimingBudgetMicroSeconds(dev, 20000); + } + } + } + break; + + case VL53L0X_BETTER_ACCURACY_MODE: + printf("VL53L0X_BETTER_ACCURACY_MODE\n"); + if (Status == VL53L0X_ERROR_NONE) + { + Status = + VL53L0X_SetMeasurementTimingBudgetMicroSeconds(dev, 66000); + } + break; + + case VL53L0X_GOOD_ACCURACY_MODE: + default: + printf("VL53L0X_GOOD_ACCURACY_MODE\n"); + if (Status == VL53L0X_ERROR_NONE) + { + Status = + VL53L0X_SetMeasurementTimingBudgetMicroSeconds(dev, 33000); + } + break; + } + + if(Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_StartMeasurement(dev); } else { - printf("Object %d not initialized\n", object_number); + printf("Set Accuracy\n"); } } else { - printf("Invalid object number %d specified\n", object_number); + printf ("Call of VL53L0X_SetDeviceMode\n"); } - return current_distance; + print_pal_error(Status); + + return Status; } /****************************************************************************** - * @brief Stop Ranging + * @brief Get current distance in mm + * @return Current distance in mm or -1 on error *****************************************************************************/ -void stopRanging(int object_number) +int32_t getDistance(VL53L0X_Dev_t *dev) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; + int32_t current_distance = -1; - printf ("Call of VL53L0X_StopMeasurement\n"); - - if (object_number < MAX_DEVICES) + if (dev != NULL) { - if (pMyDevice[object_number] != NULL) - { - Status = VL53L0X_StopMeasurement(pMyDevice[object_number]); - - if(Status == VL53L0X_ERROR_NONE) - { - printf ("Wait Stop to be competed\n"); - Status = WaitStopCompleted(pMyDevice[object_number]); - } + Status = WaitMeasurementDataReady(dev); + if(Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_GetRangingMeasurementData(dev, + pRangingMeasurementData); if(Status == VL53L0X_ERROR_NONE) { - Status = VL53L0X_ClearInterruptMask(pMyDevice[object_number], - VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); + current_distance = pRangingMeasurementData->RangeMilliMeter; } - print_pal_error(Status); - - free(pMyDevice[object_number]); - } - else - { - printf("Object %d not initialized\n", object_number); + // Clear the interrupt + VL53L0X_ClearInterruptMask(dev, + VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); + // VL53L0X_PollingDelay(dev); } } else { - printf("Invalid object number %d specified\n", object_number); + printf("Device not initialized\n"); } + + return current_distance; } /****************************************************************************** - * @brief Return the Dev Object to pass to Lib functions + * @brief Stop Ranging *****************************************************************************/ -VL53L0X_DEV getDev(int object_number) +void stopRanging(VL53L0X_Dev_t *dev) { - VL53L0X_DEV Dev = NULL; - if (object_number < MAX_DEVICES) + VL53L0X_Error Status = VL53L0X_ERROR_NONE; + + printf ("Call of VL53L0X_StopMeasurement\n"); + + if (dev != NULL) { - if (pMyDevice[object_number] != NULL) + Status = VL53L0X_StopMeasurement(dev); + + if(Status == VL53L0X_ERROR_NONE) { - Dev = pMyDevice[object_number]; + printf ("Wait Stop to be competed\n"); + Status = WaitStopCompleted(dev); } - } - return Dev; -} + if(Status == VL53L0X_ERROR_NONE) + { + Status = VL53L0X_ClearInterruptMask(dev, + VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); + } + print_pal_error(Status); + + free(dev); + dev = NULL; + } + else + { + printf("Device not initialized\n"); + } +} diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..09080c7 --- /dev/null +++ b/setup.py @@ -0,0 +1,57 @@ +from distutils.core import setup, Extension +from distutils.command.build_ext import build_ext + + +# Found this on stack overflow: +# https://stackoverflow.com/questions/4529555/building-a-ctypes-based-c-library-with-distutils +# noinspection PyPep8Naming +class build_ext(build_ext): + + def build_extension(self, ext): + # noinspection PyAttributeOutsideInit + self._ctypes = isinstance(ext, CTypesExtension) + return super().build_extension(ext) + + def get_export_symbols(self, ext): + if self._ctypes: + return ext.export_symbols + return super().get_export_symbols(ext) + + def get_ext_filename(self, ext_name): + if self._ctypes: + return ext_name + '.so' + return super().get_ext_filename(ext_name) + + +class CTypesExtension(Extension): + pass + + +extension = CTypesExtension( + 'vl53l0x_python', + define_macros=[], + include_dirs=['.', 'Api/core/inc', 'platform/inc'], + libraries=[], + library_dirs=[], + sources=['Api/core/src/vl53l0x_api_calibration.c', + 'Api/core/src/vl53l0x_api_core.c', + 'Api/core/src/vl53l0x_api_ranging.c', + 'Api/core/src/vl53l0x_api_strings.c', + 'Api/core/src/vl53l0x_api.c', + 'platform/src/vl53l0x_platform.c', + 'python_lib/vl53l0x_python.c']) + +setup(name='VL53L0X_rasp_python', + version='1.0.2', + description='VL53L0X sensor for raspberry PI', + # author='?', + # author_email='?', + url='https://github.com/grantramsay/VL53L0X_rasp_python', + long_description=''' +VL53L0X sensor for raspberry PI. +''', + ext_modules=[extension], + package_dir={'': 'python'}, + py_modules=['VL53L0X'], + requires=['smbus2'], + cmdclass={'build_ext': build_ext})