VL53L1X ToF distance sensor - adapting to micropython

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by pythoncoder » Sat Oct 03, 2020 4:24 pm

Thanks, that is useful information. That's a substantial archive ;)

AAMOI when you wrote your driver did you manage to track down a programming manual?
Peter Hinch
Index to my micropython libraries.

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by deshipu » Sat Oct 03, 2020 11:39 pm

No, it's based on the Pololu Arduino library, if I remember correctly.

rodrigolange
Posts: 1
Joined: Fri Oct 30, 2020 2:50 pm

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by rodrigolange » Fri Oct 30, 2020 3:21 pm

For those with some difficults regarding how to actually get the .py file from the bitbucket archive, my small contribution:

1 - download the repository from https://bitbucket-archive.softwareherit ... 53l0x.html and extract it somewhere. You will end with folder with a .hg subfolder

2 - in a linux machine install mercurial (in Ubuntu: sudo apt install mercurial) (you may use Windows, but I only tested in Linux)

Using a terminal, do the following:

3 - create a temporary folder (for instance, "temp" :-) ) and access it (cd temp)
4 - run the command "hg init ." (this will create a new mercurial repo in the local folder - dont forget the dot)

5- copy the .hg file from the folder created in step 1 to the new temp folder created in step 3
6 - form inside the temp folder, start a mercurial webserver with the command "hg serve".
7 - It will give you an error regarding some requirements (revlog-compression-zstd sparserevlog!). Its ok.
8 - edit the file .hg/requires and remove the entries related to the previous step (revlog-compression-zstd and sparserevlog)
9 - run "hg serve" again. It shoud give you a url (for instance, localhost:8000). Open that url with an web browser. You should see the repository

- Click in browse. The VL5310x.py is should be there :-)

Hope it helps and sorry my english :-)

User avatar
deshipu
Posts: 1388
Joined: Thu May 28, 2015 5:54 pm

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by deshipu » Fri Oct 30, 2020 7:15 pm

You can just run "hg update" in the directory to which you unpacked the ".hg" directory.

User avatar
JonHylands
Posts: 69
Joined: Sun Dec 29, 2013 1:33 am

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by JonHylands » Fri Nov 27, 2020 9:59 pm

So I'm working on Roz, my bioloid quad walker, and I've built a new head with five VL53L1X sensors in it.

It turns out the OpenMV project has a perfectly functional VL53L1X library:

https://github.com/openmv/openmv/blob/m ... vl53l1x.py

That works for a single sensor. I added a new method to allow the user to change the id of the current sensor:

Code: Select all

    def change_id(self, new_id):
        self.writeReg(0x0001, new_id & 0x7F)
        pyb.delay(50)
        self.address = new_id        
You need a lot of pauses in the right places to initialize 5 of the sensors, but it works, and I have it running.

dukeduck
Posts: 22
Joined: Thu Aug 29, 2019 2:06 pm

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by dukeduck » Fri Jan 01, 2021 2:55 pm

Thanks for the link! I was looking for this driver for quite a while.

Also, would you provide an example for your change_ide method, say how to correctly initialize 5 sensors? What's the type of the variable new_id?

Thanks again.
JonHylands wrote:
Fri Nov 27, 2020 9:59 pm
So I'm working on Roz, my bioloid quad walker, and I've built a new head with five VL53L1X sensors in it.

It turns out the OpenMV project has a perfectly functional VL53L1X library:

https://github.com/openmv/openmv/blob/m ... vl53l1x.py

That works for a single sensor. I added a new method to allow the user to change the id of the current sensor:

Code: Select all

    def change_id(self, new_id):
        self.writeReg(0x0001, new_id & 0x7F)
        pyb.delay(50)
        self.address = new_id        
You need a lot of pauses in the right places to initialize 5 of the sensors, but it works, and I have it running.

User avatar
JonHylands
Posts: 69
Joined: Sun Dec 29, 2013 1:33 am

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by JonHylands » Fri Jan 01, 2021 3:10 pm

Okay, here's my code, which works pretty much 100% of the time. Note that the delays are absolutely required, but the times may need optimization. The values that I use work, but you might be able to get away with shorter delays. You will of course want to change out the shutdown pin definitions with the pins you have them hooked up to. Note also I assume that self.i2c is already set with something like this:

Code: Select all

from machine import I2C
self.i2c = I2C(HEAD_I2C_BUS)
In my case, the HEAD_I2C_BUS is 1, since I am using I2C_1.

Note also that it is really critical to pull all five sensor shutdown pins low at the beginning - if you do a soft-reboot of your MIcroPython processor, and don't power cycle the sensors, you will try to initialize them from whatever state they are in, including their new addresses, so this routine wouldn't work. When you shut them down with the shutdown lines, you basically power cycle them, and when you start each one up, it comes up in a fresh state with the default address.

