2025-03-12 9:02 AM - edited 2025-03-12 10:53 AM
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.
Solved! Go to Solution.
2025-03-12 10:36 AM
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.)
2025-03-12 10:36 AM
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.)
2025-03-12 11:50 AM
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)