|
21 | 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | 23 | # SOFTWARE. |
24 | | -from ctypes import * |
| 24 | +from ctypes import CDLL, CFUNCTYPE, POINTER, c_int, c_uint, pointer, c_ubyte, c_uint8, c_uint32 |
25 | 25 | import smbus2 as smbus |
26 | 26 | import site |
27 | 27 |
|
28 | | -VL53L0X_GOOD_ACCURACY_MODE = 0 # Good Accuracy mode |
29 | | -VL53L0X_BETTER_ACCURACY_MODE = 1 # Better Accuracy mode |
30 | | -VL53L0X_BEST_ACCURACY_MODE = 2 # Best Accuracy mode |
31 | | -VL53L0X_LONG_RANGE_MODE = 3 # Longe Range mode |
32 | | -VL53L0X_HIGH_SPEED_MODE = 4 # High Speed mode |
33 | 28 |
|
34 | | -i2cbus = smbus.SMBus(1) |
| 29 | +class Vl53l0xError(RuntimeError): |
| 30 | + pass |
35 | 31 |
|
36 | 32 |
|
37 | | -# i2c bus read callback |
38 | | -def i2c_read(address, reg, data_p, 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 |
| 33 | +class Vl53l0xAccuracyMode: |
| 34 | + GOOD = 0 # 33 ms timing budget 1.2m range |
| 35 | + BETTER = 1 # 66 ms timing budget 1.2m range |
| 36 | + BEST = 2 # 200 ms 1.2m range |
| 37 | + LONG_RANGE = 3 # 33 ms timing budget 2m range |
| 38 | + HIGH_SPEED = 4 # 20 ms timing budget 1.2m range |
46 | 39 |
|
47 | | - if ret_val == 0: |
48 | | - for index in range(length): |
49 | | - data_p[index] = result[index] |
50 | 40 |
|
51 | | - return ret_val |
| 41 | +class Vl53l0xDeviceMode: |
| 42 | + SINGLE_RANGING = 0 |
| 43 | + CONTINUOUS_RANGING = 1 |
| 44 | + SINGLE_HISTOGRAM = 2 |
| 45 | + CONTINUOUS_TIMED_RANGING = 3 |
| 46 | + SINGLE_ALS = 10 |
| 47 | + GPIO_DRIVE = 20 |
| 48 | + GPIO_OSC = 21 |
52 | 49 |
|
53 | 50 |
|
54 | | -# i2c bus write callback |
55 | | -def i2c_write(address, reg, data_p, length): |
56 | | - ret_val = 0 |
57 | | - data = [] |
| 51 | +class Vl53l0xGpioAlarmType: |
| 52 | + OFF = 0 |
| 53 | + THRESHOLD_CROSSED_LOW = 1 |
| 54 | + THRESHOLD_CROSSED_HIGH = 2 |
| 55 | + THRESHOLD_CROSSED_OUT = 3 |
| 56 | + NEW_MEASUREMENT_READY = 4 |
58 | 57 |
|
59 | | - for index in range(length): |
60 | | - data.append(data_p[index]) |
61 | | - try: |
62 | | - i2cbus.write_i2c_block_data(address, reg, data) |
63 | | - except IOError: |
64 | | - ret_val = -1 |
65 | 58 |
|
66 | | - return ret_val |
| 59 | +class Vl53l0xInterruptPolarity: |
| 60 | + LOW = 0 |
| 61 | + HIGH = 1 |
| 62 | + |
67 | 63 |
|
| 64 | +# Read/write function pointer types. |
| 65 | +_I2C_READ_FUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) |
| 66 | +_I2C_WRITE_FUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) |
68 | 67 |
|
69 | 68 | # Load VL53L0X shared lib |
70 | | -_possible_lib_locations = site.getsitepackages() + ['../bin'] |
71 | | -for lib_location in _possible_lib_locations: |
| 69 | +_POSSIBLE_LIBRARY_LOCATIONS = ['../bin'] + site.getsitepackages() |
| 70 | +for lib_location in _POSSIBLE_LIBRARY_LOCATIONS: |
72 | 71 | try: |
73 | | - tof_lib = CDLL(lib_location + "/vl53l0x_python.so") |
| 72 | + _TOF_LIBRARY = CDLL(lib_location + "/vl53l0x_python.so") |
74 | 73 | break |
75 | 74 | except OSError: |
76 | 75 | pass |
77 | 76 | else: |
78 | 77 | raise OSError('Could not find vl53l0x_python.so') |
79 | 78 |
|
80 | | -# Create read function pointer |
81 | | -READFUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) |
82 | | -read_func = READFUNC(i2c_read) |
83 | | - |
84 | | -# Create write function pointer |
85 | | -WRITEFUNC = CFUNCTYPE(c_int, c_ubyte, c_ubyte, POINTER(c_ubyte), c_ubyte) |
86 | | -write_func = WRITEFUNC(i2c_write) |
87 | | - |
88 | | -# pass i2c read and write function pointers to VL53L0X library |
89 | | -tof_lib.VL53L0X_set_i2c(read_func, write_func) |
90 | | - |
91 | 79 |
|
92 | | -class VL53L0X(object): |
| 80 | +class VL53L0X: |
93 | 81 | """VL53L0X ToF.""" |
94 | | - |
95 | | - object_number = 0 |
96 | | - |
97 | | - def __init__(self, address=0x29, TCA9548A_Num=255, TCA9548A_Addr=0): |
| 82 | + def __init__(self, i2c_bus=1, i2c_address=0x29, tca9548a_num=255, tca9548a_addr=0): |
98 | 83 | """Initialize the VL53L0X ToF Sensor from ST""" |
99 | | - self.device_address = address |
100 | | - self.TCA9548A_Device = TCA9548A_Num |
101 | | - self.TCA9548A_Address = TCA9548A_Addr |
102 | | - self.my_object_number = VL53L0X.object_number |
103 | | - VL53L0X.object_number += 1 |
104 | | - |
105 | | - def start_ranging(self, mode=VL53L0X_GOOD_ACCURACY_MODE): |
| 84 | + self._i2c_bus = i2c_bus |
| 85 | + self.i2c_address = i2c_address |
| 86 | + self._tca9548a_num = tca9548a_num |
| 87 | + self._tca9548a_addr = tca9548a_addr |
| 88 | + self._i2c = smbus.SMBus() |
| 89 | + self._dev = None |
| 90 | + |
| 91 | + def open(self): |
| 92 | + self._i2c.open(bus=self._i2c_bus) |
| 93 | + self._configure_i2c_library_functions() |
| 94 | + self._dev = _TOF_LIBRARY.initialise(self.i2c_address, self._tca9548a_num, self._tca9548a_addr) |
| 95 | + |
| 96 | + def close(self): |
| 97 | + self._i2c.close() |
| 98 | + self._dev = None |
| 99 | + |
| 100 | + def _configure_i2c_library_functions(self): |
| 101 | + # I2C bus read callback for low level library. |
| 102 | + def _i2c_read(address, reg, data_p, length): |
| 103 | + ret_val = 0 |
| 104 | + result = [] |
| 105 | + |
| 106 | + try: |
| 107 | + result = self._i2c.read_i2c_block_data(address, reg, length) |
| 108 | + except IOError: |
| 109 | + ret_val = -1 |
| 110 | + |
| 111 | + if ret_val == 0: |
| 112 | + for index in range(length): |
| 113 | + data_p[index] = result[index] |
| 114 | + |
| 115 | + return ret_val |
| 116 | + |
| 117 | + # I2C bus write callback for low level library. |
| 118 | + def _i2c_write(address, reg, data_p, length): |
| 119 | + ret_val = 0 |
| 120 | + data = [] |
| 121 | + |
| 122 | + for index in range(length): |
| 123 | + data.append(data_p[index]) |
| 124 | + try: |
| 125 | + self._i2c.write_i2c_block_data(address, reg, data) |
| 126 | + except IOError: |
| 127 | + ret_val = -1 |
| 128 | + |
| 129 | + return ret_val |
| 130 | + |
| 131 | + # Pass i2c read/write function pointers to VL53L0X library. |
| 132 | + self._i2c_read_func = _I2C_READ_FUNC(_i2c_read) |
| 133 | + self._i2c_write_func = _I2C_WRITE_FUNC(_i2c_write) |
| 134 | + _TOF_LIBRARY.VL53L0X_set_i2c(self._i2c_read_func, self._i2c_write_func) |
| 135 | + |
| 136 | + def start_ranging(self, mode=Vl53l0xAccuracyMode.GOOD): |
106 | 137 | """Start VL53L0X ToF Sensor Ranging""" |
107 | | - tof_lib.startRanging(self.my_object_number, mode, self.device_address, |
108 | | - self.TCA9548A_Device, self.TCA9548A_Address) |
109 | | - |
| 138 | + _TOF_LIBRARY.startRanging(self._dev, mode) |
| 139 | + |
110 | 140 | def stop_ranging(self): |
111 | 141 | """Stop VL53L0X ToF Sensor Ranging""" |
112 | | - tof_lib.stopRanging(self.my_object_number) |
| 142 | + _TOF_LIBRARY.stopRanging(self._dev) |
113 | 143 |
|
114 | 144 | def get_distance(self): |
115 | 145 | """Get distance from VL53L0X ToF Sensor""" |
116 | | - return tof_lib.getDistance(self.my_object_number) |
| 146 | + return _TOF_LIBRARY.getDistance(self._dev) |
117 | 147 |
|
118 | 148 | # This function included to show how to access the ST library directly |
119 | 149 | # from python instead of through the simplified interface |
120 | 150 | def get_timing(self): |
121 | | - dev = tof_lib.getDev(self.my_object_number) |
122 | 151 | budget = c_uint(0) |
123 | 152 | budget_p = pointer(budget) |
124 | | - status = tof_lib.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(dev, budget_p) |
| 153 | + status = _TOF_LIBRARY.VL53L0X_GetMeasurementTimingBudgetMicroSeconds(self._dev, budget_p) |
125 | 154 | if status == 0: |
126 | 155 | return budget.value + 1000 |
127 | 156 | else: |
128 | 157 | return 0 |
| 158 | + |
| 159 | + def configure_gpio_interrupt( |
| 160 | + self, proximity_alarm_type: Vl53l0xGpioAlarmType = Vl53l0xGpioAlarmType.THRESHOLD_CROSSED_LOW, |
| 161 | + interrupt_polarity: Vl53l0xInterruptPolarity = Vl53l0xInterruptPolarity.HIGH, |
| 162 | + threshold_low_mm: int = 250, threshold_high_mm: int = 500): |
| 163 | + """ |
| 164 | + Configures a GPIO interrupt from device, be sure to call "clear_interrupt" after interrupt is processed. |
| 165 | + """ |
| 166 | + pin = c_uint8(0) # 0 is only GPIO pin. |
| 167 | + device_mode = c_uint8(Vl53l0xDeviceMode.CONTINUOUS_RANGING) |
| 168 | + functionality = c_uint8(proximity_alarm_type) |
| 169 | + polarity = c_uint8(interrupt_polarity) |
| 170 | + status = _TOF_LIBRARY.VL53L0X_SetGpioConfig(self._dev, pin, device_mode, functionality, polarity) |
| 171 | + if status != 0: |
| 172 | + raise Vl53l0xError('Error setting VL53L0X GPIO config') |
| 173 | + |
| 174 | + threshold_low = c_uint32(threshold_low_mm << 16) |
| 175 | + threshold_high = c_uint32(threshold_high_mm << 16) |
| 176 | + status = _TOF_LIBRARY.VL53L0X_SetInterruptThresholds(self._dev, device_mode, threshold_low, threshold_high) |
| 177 | + if status != 0: |
| 178 | + raise Vl53l0xError('Error setting VL53L0X thresholds') |
| 179 | + |
| 180 | + # Ensure any pending interrupts are cleared. |
| 181 | + self.clear_interrupt() |
| 182 | + |
| 183 | + def clear_interrupt(self): |
| 184 | + mask = c_uint32(0) |
| 185 | + status = _TOF_LIBRARY.VL53L0X_ClearInterruptMask(self._dev, mask) |
| 186 | + if status != 0: |
| 187 | + raise Vl53l0xError('Error clearing VL53L0X interrupt') |
0 commit comments