/ adafruit_lidarlite.py
adafruit_lidarlite.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2018 ladyada 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_lidarlite` 24 ==================================================== 25 26 A CircuitPython & Python library for Garmin LIDAR Lite sensors over I2C 27 28 * Author(s): ladyada 29 30 Implementation Notes 31 -------------------- 32 33 **Hardware:** 34 35 36 **Software and Dependencies:** 37 38 * Adafruit CircuitPython firmware for the supported boards: 39 https://github.com/adafruit/circuitpython/releases 40 41 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 42 43 """ 44 45 # imports 46 import time 47 from adafruit_bus_device.i2c_device import I2CDevice 48 from digitalio import Direction 49 from micropython import const 50 51 __version__ = "0.0.0-auto.0" 52 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LIDARLite.git" 53 54 55 _ADDR_DEFAULT = const(0x62) 56 _REG_ACQ_COMMAND = const(0x00) 57 _CMD_RESET = const(0) 58 _CMD_DISTANCENOBIAS = const(3) 59 _CMD_DISTANCEWITHBIAS = const(4) 60 61 CONFIG_DEFAULT = 0 62 CONFIG_SHORTFAST = 1 63 CONFIG_DEFAULTFAST = 2 64 CONFIG_MAXRANGE = 3 65 CONFIG_HIGHSENSITIVE = 4 66 CONFIG_LOWSENSITIVE = 5 67 68 STATUS_BUSY = 0x01 69 STATUS_REF_OVERFLOW = 0x02 70 STATUS_SIGNAL_OVERFLOW = 0x04 71 STATUS_NO_PEAK = 0x08 72 STATUS_SECOND_RETURN = 0x10 73 STATUS_HEALTHY = 0x20 74 STATUS_SYS_ERROR = 0x40 75 76 # The various configuration register values, from arduino library 77 _LIDAR_CONFIGS = ( 78 (0x80, 0x08, 0x00), # default 79 (0x1D, 0x08, 0x00), # short range, high speed 80 (0x80, 0x00, 0x00), # default range, higher speed short range 81 (0xFF, 0x08, 0x00), # maximum range 82 (0x80, 0x08, 0x80), # high sensitivity & error 83 (0x80, 0x08, 0xB0), 84 ) # low sensitivity & error 85 86 87 class LIDARLite: 88 """ 89 A driver for the Garmin LIDAR Lite laser distance sensor. 90 :param i2c_bus: The `busio.I2C` object to use. This is the only 91 required parameter. 92 :param int address: (optional) The I2C address of the device to set after initialization. 93 """ 94 95 def __init__( 96 self, 97 i2c_bus, 98 *, 99 reset_pin=None, 100 configuration=CONFIG_DEFAULT, 101 address=_ADDR_DEFAULT 102 ): 103 """Initialize the hardware for the LIDAR over I2C. You can pass in an 104 optional reset_pin for when you call reset(). There are a few common 105 configurations Garmin suggests: CONFIG_DEFAULT, CONFIG_SHORTFAST, 106 CONFIG_DEFAULTFAST, CONFIG_MAXRANGE, CONFIG_HIGHSENSITIVE, and 107 CONFIG_LOWSENSITIVE. For the I2C address, the default is 0x62 but if you 108 pass a different number in, we'll try to change the address so multiple 109 LIDARs can be connected. (Note all but one need to be in reset for this 110 to work!)""" 111 self.i2c_device = I2CDevice(i2c_bus, address) 112 self._buf = bytearray(2) 113 self._bias_count = 0 114 self._reset = reset_pin 115 time.sleep(0.5) 116 self.configure(configuration) 117 self._status = self.status 118 119 def reset(self): 120 """Hardware reset (if pin passed into init) or software reset. Will take 121 100 readings in order to 'flush' measurement unit, otherwise data is off.""" 122 # Optional hardware reset pin 123 if self._reset is not None: 124 self._reset.direction = Direction.OUTPUT 125 self._reset.value = True 126 self._reset.value = False 127 time.sleep(0.01) 128 self._reset.value = True 129 else: 130 try: 131 self._write_reg(_REG_ACQ_COMMAND, _CMD_RESET) 132 except OSError: 133 pass # it doesnt respond well once reset 134 time.sleep(1) 135 # take 100 readings to 'flush' out sensor! 136 for _ in range(100): 137 try: 138 self.read_distance(True) 139 except RuntimeError: 140 pass 141 142 def configure(self, config): 143 """Set the LIDAR desired style of measurement. There are a few common 144 configurations Garmin suggests: CONFIG_DEFAULT, CONFIG_SHORTFAST, 145 CONFIG_DEFAULTFAST, CONFIG_MAXRANGE, CONFIG_HIGHSENSITIVE, and 146 CONFIG_LOWSENSITIVE.""" 147 settings = _LIDAR_CONFIGS[config] 148 self._write_reg(0x02, settings[0]) 149 self._write_reg(0x04, settings[1]) 150 self._write_reg(0x1C, settings[2]) 151 152 def read_distance(self, bias=False): 153 """Perform a distance reading with or without 'bias'. It's recommended 154 to take a bias measurement every 100 non-bias readings (they're slower)""" 155 if bias: 156 self._write_reg(_REG_ACQ_COMMAND, _CMD_DISTANCEWITHBIAS) 157 else: 158 self._write_reg(_REG_ACQ_COMMAND, _CMD_DISTANCENOBIAS) 159 dist = self._read_reg(0x8F, 2) 160 if self._status & (STATUS_NO_PEAK | STATUS_SECOND_RETURN): 161 raise RuntimeError("Measurement failure") 162 if (self._status & STATUS_SYS_ERROR) or (not self._status & STATUS_HEALTHY): 163 raise RuntimeError("System failure") 164 return dist[0] << 8 | dist[1] 165 166 @property 167 def distance(self): 168 """The measured distance in cm. Will take a bias reading every 100 calls""" 169 self._bias_count -= 1 170 if self._bias_count < 0: 171 self._bias_count = 100 # every 100 reads, check bias 172 return self.read_distance(self._bias_count <= 0) 173 174 @property 175 def status(self): 176 """The status byte, check datasheet for bitmask""" 177 buf = bytearray([0x1]) 178 with self.i2c_device as i2c: 179 i2c.write_then_readinto(buf, buf) 180 return buf[0] 181 182 def _write_reg(self, reg, value): 183 self._buf[0] = reg 184 self._buf[1] = value 185 with self.i2c_device as i2c: 186 # print("Writing: ", [hex(i) for i in self._buf]) 187 i2c.write(self._buf) 188 time.sleep(0.001) # there's a delay in arduino library 189 190 def _read_reg(self, reg, num): 191 while True: 192 self._status = self.status 193 if not self._status & STATUS_BUSY: 194 break 195 # no longer busy 196 self._buf[0] = reg 197 with self.i2c_device as i2c: 198 i2c.write_then_readinto(self._buf, self._buf, out_end=1, in_end=num) 199 # print("Read from ", hex(reg), [hex(i) for i in self._buf]) 200 return self._buf