/ adafruit_vl53l0x.py
adafruit_vl53l0x.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2017 Tony DiCola for Adafruit Industries 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining a copy 6 # of this software and associated documentation files (the "Software"), to deal 7 # in the Software without restriction, including without limitation the rights 8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 # copies of the Software, and to permit persons to whom the Software is 10 # furnished to do so, subject to the following conditions: 11 # 12 # The above copyright notice and this permission notice shall be included in 13 # all copies or substantial portions of the Software. 14 # 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 # THE SOFTWARE. 22 """ 23 `adafruit_vl53l0x` 24 ==================================================== 25 26 CircuitPython driver for the VL53L0X distance sensor. This code is adapted 27 from the pololu driver here: 28 https://github.com/pololu/vl53l0x-arduino 29 30 See usage in the examples/vl53l0x_simpletest.py file. 31 32 * Author(s): Tony DiCola 33 34 Implementation Notes 35 -------------------- 36 37 **Hardware:** 38 39 * Adafruit `VL53L0X Time of Flight Distance Sensor - ~30 to 1000mm 40 <https://www.adafruit.com/product/3317>`_ (Product ID: 3317) 41 42 **Software and Dependencies:** 43 44 * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: 45 https://github.com/adafruit/circuitpython/releases 46 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 47 """ 48 import math 49 import time 50 51 import adafruit_bus_device.i2c_device as i2c_device 52 from micropython import const 53 54 __version__ = "0.0.0-auto.0" 55 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_VL53L0X.git" 56 57 # Configuration constants: 58 # pylint: disable=bad-whitespace 59 _SYSRANGE_START = const(0x00) 60 _SYSTEM_THRESH_HIGH = const(0x0C) 61 _SYSTEM_THRESH_LOW = const(0x0E) 62 _SYSTEM_SEQUENCE_CONFIG = const(0x01) 63 _SYSTEM_RANGE_CONFIG = const(0x09) 64 _SYSTEM_INTERMEASUREMENT_PERIOD = const(0x04) 65 _SYSTEM_INTERRUPT_CONFIG_GPIO = const(0x0A) 66 _GPIO_HV_MUX_ACTIVE_HIGH = const(0x84) 67 _SYSTEM_INTERRUPT_CLEAR = const(0x0B) 68 _RESULT_INTERRUPT_STATUS = const(0x13) 69 _RESULT_RANGE_STATUS = const(0x14) 70 _RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN = const(0xBC) 71 _RESULT_CORE_RANGING_TOTAL_EVENTS_RTN = const(0xC0) 72 _RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF = const(0xD0) 73 _RESULT_CORE_RANGING_TOTAL_EVENTS_REF = const(0xD4) 74 _RESULT_PEAK_SIGNAL_RATE_REF = const(0xB6) 75 _ALGO_PART_TO_PART_RANGE_OFFSET_MM = const(0x28) 76 _I2C_SLAVE_DEVICE_ADDRESS = const(0x8A) 77 _MSRC_CONFIG_CONTROL = const(0x60) 78 _PRE_RANGE_CONFIG_MIN_SNR = const(0x27) 79 _PRE_RANGE_CONFIG_VALID_PHASE_LOW = const(0x56) 80 _PRE_RANGE_CONFIG_VALID_PHASE_HIGH = const(0x57) 81 _PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT = const(0x64) 82 _FINAL_RANGE_CONFIG_MIN_SNR = const(0x67) 83 _FINAL_RANGE_CONFIG_VALID_PHASE_LOW = const(0x47) 84 _FINAL_RANGE_CONFIG_VALID_PHASE_HIGH = const(0x48) 85 _FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = const(0x44) 86 _PRE_RANGE_CONFIG_SIGMA_THRESH_HI = const(0x61) 87 _PRE_RANGE_CONFIG_SIGMA_THRESH_LO = const(0x62) 88 _PRE_RANGE_CONFIG_VCSEL_PERIOD = const(0x50) 89 _PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI = const(0x51) 90 _PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO = const(0x52) 91 _SYSTEM_HISTOGRAM_BIN = const(0x81) 92 _HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT = const(0x33) 93 _HISTOGRAM_CONFIG_READOUT_CTRL = const(0x55) 94 _FINAL_RANGE_CONFIG_VCSEL_PERIOD = const(0x70) 95 _FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI = const(0x71) 96 _FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO = const(0x72) 97 _CROSSTALK_COMPENSATION_PEAK_RATE_MCPS = const(0x20) 98 _MSRC_CONFIG_TIMEOUT_MACROP = const(0x46) 99 _SOFT_RESET_GO2_SOFT_RESET_N = const(0xBF) 100 _IDENTIFICATION_MODEL_ID = const(0xC0) 101 _IDENTIFICATION_REVISION_ID = const(0xC2) 102 _OSC_CALIBRATE_VAL = const(0xF8) 103 _GLOBAL_CONFIG_VCSEL_WIDTH = const(0x32) 104 _GLOBAL_CONFIG_SPAD_ENABLES_REF_0 = const(0xB0) 105 _GLOBAL_CONFIG_SPAD_ENABLES_REF_1 = const(0xB1) 106 _GLOBAL_CONFIG_SPAD_ENABLES_REF_2 = const(0xB2) 107 _GLOBAL_CONFIG_SPAD_ENABLES_REF_3 = const(0xB3) 108 _GLOBAL_CONFIG_SPAD_ENABLES_REF_4 = const(0xB4) 109 _GLOBAL_CONFIG_SPAD_ENABLES_REF_5 = const(0xB5) 110 _GLOBAL_CONFIG_REF_EN_START_SELECT = const(0xB6) 111 _DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD = const(0x4E) 112 _DYNAMIC_SPAD_REF_EN_START_OFFSET = const(0x4F) 113 _POWER_MANAGEMENT_GO1_POWER_FORCE = const(0x80) 114 _VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV = const(0x89) 115 _ALGO_PHASECAL_LIM = const(0x30) 116 _ALGO_PHASECAL_CONFIG_TIMEOUT = const(0x30) 117 _VCSEL_PERIOD_PRE_RANGE = const(0) 118 _VCSEL_PERIOD_FINAL_RANGE = const(1) 119 # pylint: enable=bad-whitespace 120 121 122 def _decode_timeout(val): 123 # format: "(LSByte * 2^MSByte) + 1" 124 return float(val & 0xFF) * math.pow(2.0, ((val & 0xFF00) >> 8)) + 1 125 126 def _encode_timeout(timeout_mclks): 127 # format: "(LSByte * 2^MSByte) + 1" 128 timeout_mclks = int(timeout_mclks) & 0xFFFF 129 ls_byte = 0 130 ms_byte = 0 131 if timeout_mclks > 0: 132 ls_byte = timeout_mclks - 1 133 while ls_byte > 255: 134 ls_byte >>= 1 135 ms_byte += 1 136 return ((ms_byte << 8) | (ls_byte & 0xFF)) & 0xFFFF 137 return 0 138 139 def _timeout_mclks_to_microseconds(timeout_period_mclks, vcsel_period_pclks): 140 macro_period_ns = (((2304 * (vcsel_period_pclks) * 1655) + 500) // 1000) 141 return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns // 2)) // 1000 142 143 def _timeout_microseconds_to_mclks(timeout_period_us, vcsel_period_pclks): 144 macro_period_ns = (((2304 * (vcsel_period_pclks) * 1655) + 500) // 1000) 145 return ((timeout_period_us * 1000) + (macro_period_ns // 2)) // macro_period_ns 146 147 class VL53L0X: 148 """Driver for the VL53L0X distance sensor.""" 149 # Class-level buffer for reading and writing data with the sensor. 150 # This reduces memory allocations but means the code is not re-entrant or 151 # thread safe! 152 _BUFFER = bytearray(3) 153 154 def __init__(self, i2c, address=41, io_timeout_s=0): 155 # pylint: disable=too-many-statements 156 self._device = i2c_device.I2CDevice(i2c, address) 157 self.io_timeout_s = io_timeout_s 158 # Check identification registers for expected values. 159 # From section 3.2 of the datasheet. 160 if (self._read_u8(0xC0) != 0xEE or self._read_u8(0xC1) != 0xAA or 161 self._read_u8(0xC2) != 0x10): 162 raise RuntimeError('Failed to find expected ID register values. Check wiring!') 163 # Initialize access to the sensor. This is based on the logic from: 164 # https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp 165 # Set I2C standard mode. 166 for pair in ((0x88, 0x00), (0x80, 0x01), (0xFF, 0x01), (0x00, 0x00)): 167 self._write_u8(pair[0], pair[1]) 168 self._stop_variable = self._read_u8(0x91) 169 for pair in ((0x00, 0x01), (0xFF, 0x00), (0x80, 0x00)): 170 self._write_u8(pair[0], pair[1]) 171 # disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) 172 # limit checks 173 config_control = self._read_u8(_MSRC_CONFIG_CONTROL) | 0x12 174 self._write_u8(_MSRC_CONFIG_CONTROL, config_control) 175 # set final range signal rate limit to 0.25 MCPS (million counts per 176 # second) 177 self.signal_rate_limit = 0.25 178 self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xFF) 179 spad_count, spad_is_aperture = self._get_spad_info() 180 # The SPAD map (RefGoodSpadMap) is read by 181 # VL53L0X_get_info_from_device() in the API, but the same data seems to 182 # be more easily readable from GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through 183 # _6, so read it from there. 184 ref_spad_map = bytearray(7) 185 ref_spad_map[0] = _GLOBAL_CONFIG_SPAD_ENABLES_REF_0 186 with self._device: 187 self._device.write(ref_spad_map, end=1) 188 self._device.readinto(ref_spad_map, start=1) 189 190 for pair in ((0xFF, 0x01), 191 (_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00), 192 (_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C), 193 (0xFF, 0x00), 194 (_GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4)): 195 self._write_u8(pair[0], pair[1]) 196 197 first_spad_to_enable = 12 if spad_is_aperture else 0 198 spads_enabled = 0 199 for i in range(48): 200 if i < first_spad_to_enable or spads_enabled == spad_count: 201 # This bit is lower than the first one that should be enabled, 202 # or (reference_spad_count) bits have already been enabled, so 203 # zero this bit. 204 ref_spad_map[1 + (i // 8)] &= ~(1 << (i % 8)) 205 elif (ref_spad_map[1 + (i // 8)] >> (i % 8)) & 0x1 > 0: 206 spads_enabled += 1 207 with self._device: 208 self._device.write(ref_spad_map) 209 for pair in ((0xFF, 0x01), (0x00, 0x00), (0xFF, 0x00), (0x09, 0x00), 210 (0x10, 0x00), (0x11, 0x00), (0x24, 0x01), (0x25, 0xFF), 211 (0x75, 0x00), (0xFF, 0x01), (0x4E, 0x2C), (0x48, 0x00), 212 (0x30, 0x20), (0xFF, 0x00), (0x30, 0x09), (0x54, 0x00), 213 (0x31, 0x04), (0x32, 0x03), (0x40, 0x83), (0x46, 0x25), 214 (0x60, 0x00), (0x27, 0x00), (0x50, 0x06), (0x51, 0x00), 215 (0x52, 0x96), (0x56, 0x08), (0x57, 0x30), (0x61, 0x00), 216 (0x62, 0x00), (0x64, 0x00), (0x65, 0x00), (0x66, 0xA0), 217 (0xFF, 0x01), (0x22, 0x32), (0x47, 0x14), (0x49, 0xFF), 218 (0x4A, 0x00), (0xFF, 0x00), (0x7A, 0x0A), (0x7B, 0x00), 219 (0x78, 0x21), (0xFF, 0x01), (0x23, 0x34), (0x42, 0x00), 220 (0x44, 0xFF), (0x45, 0x26), (0x46, 0x05), (0x40, 0x40), 221 (0x0E, 0x06), (0x20, 0x1A), (0x43, 0x40), (0xFF, 0x00), 222 (0x34, 0x03), (0x35, 0x44), (0xFF, 0x01), (0x31, 0x04), 223 (0x4B, 0x09), (0x4C, 0x05), (0x4D, 0x04), (0xFF, 0x00), 224 (0x44, 0x00), (0x45, 0x20), (0x47, 0x08), (0x48, 0x28), 225 (0x67, 0x00), (0x70, 0x04), (0x71, 0x01), (0x72, 0xFE), 226 (0x76, 0x00), (0x77, 0x00), (0xFF, 0x01), (0x0D, 0x01), 227 (0xFF, 0x00), (0x80, 0x01), (0x01, 0xF8), (0xFF, 0x01), 228 (0x8E, 0x01), (0x00, 0x01), (0xFF, 0x00), (0x80, 0x00)): 229 self._write_u8(pair[0], pair[1]) 230 231 self._write_u8(_SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04) 232 gpio_hv_mux_active_high = self._read_u8(_GPIO_HV_MUX_ACTIVE_HIGH) 233 self._write_u8(_GPIO_HV_MUX_ACTIVE_HIGH, 234 gpio_hv_mux_active_high & ~0x10) # active low 235 self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01) 236 self._measurement_timing_budget_us = self.measurement_timing_budget 237 self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xE8) 238 self.measurement_timing_budget = self._measurement_timing_budget_us 239 self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0x01) 240 self._perform_single_ref_calibration(0x40) 241 self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0x02) 242 self._perform_single_ref_calibration(0x00) 243 # "restore the previous Sequence Config" 244 self._write_u8(_SYSTEM_SEQUENCE_CONFIG, 0xE8) 245 246 def _read_u8(self, address): 247 # Read an 8-bit unsigned value from the specified 8-bit address. 248 with self._device: 249 self._BUFFER[0] = address & 0xFF 250 self._device.write(self._BUFFER, end=1) 251 self._device.readinto(self._BUFFER, end=1) 252 return self._BUFFER[0] 253 254 def _read_u16(self, address): 255 # Read a 16-bit BE unsigned value from the specified 8-bit address. 256 with self._device: 257 self._BUFFER[0] = address & 0xFF 258 self._device.write(self._BUFFER, end=1) 259 self._device.readinto(self._BUFFER) 260 return (self._BUFFER[0] << 8) | self._BUFFER[1] 261 262 def _write_u8(self, address, val): 263 # Write an 8-bit unsigned value to the specified 8-bit address. 264 with self._device: 265 self._BUFFER[0] = address & 0xFF 266 self._BUFFER[1] = val & 0xFF 267 self._device.write(self._BUFFER, end=2) 268 269 def _write_u16(self, address, val): 270 # Write a 16-bit BE unsigned value to the specified 8-bit address. 271 with self._device: 272 self._BUFFER[0] = address & 0xFF 273 self._BUFFER[1] = (val >> 8) & 0xFF 274 self._BUFFER[2] = val & 0xFF 275 self._device.write(self._BUFFER) 276 277 278 def _get_spad_info(self): 279 # Get reference SPAD count and type, returned as a 2-tuple of 280 # count and boolean is_aperture. Based on code from: 281 # https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp 282 for pair in ((0x80, 0x01), (0xFF, 0x01), (0x00, 0x00), (0xFF, 0x06)): 283 self._write_u8(pair[0], pair[1]) 284 self._write_u8(0x83, self._read_u8(0x83) | 0x04) 285 for pair in ((0xFF, 0x07), (0x81, 0x01), (0x80, 0x01), 286 (0x94, 0x6b), (0x83, 0x00)): 287 self._write_u8(pair[0], pair[1]) 288 start = time.monotonic() 289 while self._read_u8(0x83) == 0x00: 290 if self.io_timeout_s > 0 and \ 291 (time.monotonic() - start) >= self.io_timeout_s: 292 raise RuntimeError('Timeout waiting for VL53L0X!') 293 self._write_u8(0x83, 0x01) 294 tmp = self._read_u8(0x92) 295 count = tmp & 0x7F 296 is_aperture = ((tmp >> 7) & 0x01) == 1 297 for pair in ((0x81, 0x00), (0xFF, 0x06)): 298 self._write_u8(pair[0], pair[1]) 299 self._write_u8(0x83, self._read_u8(0x83) & ~0x04) 300 for pair in ((0xFF, 0x01), (0x00, 0x01), (0xFF, 0x00), (0x80, 0x00)): 301 self._write_u8(pair[0], pair[1]) 302 return (count, is_aperture) 303 304 def _perform_single_ref_calibration(self, vhv_init_byte): 305 # based on VL53L0X_perform_single_ref_calibration() from ST API. 306 self._write_u8(_SYSRANGE_START, 0x01 | vhv_init_byte & 0xFF) 307 start = time.monotonic() 308 while (self._read_u8(_RESULT_INTERRUPT_STATUS) & 0x07) == 0: 309 if self.io_timeout_s > 0 and \ 310 (time.monotonic() - start) >= self.io_timeout_s: 311 raise RuntimeError('Timeout waiting for VL53L0X!') 312 self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01) 313 self._write_u8(_SYSRANGE_START, 0x00) 314 315 def _get_vcsel_pulse_period(self, vcsel_period_type): 316 # pylint: disable=no-else-return 317 # Disable should be removed when refactor can be tested 318 if vcsel_period_type == _VCSEL_PERIOD_PRE_RANGE: 319 val = self._read_u8(_PRE_RANGE_CONFIG_VCSEL_PERIOD) 320 return (((val) + 1) & 0xFF) << 1 321 elif vcsel_period_type == _VCSEL_PERIOD_FINAL_RANGE: 322 val = self._read_u8(_FINAL_RANGE_CONFIG_VCSEL_PERIOD) 323 return (((val) + 1) & 0xFF) << 1 324 return 255 325 326 def _get_sequence_step_enables(self): 327 # based on VL53L0X_GetSequenceStepEnables() from ST API 328 sequence_config = self._read_u8(_SYSTEM_SEQUENCE_CONFIG) 329 # pylint: disable=bad-whitespace 330 tcc = (sequence_config >> 4) & 0x1 > 0 331 dss = (sequence_config >> 3) & 0x1 > 0 332 msrc = (sequence_config >> 2) & 0x1 > 0 333 pre_range = (sequence_config >> 6) & 0x1 > 0 334 final_range = (sequence_config >> 7) & 0x1 > 0 335 return (tcc, dss, msrc, pre_range, final_range) 336 337 def _get_sequence_step_timeouts(self, pre_range): 338 # based on get_sequence_step_timeout() from ST API but modified by 339 # pololu here: 340 # https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp 341 pre_range_vcsel_period_pclks = self._get_vcsel_pulse_period(_VCSEL_PERIOD_PRE_RANGE) 342 msrc_dss_tcc_mclks = (self._read_u8(_MSRC_CONFIG_TIMEOUT_MACROP) + 1) & 0xFF 343 msrc_dss_tcc_us = _timeout_mclks_to_microseconds( 344 msrc_dss_tcc_mclks, pre_range_vcsel_period_pclks) 345 pre_range_mclks = _decode_timeout(self._read_u16(_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI)) 346 pre_range_us = _timeout_mclks_to_microseconds(pre_range_mclks, pre_range_vcsel_period_pclks) 347 final_range_vcsel_period_pclks = self._get_vcsel_pulse_period(_VCSEL_PERIOD_FINAL_RANGE) 348 final_range_mclks = _decode_timeout(self._read_u16(_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI)) 349 if pre_range: 350 final_range_mclks -= pre_range_mclks 351 final_range_us = _timeout_mclks_to_microseconds( 352 final_range_mclks, final_range_vcsel_period_pclks) 353 return (msrc_dss_tcc_us, 354 pre_range_us, 355 final_range_us, 356 final_range_vcsel_period_pclks, 357 pre_range_mclks) 358 359 @property 360 def signal_rate_limit(self): 361 """The signal rate limit in mega counts per second.""" 362 val = self._read_u16(_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT) 363 # Return value converted from 16-bit 9.7 fixed point to float. 364 return val / (1 << 7) 365 366 @signal_rate_limit.setter 367 def signal_rate_limit(self, val): 368 assert 0.0 <= val <= 511.99 369 # Convert to 16-bit 9.7 fixed point value from a float. 370 val = int(val * (1 << 7)) 371 self._write_u16(_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, val) 372 373 @property 374 def measurement_timing_budget(self): 375 """The measurement timing budget in microseconds.""" 376 budget_us = 1910 + 960 # Start overhead + end overhead. 377 tcc, dss, msrc, pre_range, final_range = self._get_sequence_step_enables() 378 step_timeouts = self._get_sequence_step_timeouts(pre_range) 379 msrc_dss_tcc_us, pre_range_us, final_range_us, _, _ = step_timeouts 380 if tcc: 381 budget_us += (msrc_dss_tcc_us + 590) 382 if dss: 383 budget_us += 2*(msrc_dss_tcc_us + 690) 384 elif msrc: 385 budget_us += (msrc_dss_tcc_us + 660) 386 if pre_range: 387 budget_us += (pre_range_us + 660) 388 if final_range: 389 budget_us += (final_range_us + 550) 390 self._measurement_timing_budget_us = budget_us 391 return budget_us 392 393 @measurement_timing_budget.setter 394 def measurement_timing_budget(self, budget_us): 395 # pylint: disable=too-many-locals 396 assert budget_us >= 20000 397 used_budget_us = 1320 + 960 # Start (diff from get) + end overhead 398 tcc, dss, msrc, pre_range, final_range = self._get_sequence_step_enables() 399 step_timeouts = self._get_sequence_step_timeouts(pre_range) 400 msrc_dss_tcc_us, pre_range_us, _ = step_timeouts[:3] 401 final_range_vcsel_period_pclks, pre_range_mclks = step_timeouts[3:] 402 if tcc: 403 used_budget_us += (msrc_dss_tcc_us + 590) 404 if dss: 405 used_budget_us += 2*(msrc_dss_tcc_us + 690) 406 elif msrc: 407 used_budget_us += (msrc_dss_tcc_us + 660) 408 if pre_range: 409 used_budget_us += (pre_range_us + 660) 410 if final_range: 411 used_budget_us += 550 412 # "Note that the final range timeout is determined by the timing 413 # budget and the sum of all other timeouts within the sequence. 414 # If there is no room for the final range timeout, then an error 415 # will be set. Otherwise the remaining time will be applied to 416 # the final range." 417 if used_budget_us > budget_us: 418 raise ValueError('Requested timeout too big.') 419 final_range_timeout_us = budget_us - used_budget_us 420 final_range_timeout_mclks = _timeout_microseconds_to_mclks( 421 final_range_timeout_us, 422 final_range_vcsel_period_pclks) 423 if pre_range: 424 final_range_timeout_mclks += pre_range_mclks 425 self._write_u16(_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, 426 _encode_timeout(final_range_timeout_mclks)) 427 self._measurement_timing_budget_us = budget_us 428 429 @property 430 def range(self): 431 """Perform a single reading of the range for an object in front of 432 the sensor and return the distance in millimeters. 433 """ 434 # Adapted from readRangeSingleMillimeters & 435 # readRangeContinuousMillimeters in pololu code at: 436 # https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp 437 for pair in ((0x80, 0x01), (0xFF, 0x01), (0x00, 0x00), 438 (0x91, self._stop_variable), (0x00, 0x01), (0xFF, 0x00), 439 (0x80, 0x00), (_SYSRANGE_START, 0x01)): 440 self._write_u8(pair[0], pair[1]) 441 start = time.monotonic() 442 while (self._read_u8(_SYSRANGE_START) & 0x01) > 0: 443 if self.io_timeout_s > 0 and \ 444 (time.monotonic() - start) >= self.io_timeout_s: 445 raise RuntimeError('Timeout waiting for VL53L0X!') 446 start = time.monotonic() 447 while (self._read_u8(_RESULT_INTERRUPT_STATUS) & 0x07) == 0: 448 if self.io_timeout_s > 0 and \ 449 (time.monotonic() - start) >= self.io_timeout_s: 450 raise RuntimeError('Timeout waiting for VL53L0X!') 451 # assumptions: Linearity Corrective Gain is 1000 (default) 452 # fractional ranging is not enabled 453 range_mm = self._read_u16(_RESULT_RANGE_STATUS + 10) 454 self._write_u8(_SYSTEM_INTERRUPT_CLEAR, 0x01) 455 return range_mm 456 457 def set_address(self, new_address): 458 """Set a new I2C address to the instantaited object. This is only called when using 459 multiple VL53L0X sensors on the same I2C bus (SDA & SCL pins). See also the 460 `example <examples.html#multiple-vl53l0x-on-same-i2c-bus>`_ for proper usage. 461 462 :param int new_address: The 7-bit `int` that is to be assigned to the VL53L0X sensor. 463 The address that is assigned should NOT be already in use by another device on the 464 I2C bus. 465 466 .. important:: To properly set the address to an individual VL53L0X sensor, you must 467 first ensure that all other VL53L0X sensors (using the default address of ``0x29``) 468 on the same I2C bus are in their off state by pulling the "SHDN" pins LOW. When the 469 "SHDN" pin is pulled HIGH again the default I2C address is ``0x29``. 470 """ 471 self._write_u8(_I2C_SLAVE_DEVICE_ADDRESS, new_address & 0x7f) 472 self._device.device_address = new_address