Code: Select all

HEAD_SHUTDOWN_LEFT = pyb.Pin.cpu.C3
HEAD_SHUTDOWN_FRONT_LEFT = pyb.Pin.cpu.B9
HEAD_SHUTDOWN_FRONT = pyb.Pin.cpu.B8
HEAD_SHUTDOWN_FRONT_RIGHT = pyb.Pin.cpu.C6
HEAD_SHUTDOWN_RIGHT = pyb.Pin.cpu.C7

HEAD_RANGE_DEFAULT_ADDRESS = 0x29
HEAD_RANGE_FRONT_ADDRESS = 0x30
HEAD_RANGE_FRONT_LEFT_ADDRESS = 0x31
HEAD_RANGE_FRONT_RIGHT_ADDRESS = 0x32
HEAD_RANGE_LEFT_ADDRESS = 0x33
HEAD_RANGE_RIGHT_ADDRESS = 0x34


  def initializeRangeSensors(self):
    shutdown_left_pin = pyb.Pin(HEAD_SHUTDOWN_LEFT, pyb.Pin.OUT_PP)
    shutdown_front_left_pin = pyb.Pin(HEAD_SHUTDOWN_FRONT_LEFT, pyb.Pin.OUT_PP)
    shutdown_front_pin = pyb.Pin(HEAD_SHUTDOWN_FRONT, pyb.Pin.OUT_PP)
    shutdown_front_right_pin = pyb.Pin(HEAD_SHUTDOWN_FRONT_RIGHT, pyb.Pin.OUT_PP)
    shutdown_right_pin = pyb.Pin(HEAD_SHUTDOWN_RIGHT, pyb.Pin.OUT_PP)
    # Shut down all the sensors
    shutdown_left_pin.value(0)
    shutdown_front_left_pin.value(0)
    shutdown_front_right_pin.value(0)
    shutdown_right_pin.value(0)
    shutdown_front_pin.value(0)
    pyb.delay(250)

    shutdown_front_pin.value(1)
    pyb.delay(250)
    self.front_lidar = VL53L1X(self.i2c, address=HEAD_RANGE_DEFAULT_ADDRESS)
    pyb.delay(100)
    self.front_lidar.change_id(HEAD_RANGE_FRONT_ADDRESS)
    pyb.delay(100)
    shutdown_front_left_pin.value(1)
    pyb.delay(100)
    self.front_left_lidar = VL53L1X(self.i2c, address=HEAD_RANGE_DEFAULT_ADDRESS)
    pyb.delay(100)
    self.front_left_lidar.change_id(HEAD_RANGE_FRONT_LEFT_ADDRESS)
    pyb.delay(100)
    shutdown_front_right_pin.value(1)
    pyb.delay(100)
    self.front_right_lidar = VL53L1X(self.i2c, address=HEAD_RANGE_DEFAULT_ADDRESS)
    pyb.delay(100)
    self.front_right_lidar.change_id(HEAD_RANGE_FRONT_RIGHT_ADDRESS)
    pyb.delay(100)
    shutdown_left_pin.value(1)
    pyb.delay(100)
    self.left_lidar = VL53L1X(self.i2c, address=HEAD_RANGE_DEFAULT_ADDRESS)
    pyb.delay(100)
    self.left_lidar.change_id(HEAD_RANGE_LEFT_ADDRESS)
    pyb.delay(100)
    shutdown_right_pin.value(1)
    pyb.delay(100)
    self.right_lidar = VL53L1X(self.i2c, address=HEAD_RANGE_DEFAULT_ADDRESS)
    pyb.delay(100)
    self.right_lidar.change_id(HEAD_RANGE_RIGHT_ADDRESS)
    pyb.delay(100)
Let me know if this works for you.

dukeduck
Posts: 22
Joined: Thu Aug 29, 2019 2:06 pm

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by dukeduck » Sun Jan 03, 2021 7:27 am

Thanks a lot. I ordered a few of them, and will test when I get them.

ghilliesuit
Posts: 15
Joined: Fri Feb 25, 2022 2:55 am

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by ghilliesuit » Wed Jul 27, 2022 12:43 am

The following code works on my Raspberry Pi Pico running uPy v1.19. This code was adapted from the OpenMV file which available on their github. I added a few things and cut down on some of the sleep times.

Code: Select all

import rp2
from machine import Pin, I2C
import utime

