Skip to main content
Associate
March 12, 2025
Solved

VL53L5CX address changes not working.

  • March 12, 2025
  • 1 reply
  • 972 views

I am trying to run multiple VL53L5CX-TOF-sensors with a single Pi Pico to be able to stitch together multiple of the sensor images.However for this to work I have to change the i2c addresses of the sensors.

I am trying to use the set_i2c_address function for this.

I have read the steps from the usage guide about enabling/disabling additional sensors on the same i2c bus via the LPn pin but whenever I try to call the set_i2c_address even with just a single sensor connected I get  RuntimeError: VL53L5CX: set_i2c_address error.

Currently I am using the Pimoroni Firmware that provides a driver accessible via micropython that just calls the ULD firmware / driver for the VL53L5CX. 


I am not sure if something about my approach/order is wrong or if there might be an issue with the ULD driver itself.

I would greatly appreciate any help/advise. This is the code I am currently using:

 

 

 

import pimoroni_i2c
import breakout_vl53l5cx
import time
from time import sleep
import machine

PIN_CONFIG_1 = {"sda": 6, "scl": 7}
#The following 2 lines are necessary if LPN is connected to the sensor.
#When there is no connection between a GPIO and LPN of the sensor this and the following line are not required.
#LPN would be needed to activate/decactivate when there are multiple devices on one i2c bus. 
lpn_pin = machine.Pin(10, machine.Pin.OUT) 
lpn_pin.value(1) # Enable sensor_1

time.sleep(3) #wait a long duration in case the sensor takes a long time to start. 

i2c_1 = pimoroni_i2c.PimoroniI2C(**PIN_CONFIG_1, baudrate=400_000) #same error with baud rate at 100k, 400k and 2_000k
print("I2C_1 devices found:", i2c_1.scan())

# Initialize sensor_1 with default address
sensor_1 = breakout_vl53l5cx.VL53L5CX(i2c_1)

# Change to a new 7-bit address (e.g., 0x2A)
new_i2c_address_7bit = 0x09 # Valid 7-bit address (0x00 to 0x7F)
sensor_1.set_i2c_address(new_i2c_address_7bit) #this always throws the error.

# Verify address change
print("Scanning I2C after address change:", i2c_1.scan())

# Continue with configuration...
sensor_1.set_resolution(breakout_vl53l5cx.RESOLUTION_8X8)
sensor_1.set_ranging_frequency_hz(30)
sensor_1.start_ranging()

while True:
 if sensor_1.data_ready():
 avg_distance_1 = sensor_1.get_data().distance_avg
 avg_reflectance_1 = sensor_1.get_data().reflectance_avg 
 print(f"Sensor 1 Avg Distance: {avg_distance_1:.2f} mm, Avg Reflectance: {avg_reflectance_1:.2f}")
 sleep(1)

 

 

Edit: As I thought my issue might lie with the fact that I initialize the sensor before attempting to change the address I implemented the set_i2c_address function myself now using the ULD driver as inspiration. However even with this the address doesn't seem to get changed and stays at 0x52 (0x29 as a 7 bit due to the bitshift or 41 in decimal):

 

MPY: soft reboot
Available i2c before attempting address change [41]
Tried to change I2C address from 41 to 32
Available i2c after address change [41]
Available i2c devices after attempting address change [41]
Traceback (most recent call last):
 File "<stdin>", line 44, in <module>
RuntimeError: VL53L5CX: init error

 

This is the code I used for the approach to change the address before initializing the sensor:

 

import pimoroni_i2c
import time
import machine
import breakout_vl53l5cx


# Function to change the I2C address
def vl53l5cx_set_i2c_address(i2c, old_address, new_address):

 i2c.writeto_mem(old_address, 0x7fff, bytes([0x00]))
 time.sleep(1)
 i2c.writeto_mem(old_address, 0x04, bytes([(new_address << 1)]))
 time.sleep(1)
 i2c.writeto_mem(old_address, 0x7fff, bytes([0x02])) #I believe this might need to be new_address but that results in [Errno 5] EIO as the address isn't actually changed before.
 time.sleep(1)
 print(f"Tried to change I2C address from {old_address} to {new_address}")
 print("Available i2c after address change", i2c_1.scan())
 

