Code for running multiple distance sensors simultaneously not working

So our project is to run 20 VL53L4CX ToF Sensors simultaneously using daisy-chaining so we can get outputs from all 20 sensors. The code however does not work for more than 1 sensor. Below is the code we have for 2 sensors, and the error we get is written below.

/* Includes ------------------------------------------------------------------*/
#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cx_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#define SerialPort Serial

#define XSHUT_PIN A1
#define Max_Sensors 2

// Components.
TwoWire *DEV_I2C = &Wire;

// The following object assumes a zero parameter constructor exists.
VL53L4CX sensor_vl53l4cx_sat1;
VL53L4CX sensor_vl53l4cx_sat2;

/* VL53L4CX_RangeStatusCode --------------------------------------------------*/
String VL53L4CX_RangeStatusCode(uint8_t status)
{
  switch (status)
  {
    case VL53L4CX_RANGESTATUS_RANGE_VALID:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID";
    case VL53L4CX_RANGESTATUS_SIGMA_FAIL:
      return "VL53L4CX_RANGESTATUS_SIGMA_FAIL";
    case VL53L4CX_RANGESTATUS_RANGE_VALID_MIN_RANGE_CLIPPED:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID_MIN_RANGE_CLIPPED";
    case VL53L4CX_RANGESTATUS_OUTOFBOUNDS_FAIL:
      return "VL53L4CX_RANGESTATUS_OUTOFBOUNDS_FAIL";
    case VL53L4CX_RANGESTATUS_HARDWARE_FAIL:
      return "VL53L4CX_RANGESTATUS_HARDWARE_FAIL";
    case VL53L4CX_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL";
    case VL53L4CX_RANGESTATUS_WRAP_TARGET_FAIL:
      return "VL53L4CX_RANGESTATUS_WRAP_TARGET_FAIL";
    case VL53L4CX_RANGESTATUS_PROCESSING_FAIL:
      return "VL53L4CX_RANGESTATUS_PROCESSING_FAIL";
    case VL53L4CX_RANGESTATUS_XTALK_SIGNAL_FAIL:
      return "VL53L4CX_RANGESTATUS_XTALK_SIGNAL_FAIL";
    case VL53L4CX_RANGESTATUS_SYNCRONISATION_INT:
      return "VL53L4CX_RANGESTATUS_SYNCRONISATION_INT";
    case VL53L4CX_RANGESTATUS_RANGE_VALID_MERGED_PULSE:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID_MERGED_PULSE";
    case VL53L4CX_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL:
      return "VL53L4CX_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL";
    case VL53L4CX_RANGESTATUS_MIN_RANGE_FAIL:
      return "VL53L4CX_RANGESTATUS_MIN_RANGE_FAIL";
    case VL53L4CX_RANGESTATUS_RANGE_INVALID:
      return "VL53L4CX_RANGESTATUS_RANGE_INVALID";
    case VL53L4CX_RANGESTATUS_NONE:
      return "VL53L4CX_RANGESTATUS_NONE";
    default:
      return ("UNKNOWN STATUS: " + String(status));
  }
}

/* I2CDeviceAvailable --------------------------------------------------------*/
bool I2CDeviceAvailable(uint8_t address, TwoWire **wire)
{
  byte error = 1;
  bool available = false;

  // Check if device is available at the expected address
  Wire.begin();
  Wire.beginTransmission(address);
  error = Wire.endTransmission();

  if (error == 0) {
    *wire = &Wire;
    available = true;

    Serial.print("  I2c Device Found at Address 0x");
    Serial.print(address, HEX); Serial.println(" on Wire");
  }
  Wire.end();

  return available;
}