VL51L1X_DEFAULT_CONFIGURATION = bytes([
0x00, # 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C), else don't touch */
0x00, # 0x2e : bit 0 if I2C pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD) */
0x00, # 0x2f : bit 0 if GPIO pulled up at 1.8V, else set bit 0 to 1 (pull up at AVDD) */
0x01, # 0x30 : set bit 4 to 0 for active high interrupt and 1 for active low (bits 3:0 must be 0x1), use SetInterruptPolarity() */
0x02, # 0x31 : bit 1 = interrupt depending on the polarity, use CheckForDataReady() */
0x00, # 0x32 : not user-modifiable */
0x02, # 0x33 : not user-modifiable */
0x08, # 0x34 : not user-modifiable */
0x00, # 0x35 : not user-modifiable */
0x08, # 0x36 : not user-modifiable */
0x10, # 0x37 : not user-modifiable */
0x01, # 0x38 : not user-modifiable */
0x01, # 0x39 : not user-modifiable */
0x00, # 0x3a : not user-modifiable */
0x00, # 0x3b : not user-modifiable */
0x00, # 0x3c : not user-modifiable */
0x00, # 0x3d : not user-modifiable */
0xff, # 0x3e : not user-modifiable */
0x00, # 0x3f : not user-modifiable */
0x0F, # 0x40 : not user-modifiable */
0x00, # 0x41 : not user-modifiable */
0x00, # 0x42 : not user-modifiable */
0x00, # 0x43 : not user-modifiable */
0x00, # 0x44 : not user-modifiable */
0x00, # 0x45 : not user-modifiable */
0x20, # 0x46 : interrupt configuration 0->level low detection, 1-> level high, 2-> Out of window, 3->In window, 0x20-> New sample ready , TBC */
0x0b, # 0x47 : not user-modifiable */
0x00, # 0x48 : not user-modifiable */
0x00, # 0x49 : not user-modifiable */
0x02, # 0x4a : not user-modifiable */
0x0a, # 0x4b : not user-modifiable */
0x21, # 0x4c : not user-modifiable */
0x00, # 0x4d : not user-modifiable */
0x00, # 0x4e : not user-modifiable */
0x05, # 0x4f : not user-modifiable */
0x00, # 0x50 : not user-modifiable */
0x00, # 0x51 : not user-modifiable */
0x00, # 0x52 : not user-modifiable */
0x00, # 0x53 : not user-modifiable */
0xc8, # 0x54 : not user-modifiable */
0x00, # 0x55 : not user-modifiable */
0x00, # 0x56 : not user-modifiable */
0x38, # 0x57 : not user-modifiable */
0xff, # 0x58 : not user-modifiable */
0x01, # 0x59 : not user-modifiable */
0x00, # 0x5a : not user-modifiable */
0x08, # 0x5b : not user-modifiable */
0x00, # 0x5c : not user-modifiable */
0x00, # 0x5d : not user-modifiable */
0x01, # 0x5e : not user-modifiable */
0xdb, # 0x5f : not user-modifiable */
0x0f, # 0x60 : not user-modifiable */
0x01, # 0x61 : not user-modifiable */
0xf1, # 0x62 : not user-modifiable */
0x0d, # 0x63 : not user-modifiable */
0x01, # 0x64 : Sigma threshold MSB (mm in 14.2 format for MSB+LSB), use SetSigmaThreshold(), default value 90 mm  */
0x68, # 0x65 : Sigma threshold LSB */
0x00, # 0x66 : Min count Rate MSB (MCPS in 9.7 format for MSB+LSB), use SetSignalThreshold() */
0x80, # 0x67 : Min count Rate LSB */
0x08, # 0x68 : not user-modifiable */
0xb8, # 0x69 : not user-modifiable */
0x00, # 0x6a : not user-modifiable */
0x00, # 0x6b : not user-modifiable */
0x00, # 0x6c : Intermeasurement period MSB, 32 bits register, use SetIntermeasurementInMs() */
0x00, # 0x6d : Intermeasurement period */
0x0f, # 0x6e : Intermeasurement period */
0x89, # 0x6f : Intermeasurement period LSB */
0x00, # 0x70 : not user-modifiable */
0x00, # 0x71 : not user-modifiable */
0x00, # 0x72 : distance threshold high MSB (in mm, MSB+LSB), use SetD:tanceThreshold() */
0x00, # 0x73 : distance threshold high LSB */
0x00, # 0x74 : distance threshold low MSB ( in mm, MSB+LSB), use SetD:tanceThreshold() */
0x00, # 0x75 : distance threshold low LSB */
0x00, # 0x76 : not user-modifiable */
0x01, # 0x77 : not user-modifiable */
0x0f, # 0x78 : not user-modifiable */
0x0d, # 0x79 : not user-modifiable */
0x0e, # 0x7a : not user-modifiable */
0x0e, # 0x7b : not user-modifiable */
0x00, # 0x7c : not user-modifiable */
0x00, # 0x7d : not user-modifiable */
0x02, # 0x7e : not user-modifiable */
0xc7, # 0x7f : ROI center, use SetROI() */
0xff, # 0x80 : XY ROI (X=Width, Y=Height), use SetROI() */
0x9B, # 0x81 : not user-modifiable */
0x00, # 0x82 : not user-modifiable */
0x00, # 0x83 : not user-modifiable */
0x00, # 0x84 : not user-modifiable */
0x01, # 0x85 : not user-modifiable */
0x01, # 0x86 : clear interrupt, use ClearInterrupt() */
0x40  # 0x87 : start ranging, use StartRanging() or StopRanging(), If you want an automatic start after VL53L1X_init() call, put 0x40 in location 0x87 */
])