# Configuration
PIN_CONFIG_1 = {"sda": 6, "scl": 7}
#The following 2 lines are necessary if LPN is connected to the sensor.
#When there is no connection between a GPIO and LPN of the sensor this and the following line are not required.
#LPN would be needed to activate/decactivate when there are multiple devices on one i2c bus.
lpn_pin = machine.Pin(10, machine.Pin.OUT)
lpn_pin.value(1) # Enable sensor_1
time.sleep(3) #wait a long duration in case the sensor takes a long time to start. 



i2c_1 = pimoroni_i2c.PimoroniI2C(**PIN_CONFIG_1, baudrate=2_000_000)
default_address = 0x29 # default 7 bit address, shifted from 0x52, is 41 in decimal. 
new_i2c_address_7bit = 0x20 # Example: new address

print("Available i2c before attempting address change", i2c_1.scan())

# Change the I2C address BEFORE initializing the sensor
vl53l5cx_set_i2c_address(i2c_1, default_address, new_i2c_address_7bit)
print("Available i2c devices after attempting address change", i2c_1.scan()) 

# Initialize the sensor with the new address 
sensor_1 = breakout_vl53l5cx.VL53L5CX(i2c_1, new_i2c_address_7bit ) 
 # this fails to initialize. Makes sense as the address wasn't changed. When using default_address here it still connects to the sensor just fine. 

# Verify address change
print("Scanning I2C after address change:", i2c_1.scan())

# Continue with sensor configuration
sensor_1.set_resolution(breakout_vl53l5cx.RESOLUTION_8X8)
sensor_1.set_ranging_frequency_hz(30)
sensor_1.start_ranging()

while True:
 if sensor_1.data_ready():
 data = sensor_1.get_data()
 avg_distance_1 = data.distance_avg
 avg_reflectance_1 = data.reflectance_avg
 print(f"Sensor 1 Avg Distance: {avg_distance_1:.2f} mm, Avg Reflectance: {avg_reflectance_1:.2f}")
 time.sleep(1)

 

 I would really appreciate any hints to what I am doing wrong.

Best answer by John E KVAM

I'm not familiar with that code, but I'm going to make some guesses.

the lines:

# Initialize sensor_1 with default address
sensor_1 = breakout_vl53l5cx.VL53L5CX(i2c_1)

# Change to a new 7-bit address (e.g., 0x2A)
new_i2c_address_7bit = 0x09 # Valid 7-bit address (0x00 to 0x7F)
sensor_1.set_i2c_address(new_i2c_address_7bit) #this always throws the error.

seem problematic. 

One must send the new address to the device at the old address. 

So if you initialize the sensor with:

sensor_1 = breakout_vl53l5cx.VL53L5CX(i2c_1)

Then you would have to change the address with something like:

sensor_1.set_i2c_address(i2c_1,new_i2c_address_7bit)

So you are talking to the current address (i2c_1) and sending the command to change it to new_i2c_address-7bit)

I think you were heading in the right direction with your last bit of code. 

Also, I'm not a big fan of small i2c addresses. I'd suggest going higher than lower. 0x39 and 0x49 are what I use.

(Might just be superstition on my part though.)

1 reply

John E KVAM
John E KVAMBest answer
ST Employee
March 12, 2025

I'm not familiar with that code, but I'm going to make some guesses.

the lines:

# Initialize sensor_1 with default address
sensor_1 = breakout_vl53l5cx.VL53L5CX(i2c_1)

# Change to a new 7-bit address (e.g., 0x2A)
new_i2c_address_7bit = 0x09 # Valid 7-bit address (0x00 to 0x7F)
sensor_1.set_i2c_address(new_i2c_address_7bit) #this always throws the error.

seem problematic. 