/* I2CDeviceAvailable --------------------------------------------------------*/
void InitializeDevice(VL53L4CX sensor, TwoWire *DEV_I2C) {

  VL53L4CX_Error error = VL53L4CX_ERROR_NONE;

  // Determine the ToF device's address and Twowire device to which it is connected.
  // The default address must be shifted right 1 bit to match the expected physical
  // I2c address.
  if (I2CDeviceAvailable(VL53L4CX_DEFAULT_DEVICE_ADDRESS >> 1, &DEV_I2C)) {
    DEV_I2C->begin();
    sensor.setI2cDevice(DEV_I2C);
    sensor.setXShutPin(XSHUT_PIN);
  } else {
    Serial.println("Failure Initializing I2c Port or No I2c device found");
    while (true)
      delay(10);
  }

  // Configure VL53L4CX satellite component.
  sensor.begin();

  // Switch off VL53L4CX satellite component.
  sensor.VL53L4CX_Off();

  //Initialize VL53L4CX satellite component.
  error = sensor.InitSensor(VL53L4CX_DEFAULT_DEVICE_ADDRESS);

  if (error != VL53L4CX_ERROR_NONE) {
    Serial.print("Error Initializing Sensor: ");
    Serial.println(error);
    while (true)
      delay(10);
  }

  // Start Measurements
  sensor.VL53L4CX_StartMeasurement();
}

/* Setup ---------------------------------------------------------------------*/
void setup()
{
  // delay to view any serial setup output before any loop output is displayed
  delay(10000);

#if defined(LED_BUILTIN)
  // Led.
  pinMode(LED_BUILTIN, OUTPUT);
#endif

  // Initialize serial for output.
  SerialPort.begin(115200);
  SerialPort.println("Starting...");

  // Initialize Sensors
  InitializeDevice(sensor_vl53l4cx_sat1, &Wire);
  InitializeDevice(sensor_vl53l4cx_sat2, &Wire);
}

/* Loop ---------------------------------------------------------------------*/
void loop()
{
  VL53L4CX_MultiRangingData_t MultiRangingData;
  VL53L4CX_MultiRangingData_t *pMultiRangingData = &MultiRangingData;
  uint8_t NewDataReady = 0;
  int no_of_object_found = 0, j;
  char report[64];
  int status;

  do {
    status = sensor_vl53l4cx_sat1.VL53L4CX_GetMeasurementDataReady(&NewDataReady);
  } while (!NewDataReady);

#if defined(LED_BUILTIN)
  //Led on
  digitalWrite(LED_BUILTIN, HIGH);
#endif

  if ((!status) && (NewDataReady != 0)) {
    status = sensor_vl53l4cx_sat1.VL53L4CX_GetMultiRangingData(pMultiRangingData);
    no_of_object_found = pMultiRangingData->NumberOfObjectsFound;

    snprintf(report, sizeof(report), "VL53L4CX Satellite: Count=%d, #Objs=%1d ", pMultiRangingData->StreamCount, no_of_object_found);
    SerialPort.print(report);
    for (j = 0; j < no_of_object_found; j++) {
      SerialPort.print("\r\n                               ");
      SerialPort.print("status=");
      SerialPort.print(pMultiRangingData->RangeData[j].RangeStatus);
      SerialPort.print(", D=");
      SerialPort.print(pMultiRangingData->RangeData[j].RangeMilliMeter);
      SerialPort.print("mm");
    }
    SerialPort.println();
    if (status == 0) {
      status = sensor_vl53l4cx_sat1.VL53L4CX_ClearInterruptAndStartMeasurement();
    }
  }

#if defined(LED_BUILTIN)
  digitalWrite(LED_BUILTIN, LOW);
#endif
}

This code gives the error,