class VL53L1X:
    def __init__(self,  i2c):
        self.i2c = i2c
        self.address = 0x29
        self.reset()
        utime.sleep(1)
        if self.read_model_id() != 0xEACC:
            raise RuntimeError('Failed to find expected ID register values. Check wiring!')
        # write default configuration
        self.i2c.writeto_mem(self.address, 0x2D, VL51L1X_DEFAULT_CONFIGURATION, addrsize=16)
        utime.sleep(1)
        # the API triggers this change in VL53L1_init_and_start_range() once a
        # measurement is started; assumes MM1 and MM2 are disabled
        self.writeReg16Bit(0x001E, self.readReg16Bit(0x0022) * 4)
        utime.sleep(1)

    def writeReg(self, reg, value):
        return self.i2c.writeto_mem(self.address, reg, bytes([value]), addrsize=16)
    def writeReg16Bit(self, reg, value):
        return self.i2c.writeto_mem(self.address, reg, bytes([(value >> 8) & 0xFF, value & 0xFF]), addrsize=16)
    def readReg(self, reg):
        return self.i2c.readfrom_mem(self.address, reg, 1, addrsize=16)[0]
    def readReg16Bit(self, reg):
        data = self.i2c.readfrom_mem(self.address, reg, 2, addrsize=16)
        return (data[0]<<8) + data[1]
    def read_model_id(self):
        return self.readReg16Bit(0x010F)
    def reset(self):
        self.writeReg(0x0000, 0x00)
        utime.sleep(1)
        self.writeReg(0x0000, 0x01)
    def read(self):
        data = self.i2c.readfrom_mem(self.address, 0x0089, 17, addrsize=16) # RESULT__RANGE_STATUS
        range_status = data[0]
        # report_status = data[1]
        stream_count = data[2]
        dss_actual_effective_spads_sd0 = (data[3]<<8) + data[4]
        # peak_signal_count_rate_mcps_sd0 = (data[5]<<8) + data[6]
        ambient_count_rate_mcps_sd0 = (data[7]<<8) + data[8]
        # sigma_sd0 = (data[9]<<8) + data[10]
        # phase_sd0 = (data[11]<<8) + data[12]
        final_crosstalk_corrected_range_mm_sd0 = (data[13]<<8) + data[14]
        peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = (data[15]<<8) + data[16]
        #status = None
        #if range_status in (17, 2, 1, 3):
            #status = "HardwareFail"
        #elif range_status == 13:
            #status = "MinRangeFail"
        #elif range_status == 18:
            #status = "SynchronizationInt"
        #elif range_status == 5:
            #status = "OutOfBoundsFail"
        #elif range_status == 4:
            #status = "SignalFail"
        #elif range_status == 6:
            #status = "SignalFail"
        #elif range_status == 7:
            #status = "WrapTargetFail"
        #elif range_status == 12:
            #status = "XtalkSignalFail"
        #elif range_status == 8:
            #status = "RangeValidMinRangeClipped"
        #elif range_status == 9:
            #if stream_count == 0:
                #status = "RangeValidNoWrapCheckFail"
            #else:
                #status = "OK"
        return final_crosstalk_corrected_range_mm_sd0

    def get_mm(self):
        return test.read()
    def get_in(self):
        return test.read()*0.0393701
    def get_ft(self):
        return test.read()*0.0393701/12
        
i2c = I2C(0,  freq=400000, sda=Pin(0), scl=Pin(1))
print(i2c.scan()[0])
test = VL53L1X(i2c)

print("OUTSIDE WHILE LOOP")

while(True):
    print("Distance in mm: ", test.get_mm() )
    print("Distance in in: ", test.get_in() )
    print("Distance in feet: ", test.get_ft() )
    utime.sleep(0.05)
        


User avatar
mcauser
Posts: 507
Joined: Mon Jun 15, 2015 8:03 am

Re: VL53L1X ToF distance sensor - adapting to micropython

Post by mcauser » Wed Aug 17, 2022 1:36 am

FYI. I added forks of some of deshipu's archived repos up on my github: https://github.com/mcauser?tab=repositories&q=deshipu
Less mercurial, more git.

Post Reply