One must send the new address to the device at the old address. 

So if you initialize the sensor with:

sensor_1 = breakout_vl53l5cx.VL53L5CX(i2c_1)

Then you would have to change the address with something like:

sensor_1.set_i2c_address(i2c_1,new_i2c_address_7bit)

So you are talking to the current address (i2c_1) and sending the command to change it to new_i2c_address-7bit)

I think you were heading in the right direction with your last bit of code. 

Also, I'm not a big fan of small i2c addresses. I'd suggest going higher than lower. 0x39 and 0x49 are what I use.

(Might just be superstition on my part though.)

FY_itAuthor
Associate
March 12, 2025

Thanks for the help.
The last part I posted was like you wrote actually quite close to working. I needed to modify one part about the address shifting and had to specify a 2 byte length for the memory writes. Thanks for the advise about going higher with my i2c-addresses.

In case somebody else comes across this post: Here is a working version I cleaned up a bit that supports 2 sensors and easily allows the addition of more sensors all with their own specified i2c-addresses.

import pimoroni_i2c
import time
import machine
import breakout_vl53l5cx

# Function to change the I2C address
def vl53l5cx_set_i2c_address(i2c, old_address, new_address):
 # Unlock the sensor's I2C address change functionality
 i2c.writeto_mem(old_address, 0x7fff, bytes([0x00]), addrsize=16)
 # Write the new I2C address 
 i2c.writeto_mem(old_address, 0x04, bytes([(new_address)]), addrsize=16)
 # Lock the sensor's I2C address change functionality
 i2c.writeto_mem(new_address, 0x7fff, bytes([0x02]), addrsize=16)
 time.sleep(1)
 
 print(f"Changed I2C address from {hex(old_address)} to {hex(new_address)}")
 print("Available I2C devices after address change:", i2c.scan())

# Configuration
PIN_CONFIG = {"sda": 6, "scl": 7}
I2C_BAUDRATE = 2_000_000
DEFAULT_ADDRESS = 0x29 # Default 7-bit address (0x52 shifted right by 1)

# Sensor configuration
SENSORS = {
 "sensor_1": {
 "lpn_pin": machine.Pin(10, machine.Pin.OUT),
 "new_address": 0x40,
 },
 "sensor_2": {
 "lpn_pin": machine.Pin(11, machine.Pin.OUT),
 "new_address": 0x50,
 },
}

# Initialize I2C
i2c = pimoroni_i2c.PimoroniI2C(**PIN_CONFIG, baudrate=I2C_BAUDRATE)

# Disable all sensors initially
for config in SENSORS.values():
 config["lpn_pin"].value(0)

# Initialize and configure sensors
sensors = {}
for name, config in SENSORS.items():
 # Disable all sensors except the one being configured
 for sensor_name, sensor_config in SENSORS.items():
 sensor_config["lpn_pin"].value(1 if sensor_name == name else 0)
 time.sleep(3) # Wait for the sensor to stabilize

 # Change the I2C address
 vl53l5cx_set_i2c_address(i2c, DEFAULT_ADDRESS, config["new_address"])

 # Initialize the sensor
 sensors[name] = breakout_vl53l5cx.VL53L5CX(i2c, config["new_address"])
 sensors[name].set_resolution(breakout_vl53l5cx.RESOLUTION_8X8)
 sensors[name].set_ranging_frequency_hz(30)
 sensors[name].start_ranging()

 print(f"{name} initialized with address {hex(config['new_address'])}")

# Enable all sensors after configuration
for config in SENSORS.values():
 config["lpn_pin"].value(1)
time.sleep(3) # Wait for all sensors to stabilize

# Main loop to read sensor data
while True:
 for name, sensor in sensors.items():
 if sensor.data_ready():
 data = sensor.get_data()
 avg_distance = data.distance_avg
 avg_reflectance = data.reflectance_avg
 print(f"{name} - Avg Distance: {avg_distance:.2f} mm, Avg Reflectance: {avg_reflectance:.2f}")
 time.sleep(1)