c:/users/jhisc/appdata/local/arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: C:\Users\jhisc\AppData\Local\Temp\arduino\sketches\97702E6FA838A0E817F30259A3D241BF/sketch_sep5aV2.ino.elf section .bss' will not fit in region RAM'
c:/users/jhisc/appdata/local/arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: section .stack_dummy VMA [20007b00,20007eff] overlaps section .bss VMA [20000290,2000dcf3]
c:/users/jhisc/appdata/local/arduino15/packages/arduino/tools/arm-none-eabi-gcc/7-2017q4/bin/../lib/gcc/arm-none-eabi/7.2.1/../../../../arm-none-eabi/bin/ld.exe: region `RAM' overflowed by 0 bytes
collect2.exe: error: ld returned 1 exit status

exit status 1

Compilation error: exit status 1

We believe the arduino we are using (UNO R4 WIFI) has enough memory to run all 20 sensors.

Is there any way we can access the ESP32 memory on the board or if there's a way we can initialize the sensors so we can have that memory be stored in flash memory instead of RAM?

If we were to flash the esp32 firmware in order to program on it directly. Would it still be able to access the I2C bus? I know that the esp32 can be accessed by itself through the documentation

that's kind of where we need help. Could the error be an addressing issue on the bus where we are accessing some memory that is not even present?

Yes, the I2C address on each sensor can be changed.

Sorry, the previous code is for individual initializations. This code is initialized with the array that we wrote. However, the error is the same, but at least this might clear it up a little more.

We create an address in setup() and called the function initializeSensor() which calls initSensor() setting an address on the index representing the sensor. That is the approach we took.

#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cx_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#define SerialPort Serial

#define XSHUT_PIN_BASE A1 // Define the base pin for XSHUT (adjust as needed)
#define MAX_SENSORS 2 // Adjust based on your setup
#define STARTING_ADDRESS 0x29 // Starting I2C address for VL53L4CX

// Display valid results "graphically" when true instead of details
bool showGraphicly = true;

// Components.
TwoWire *DEV_I2C = &Wire;
VL53L4CX sensors[MAX_SENSORS];

// Function to print the range status code as a string
String VL53L4CX_RangeStatusCode(uint8_t status) {
  switch (status) {
    case VL53L4CX_RANGESTATUS_RANGE_VALID:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID";
    case VL53L4CX_RANGESTATUS_SIGMA_FAIL:
      return "VL53L4CX_RANGESTATUS_SIGMA_FAIL";
    case VL53L4CX_RANGESTATUS_RANGE_VALID_MIN_RANGE_CLIPPED:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID_MIN_RANGE_CLIPPED";
    case VL53L4CX_RANGESTATUS_OUTOFBOUNDS_FAIL:
      return "VL53L4CX_RANGESTATUS_OUTOFBOUNDS_FAIL";
    case VL53L4CX_RANGESTATUS_HARDWARE_FAIL:
      return "VL53L4CX_RANGESTATUS_HARDWARE_FAIL";
    case VL53L4CX_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL";
    case VL53L4CX_RANGESTATUS_WRAP_TARGET_FAIL:
      return "VL53L4CX_RANGESTATUS_WRAP_TARGET_FAIL";
    case VL53L4CX_RANGESTATUS_PROCESSING_FAIL:
      return "VL53L4CX_RANGESTATUS_PROCESSING_FAIL";
    case VL53L4CX_RANGESTATUS_XTALK_SIGNAL_FAIL:
      return "VL53L4CX_RANGESTATUS_XTALK_SIGNAL_FAIL";
    case VL53L4CX_RANGESTATUS_SYNCRONISATION_INT:
      return "VL53L4CX_RANGESTATUS_SYNCRONISATION_INT";
    case VL53L4CX_RANGESTATUS_RANGE_VALID_MERGED_PULSE:
      return "VL53L4CX_RANGESTATUS_RANGE_VALID_MERGED_PULSE";
    case VL53L4CX_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL:
      return "VL53L4CX_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL";
    case VL53L4CX_RANGESTATUS_MIN_RANGE_FAIL:
      return "VL53L4CX_RANGESTATUS_MIN_RANGE_FAIL";
    case VL53L4CX_RANGESTATUS_RANGE_INVALID:
      return "VL53L4CX_RANGESTATUS_RANGE_INVALID";
    case VL53L4CX_RANGESTATUS_NONE:
      return "VL53L4CX_RANGESTATUS_NONE";
    default:
      return ("UNKNOWN STATUS: " + String(status));
  }
}

// Function to check if the I2C device is available
bool I2CDeviceAvailable(uint8_t address, TwoWire **wire) {
  byte error = 1;
  bool available = false;

  // Check if device is available at expected address
  Wire.begin();
  Wire.beginTransmission(address);
  error = Wire.endTransmission();

  if (error == 0) {
    *wire = &Wire;
    available = true;

    Serial.print("I2C Device Found at Address 0x");
    Serial.print(address, HEX); Serial.println(" on Wire");
  }
  Wire.end();

  return available;
}

// Function to initialize each sensor
bool initializeSensor(int index, uint8_t address) {
  pinMode(XSHUT_PIN_BASE + index, OUTPUT);
  digitalWrite(XSHUT_PIN_BASE + index, LOW);
  delay(10); // Allow time for XSHUT to take effect

  digitalWrite(XSHUT_PIN_BASE + index, HIGH);
  delay(100); // Allow time for the sensor to power up

  if (I2CDeviceAvailable(address, &DEV_I2C)) {
    DEV_I2C->begin();
    sensors[index].setI2cDevice(DEV_I2C);
    sensors[index].setXShutPin(XSHUT_PIN_BASE);
    sensors[index].begin();
    sensors[index].VL53L4CX_Off();
    VL53L4CX_Error error = sensors[index].InitSensor(address);
    if (error != VL53L4CX_ERROR_NONE) {
      Serial.print("Error Initializing Sensor at Address 0x");
      Serial.print(address, HEX);
      Serial.print(": ");
      Serial.println(error);
      return false;
    }
    return true;
  } else {
    Serial.print("Failure Initializing I2C Port or No I2C device found at Address 0x");
    Serial.print(address, HEX);
    return false;
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("Starting...");

#if defined(LED_BUILTIN)
  pinMode(LED_BUILTIN, OUTPUT);
#endif

  bool allInitialized = true;
  for (int i = 0; i < MAX_SENSORS; i++) {
    uint8_t address = STARTING_ADDRESS + i * 2; // Adjust based on your address scheme
    if (!initializeSensor(i, address)) {
      allInitialized = false;
      Serial.print(" Sensor initialization failed at index ");
      Serial.println(i);
      while (true) 
        delay(10); // Halt execution on initialization failure
    }
  }

  if (allInitialized) {
    for (int i = 0; i < MAX_SENSORS; i++) {
      sensors[i].VL53L4CX_StartMeasurement();
    }
  }
}

void loop() {
  for (int i = 0; i < MAX_SENSORS; i++) {
    VL53L4CX_MultiRangingData_t MultiRangingData;
    VL53L4CX_MultiRangingData_t *pMultiRangingData = &MultiRangingData;
    uint8_t NewDataReady = 0;
    int status;

    do {
      status = sensors[i].VL53L4CX_GetMeasurementDataReady(&NewDataReady);
    } while (!NewDataReady);

#if defined(LED_BUILTIN)
    digitalWrite(LED_BUILTIN, HIGH);
#endif

    if ((!status) && (NewDataReady != 0)) {
      status = sensors[i].VL53L4CX_GetMultiRangingData(pMultiRangingData);
      int no_of_object_found = pMultiRangingData->NumberOfObjectsFound;

      if (showGraphicly) {
        Serial.print("Sensor ");
        Serial.print(i);
        Serial.println(":");

        for (int j = 0; j < no_of_object_found; j++) {
          if (pMultiRangingData->RangeData[j].RangeStatus == VL53L4CX_RANGESTATUS_RANGE_VALID ||
              pMultiRangingData->RangeData[j].RangeStatus == VL53L4CX_RANGESTATUS_RANGE_VALID_MERGED_PULSE) {
            int16_t mm = pMultiRangingData->RangeData[j].RangeMilliMeter;
            for (int k = 0; k < mm / 10; k++) Serial.print(" ");
            Serial.println(mm);
          }
        }
      } else {
        Serial.print("Sensor ");
        Serial.print(i);
        Serial.print(": Count=");
        Serial.print(pMultiRangingData->StreamCount);
        Serial.print(", #Objs=");
        Serial.print(no_of_object_found);
        Serial.println();

        for (int j = 0; j < no_of_object_found; j++) {
          Serial.print("  status=");
          Serial.print(pMultiRangingData->RangeData[j].RangeStatus);
          Serial.print(", D=");
          Serial.print(pMultiRangingData->RangeData[j].RangeMilliMeter);
          Serial.print("mm");
        }
      }

      if (status == 0) {
        status = sensors[i].VL53L4CX_ClearInterruptAndStartMeasurement();
      }
    }

#if defined(LED_BUILTIN)
    digitalWrite(LED_BUILTIN, LOW);
#endif
  }
}

In the for loop where i increments and multiplied by 2. This should change the address it is initializing.

/*
 * To use this sketch you need to connect the VL53L4CD satellite sensor directly to the Nucleo board with wires in this way:
 * pin 1 (GND) of the VL53L4CD satellite connected to GND of the Nucleo board
 * pin 2 (VDD) of the VL53L4CD satellite connected to 3V3 pin of the Nucleo board
 * pin 3 (SCL) of the VL53L4CD satellite connected to pin D15 (SCL) of the Nucleo board
 * pin 4 (SDA) of the VL53L4CD satellite connected to pin D14 (SDA) of the Nucleo board
 * pin 5 (GPIO1) of the VL53L4CD satellite connected to pin A2 of the Nucleo board
 * pin 6 (XSHUT) of the VL53L4CD satellite connected to pin A1 of the Nucleo board
 */
/* Includes ------------------------------------------------------------------*/
#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cx_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>

#define DEV_I2C Wire
#define SerialPort Serial

#ifndef LED_BUILTIN
  #define LED_BUILTIN 13
#endif
#define LedPin LED_BUILTIN

// Define constant(s)
#define MAX_SENSORS 5
#define BASE_ADDRESS 0x29

// Create instances of VL53L4CX sensor objects for each sensor
VL53L4CX* sensors[MAX_SENSORS];


/* Setup ---------------------------------------------------------------------*/

void setup()
{
  // Led.
  pinMode(LedPin, OUTPUT);

  // Initialize serial for output.
  SerialPort.begin(115200);
  SerialPort.println("Starting...");

  // Initialize I2C bus.
  DEV_I2C.begin();

  // Initialize each sensor and allocate memory for sensor data

  for (int i = 0; i< MAX_SENSORS; i++) {
    pinMode(A1 + i, OUTPUT);
    digitalWrite(A1 + i, LOW); // Start with sensor off
    delay(10); // Wait for XSHUT pin to settle
  }

  for (int i = 0; i < MAX_SENSORS; i++) {

    // Assign sensor with unique addresses
    uint8_t address = BASE_ADDRESS + i;

    // Initialize each sensor with its specific XSHUT pin
    digitalWrite(A1 + i, HIGH); // Power on sensor
    delay(100); // Give time for sensor to power up

    // Create sensor instances
    sensors[i] = new VL53L4CX(&DEV_I2C, A1);

    // Set device address
    sensors[i] -> VL53L4CX_SetDeviceAddress(address);

    // Initialize VL53L4CX sensor
    sensors[i]->begin(); // Configure VL53L4CX satellite component.
    sensors[i]->VL53L4CX_Off(); // Switch off VL53L4CX satellite component.
    sensors[i]->InitSensor(0x12); // Initialize VL53L4CX satellite component.
    sensors[i]->VL53L4CX_StartMeasurement(); // Start Measurements

  }
}

void loop()
{
  VL53L4CX_MultiRangingData_t MultiRangingData;
  VL53L4CX_MultiRangingData_t *pMultiRangingData = &MultiRangingData;
  uint8_t NewDataReady = 0;
  int no_of_object_found = 0, j;
  char report[64];
  int status;

  for (int i=0; i<MAX_SENSORS; i++) {
    do {
      status = sensors[i]->VL53L4CX_GetMeasurementDataReady(&NewDataReady);
    } while (!NewDataReady);

    //Led on
    digitalWrite(LedPin, HIGH);

    if ((!status) && (NewDataReady != 0)) {
      status = sensors[i]->VL53L4CX_GetMultiRangingData(pMultiRangingData);
      no_of_object_found = pMultiRangingData->NumberOfObjectsFound;
      snprintf(report, sizeof(report), "VL53L4CX Satellite: Count=%d, #Objs=%1d ", pMultiRangingData->StreamCount, no_of_object_found);
      SerialPort.print(report);
      for (j = 0; j < no_of_object_found; j++) {
        if (j != 0) {
          SerialPort.print("\r\n                               ");
        }
        SerialPort.print("status=");
        SerialPort.print(pMultiRangingData->RangeData[j].RangeStatus);
        SerialPort.print(", D=");
        SerialPort.print(pMultiRangingData->RangeData[j].RangeMilliMeter);
        SerialPort.print("mm");
        SerialPort.print(", Signal=");
        SerialPort.print((float)pMultiRangingData->RangeData[j].SignalRateRtnMegaCps / 65536.0);
        SerialPort.print(" Mcps, Ambient=");
        SerialPort.print((float)pMultiRangingData->RangeData[j].AmbientRateRtnMegaCps / 65536.0);
        SerialPort.print(" Mcps");
      }
      SerialPort.println("");
      if (status == 0) {
        status = sensors[i]->VL53L4CX_ClearInterruptAndStartMeasurement();
      }
    }

    digitalWrite(LedPin, LOW);
  }
  
}

We have changed the code so that it actually sets the device address and it compiles now but gives no output.

Well this is how we are told to change the address by the library so we went with that. Plus we had just added this and were still facing the same issues.

We are also aware that the line "sensors[i]->InitSensor(0x12);" line would be interfering with the code as it initializes all sensors to 1 address again, but it does not fix anything if removed or changed. Do you have any other ideas?

I was perusing the user manual (I know, what a concept) for the VL53L4CX and came across this:

I2C address
The default I2C address of the VL53L4CX is 0x52.
Some applications need to set a different I2C device address. This is the case, for example, when several
VL53L4CX parts share the same I2C bus.
The customer should apply the following procedure:
• The board mounting the VL53L4CX have to be designed carefully. The Xshut and the GPIO1 (interrupt) pins
have to be controlled individually for each VL53L4CX
• The host has to put in HW Standby, setting the Xshut pin low, all the VL53L4CX.
• The host raises the Xshut pin of 1 of the VL53L4CX
• The host calls the function VL53LX_SetDeviceAddress()
• The host repeats the latter three points since all the VL53L4CX addresses are correctly set.
For example, by calling the function: status = VL53LX_SetDeviceAddress(&VL53L4CXDev, WantedAddress)
the value of WantedAddress is set as the new I2C address.

Hey Emily! I believe we went over that and implemented it within the code.. However, we get no output but it at least compiles!

There seemed to be some question about that in the topic dialog so I thought it worth verifying that it's what the library actually does. You have posted no schematic of your project so it's not evident that you have the multiple devices wired correctly. It seems you are having a problem addressing multiple devices so thoroughly studying the process deserves doing.

I think individual GPIO1 pins as well if I'm reading the data sheet correctly.

Yes that would work, however we are taking a daisy-chaining approach (meaning connecting each sensor to another sensor , all on the same bus - VL53L4CX ToF sensor) so we would only need to use the i2c pins on the board. The 20 GPIO pins would work but that is not the approach we are taking.

The Xshut and GPIO pins are available on the breakout boards. Are you saying I can daisy chain the sensors but I will also need to connect to the GPIO pin on each sensor? And to daisy chain I need to initialize each sensor with a different Xshut pin making connections from my board to the sensor again in the same manner like the GPIO? Is the I2C connection on the sensor not already doing that?

@demonic_1000
If you were to post a schematic diagram showing how you are trying to hook all this up it might be possible to suggest how to make it work.

this is our approach

I2C is not a "daisy chain" bus. All the devices are connected in parallel and must either have unique addresses or a method of enabling/disabling them programmatically.

You will have to control each Xshut line individually in order to set their address. To use them, either individual interrupt lines to the controller will have to be used or you will have to create some sort of protocol for knowing when they have a new value. Some sort of priority interrupt controller, shift register, gate, etc.