Interactions between Wire1 & Wire2 I2C busses on Teensy 3.5?

Status
Not open for further replies.

paynterf

Well-known member
I have a project where I am attempting to use six ST Microelectronics VL53L0X Time-of-Flight (ToF) sensors grouped into two 3-element arrays for position/orientation sensing on an autonomous wall-following robot. I originally had both arrays installed on Wire1 of a Teensy 3.5, but discovered that only 4 of the 6 elements were reporting 'real' values. After screwing around for a while, I came to the conclusion that the integral 10K pullup resistors on each sensor (times 6) was too much for the I2C bus. So, I decided to move one of the two 3-element arrays to Wire2, so I would be driving three sets of 10K pullups on Wire1 and three sets of 10K pullups on Wire2.

Unfortunately, even with the arrays split over the two secondary I2C busses, I can't get all six sensors to operate properly. One array of three elements will report believable values, but one or more of the other array will not. For all the gory details, see my 'Paynters Palace' post on this subject.

So, it appears there is some sort of interaction going on between the two auxiliary I2C busses on the Teensy 3.5. Any ideas?

Frank
 
Six 10K resistors in parallel is not too much load. That's approx 1.7K total. Many projects use a 1K resistor and work very nicely, so whatever is going wrong here, I can say with confidence it's not too much load on SDA or SCL due to several 10K resistors.

Duplicate I2C addresses are absolutely a problem with any I2C device. The first thing I'd recommend is running the Wire lib Scanner example after your code has configured the sensors.

The next thing I'd investigate is power consumption. With the much larger LidarLite sensor, Teensy normally has just barely enough extra power to run 1 of them. These look smaller, but they are powering up tiny IR lasers. If all 6 try to run at the same time, your problems might be as "simple" as not having enough power.

A mysterious interaction between Wire1 and Wire2 seems very unlikely. I'm not absolutely ruling it out, but I am saying to check the address config and power, as those are much more likely causes of trouble.
 
Six 10K resistors in parallel is not too much load. That's approx 1.7K total. Many projects use a 1K resistor and work very nicely, so whatever is going wrong here, I can say with confidence it's not too much load on SDA or SCL due to several 10K resistors.

Duplicate I2C addresses are absolutely a problem with any I2C device. The first thing I'd recommend is running the Wire lib Scanner example after your code has configured the sensors.

The next thing I'd investigate is power consumption. With the much larger LidarLite sensor, Teensy normally has just barely enough extra power to run 1 of them. These look smaller, but they are powering up tiny IR lasers. If all 6 try to run at the same time, your problems might be as "simple" as not having enough power.

A mysterious interaction between Wire1 and Wire2 seems very unlikely. I'm not absolutely ruling it out, but I am saying to check the address config and power, as those are much more likely causes of trouble.


Paul,

Thanks for taking the time to look at this. I'll definitely check out all of these possibilities. I think I can answer the power consumption question directly, as the Pololu spec sheet for their version of the GY530 says the typical current during active ranging is 30 mA, with a max of 40 mA. My software only takes one measurement at a time, separated by a 100 mSec delay. So, the Teensy shouldn't see any more than one sensor's load at any time. My GTC CM100 clamp-style ammeter shows about 13-15 mA on the Wire1 side, and about the same on the Wire2 side (separate power leads to each set of three sensors). My Teensy 3.5 'flash card' (wonderful idea btw!) shows 250 mA available from the 3.3V output, so I'm thinking that power consumption is probably not the issue.

On to the next item - I2C address after all initialization. I pulled in the code for the Wire I2C_Scanner.ino example, and adapted it to scan both the Wire1 & Wire2 buses immediately after initializing all six sensors. When I ran the code, I got the following output:

Code:
Opening port
Port open
Teensy Triple VL53L0X V2
In Adafruit_VL53L0X::begin(2A,0,7D0)
In Adafruit_VL53L0X::begin(2B,0,7D0)
In Adafruit_VL53L0X::begin(2C,0,7D0)
In Adafruit_VL53L0X::begin(2D,0,6F8)
In Adafruit_VL53L0X::begin(2E,0,6F8)
In Adafruit_VL53L0X::begin(2F,0,6F8)

Init complete
 
VL53L0X API Simple Ranging example
 
Scanning Wire1...I2C device found at address 0x29  !
I2C device found at address 0x2C  !
done

Scanning Wire2...I2C device found at address 0x2D  !
I2C device found at address 0x2E  !
I2C device found at address 0x2F  !
done

The 'begin' lines show the addresses passed into the Adafruit API's 'begin()' function, and the remaining lines show what the I2C scanner found. So, something is going astray for sure. 0x29 is the default address for the VL53L0X device, so it appears that one or two of the three sensors on Wire1 are not getting their addresses initialized properly. The one at 0x2C is the only sensor on the Wire1 side that is properly initialized. I have no idea why the first two sensors didn't get programmed correctly :-(.

So, for the moment I'm officially baffled.... More later.

Frank
 
In all cases like this, it would probably help if you showed your complete wiring and code. Especially with devices like this.

Why I mention this, is because if you look at the Adafruit Learn page you see:
The VL53L0X has a default I2C address of 0x29!

You can change it, but only in software. That means you have to wire the SHUTDOWN pin and hold all but one sensor in reset while you reconfigure one sensor at a time

So it would be interesting to see if your code and hardware is setup to change the IDS one at a time, with all of the others with their shutdown pin pulled low.

EDIT: Yes I see that you have some of the information up on other site (blog?) but not sure which state is which with your messages.

But I wonder if you are still doing things like:
Code:
// 4. Initialize sensor #1 
  if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, false, &Wire1))
  if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, true, &Wire1))
  {
So it is calling begin twice, although if first one succeeds than probably does not call second time.

I would also probably check to see the state of each of the shutdown pins at each phase of setup...
And would try to check the actual state of the 6 shutdown pins at the sensor after each sensor in series is started up...
 
Last edited:
Thanks for all the feedback!

Unless there is some overriding reason to do so, I prefer to document everything on my blog site, as this is my 'engineering notebook' for all my projects.

I have updated my post on this subject to make a more thorough study of the problem

- I created a separate program to run just the three sensors on Wire1, with an I2C scan to verify addresses. This seems to work properly
- Did the same thing for Wire2. This seems to work properly
- Created a new program to run all six sensors, but I initialized each set of three separately, with an I2C scan to verify proper addresses. The addresses show up properly, but the distances reported by the first two sensors on Wire1 don't change at all, and are clearly bogus.

I'm not sure what else I can do at this point. Any thoughts?

Frank
 
As for in blog or not... Often times the more steps it takes to get help, the less likely some will go and look to help. I often will not go to a secondary website as one never knows if it is legit or has virus or...

As for current code, sometimes it is hard to see the trees in the forest. Personally I would probably organize the code to probably table drive them and have function(s) where you pass in pointers to the objects or index... But sort of besides the point.

Some things that I wonder about in your init code are delays and not delays...

I started showing more of your code and maybe issues with the delays, but thought maybe showing the Adafruit example code with color highlight might be better.
What I think you are missing in some cases is delays AFTER you set enable a device to allow it to startup before you try to do the begin:
Code:
void setID() {
  // all reset
  digitalWrite(SHT_LOX1, LOW);    
  digitalWrite(SHT_LOX2, LOW);
  delay(10);
  // all unreset
  digitalWrite(SHT_LOX1, HIGH);
  digitalWrite(SHT_LOX2, HIGH);
[COLOR="#FF0000"]  delay(10);[/COLOR]

  // activating LOX1 and reseting LOX2
  digitalWrite(SHT_LOX1, HIGH);
  digitalWrite(SHT_LOX2, LOW);

  // initing LOX1
  if(!lox1.begin(LOX1_ADDRESS)) {
    Serial.println(F("Failed to boot first VL53L0X"));
    while(1);
  }
 [COLOR="#006400"] delay(10);[/COLOR]

  // activating LOX2
  digitalWrite(SHT_LOX2, HIGH);
[COLOR="#FF0000"]  delay(10);[/COLOR]

  //initing LOX2
  if(!lox2.begin(LOX2_ADDRESS)) {
    Serial.println(F("Failed to boot second VL53L0X"));
    while(1);
  }
}
Not sure if you need the 10ms delay after device begins, but does not hurt anything.

I also wonder how you are powering this.
Again I don't know much about these devices, but their docs say maybe take 19ma when ranging on average, and you have 6... So maybe check power supply issues.

I also don't know the usage of these devices on what the magic 4 means for returns and does the API return when done or does it start a measurement?
I think this waits...

Again their example shows doing both ranging test at same time and then checking data... But not sure why this help... My guess is that they later use some other Async version of API???

Code:
void read_dual_sensors() {
  
  lox1.rangingTest(&measure1, false); // pass in 'true' to get debug data printout!
  lox2.rangingTest(&measure2, false); // pass in 'true' to get debug data printout!

  // print sensor one reading
  Serial.print("1: ");
  if(measure1.RangeStatus != 4) {     // if not out of range
    Serial.print(measure1.RangeMilliMeter);
  } else {
    Serial.print("Out of range");
  }
  
  Serial.print(" ");

  // print sensor two reading
  Serial.print("2: ");
  if(measure2.RangeStatus != 4) {
    Serial.print(measure2.RangeMilliMeter);
  } else {
    Serial.print("Out of range");
  }
  
  Serial.println();
}
But I Don't like this code. Why? Where does this measure1.RangeStatus != 4) come from???
The problem that I see is that the Range Status is valid here if and only if the lox1.rangingTest(&measure1, false)
is working. If this API fails than nothing will set that value for you to 4... And your sketch as well as the Adafruit examples are NOT looking at the return code.
Which is the first thing I would do to see if there is some failure detected.
 
I added a 100 mSec delay after each 'begin()' call, but it didn't seem to affect anything; still getting correct readings from sensors 3-6, invalid results from 1 & 2.

Good point regarding secondary sources, so here's all the relevant material here ;-).

Schematic:

200722 Teensy VL53L0X Array Schematic.jpg

dual-bus (sensors 1-3 on Wire1, 4-6 on Wire2) driver program:

Code:
/*
    Name:       Teensy_Hex_VL53L0X_Demo_V3.ino
    Created:	7/22/2020 11:31:10 AM
    Author:     FRANKNEWXPS15\Frank
*/

/*
    Name:       Teensy_Hex_V53L0X.ino
    Created:	7/16/2020 2:56:35 PM
    Author:     FRANKNEWXPS15\Frank

    Notes:
      07/18/20: I discovered that only 3 VL53L0X units can share the I2C bus. The GY530
                modules have internal 10K pullups on the I2C lines, and the 4th one on the
                bus reduces the combined pullup value too much.

                Instead, I put the right-hand array of 3 on Wire1 and the left-hand array
                of 3 on Wire2.
*/


/*
    Name:       Teensy_Triple_VL53L0X_V2.ino
    Created:	5/23/2020 1:16:52 PM
    Author:     FRANKNEWXPS15\Frank
*/


/* This code copied from the following post on the Adafruit forum:
https://forums.adafruit.com/viewtopic.php?f=19&t=164589&p=808693&hilit=vl53l0x#p808693

And adapted to use it on the Wire1 I2C bus on a Teensy 3.5
*/


#include <Wire.h>
#include "Adafruit_VL53L0X.h"

Adafruit_VL53L0X lidar_RF = Adafruit_VL53L0X(); //Right-front LIDAR (angled 30 deg forward)
Adafruit_VL53L0X lidar_RC = Adafruit_VL53L0X(); //Right-center LIDAR
Adafruit_VL53L0X lidar_RR = Adafruit_VL53L0X(); //Right-rear LIDAR (angled 30 deg rearward)

//07/16/20 added three left-side sensors
Adafruit_VL53L0X lidar_LF = Adafruit_VL53L0X(); //Left-front LIDAR (angled 30 deg forward)
Adafruit_VL53L0X lidar_LC = Adafruit_VL53L0X(); //Left-center LIDAR
Adafruit_VL53L0X lidar_LR = Adafruit_VL53L0X(); //left-rear LIDAR (angled 30 deg rearward)


//XSHUT required for I2C address init 
const int RF_XSHUT_PIN = 23;
const int RC_XSHUT_PIN = 22;
const int RR_XSHUT_PIN = 21;
const int LF_XSHUT_PIN = 5;
const int LC_XSHUT_PIN = 6;
const int LR_XSHUT_PIN = 7;

const int MAX_LIDAR_RANGE_MM = 1000; //make it obvious when nearest object is out of range

uint16_t RF_Dist_mm;
uint16_t RC_Dist_mm;
uint16_t RR_Dist_mm;
uint16_t LF_Dist_mm;
uint16_t LC_Dist_mm;
uint16_t LR_Dist_mm;

void setup()
{
  Serial.begin(115200); //Teensy ignores rate parameter

  pinMode(RF_XSHUT_PIN, OUTPUT);
  pinMode(RC_XSHUT_PIN, OUTPUT);
  pinMode(RR_XSHUT_PIN, OUTPUT);
  pinMode(LF_XSHUT_PIN, OUTPUT);
  pinMode(LC_XSHUT_PIN, OUTPUT);
  pinMode(LR_XSHUT_PIN, OUTPUT);

  // wait until serial port opens for native USB devices
  while (!Serial)
  {
    delay(1);
  }

  delay(3000); //05/28/20 - this delay is necessary AFTER Serial instantiation!

  Serial.printf("Teensy Hex VL53L0X Demo\n");

  //initialize all six LIDAR sensors
// 1. Reset all sensors by setting all of their XSHUT pins low for delay(10), then set
//    all XSHUT high to bring out of reset

  //right side
  digitalWrite(RF_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(RC_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(RR_XSHUT_PIN, LOW);
  delay(10);

  //right side
  digitalWrite(RF_XSHUT_PIN, HIGH);
  digitalWrite(RC_XSHUT_PIN, HIGH);
  digitalWrite(RR_XSHUT_PIN, HIGH);

  delay(1000);

  // 2. Keep sensor #1 awake by keeping XSHUT pin high, and put all other sensors into
  //    shutdown by pulling XSHUT pins low

  //right side
  digitalWrite(RC_XSHUT_PIN, LOW);
  digitalWrite(RR_XSHUT_PIN, LOW);

  // 4. Initialize sensor #1 
  if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, false, &Wire1))
    //if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RF"));
    while (1);
  }
  
    delay(100);

  // 5. Keep sensor #1 awake, and now bring sensor #2 out of reset by setting its XSHUT pin high.
  // 6. Initialize sensor #2 with lox.begin(new_i2c_address) Pick any number but 0x29 and whatever
  //    you set the first sensor to
  digitalWrite(RC_XSHUT_PIN, HIGH);
  if (!lidar_RC.begin(VL53L0X_I2C_ADDR + 2, false, &Wire1))
  //if (!lidar_RC.begin(VL53L0X_I2C_ADDR + 2, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RC"));
    while (1);
  }

  //delay(100);

  // 7. Repeat for each sensor, turning each one on, setting a unique address.
  digitalWrite(RR_XSHUT_PIN, HIGH);
  if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, false, &Wire1))
  //if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RR"));
    while (1);
  }

  //delay(100);

  //07/21/20 now go back and verify that all I2C addresses are unique
  Serial.printf("Scanning Wire1...");

  int nDevices = 0;

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    //Wire.beginTransmission(address);
    Wire1.beginTransmission(address);
    byte error = Wire1.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    }
    else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }

  delay(2000);


  //left side
  digitalWrite(LF_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(LC_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(LR_XSHUT_PIN, LOW);
  delay(10);

  digitalWrite(LF_XSHUT_PIN, HIGH);
  digitalWrite(LC_XSHUT_PIN, HIGH);
  digitalWrite(LR_XSHUT_PIN, HIGH);

  digitalWrite(LC_XSHUT_PIN, LOW);
  digitalWrite(LR_XSHUT_PIN, LOW);


  //07/15/20 do same thing for left side sensors
   //2. Keep sensor #1 awake by keeping XSHUT pin high, and put all other sensors into
   //   shutdown by pulling XSHUT pins low

  // 4. Initialize sensor #1 
  if (!lidar_LF.begin(VL53L0X_I2C_ADDR + 4, false, &Wire2))
    // if (!lidar_LF.begin(VL53L0X_I2C_ADDR + 4, true, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_LF"));
    while (1);
  }

  //delay(100);

  // 5. Keep sensor #1 awake, and now bring sensor #2 out of reset by setting its XSHUT pin high.
  // 6. Initialize sensor #2 with lox.begin(new_i2c_address) Pick any number but 0x29 and whatever
  //    you set the first sensor to
  digitalWrite(LC_XSHUT_PIN, HIGH);
  if (!lidar_LC.begin(VL53L0X_I2C_ADDR + 5, false, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_RC"));
    while (1);
  }

  //delay(100);

  // 7. Repeat for each sensor, turning each one on, setting a unique address.
  digitalWrite(LR_XSHUT_PIN, HIGH);
  if (!lidar_LR.begin(VL53L0X_I2C_ADDR + 6, false, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_LR"));
    while (1);
  }

  //delay(100);


  Serial.printf("\nInit complete\n \nVL53L0X API Simple Ranging example\n \n");
  Serial.printf("Scanning Wire2...");

  nDevices = 0;

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    //Wire.beginTransmission(address);
    Wire2.beginTransmission(address);
    byte error = Wire2.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    }
    else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }

  delay(2000);

  Serial.printf("Msec\tFront\tCenter\tRear\tSteer\n");
}

void loop()
{
  VL53L0X_RangingMeasurementData_t measure1;
  VL53L0X_RangingMeasurementData_t measure2;
  VL53L0X_RangingMeasurementData_t measure3;
  VL53L0X_RangingMeasurementData_t measure4;
  VL53L0X_RangingMeasurementData_t measure5;
  VL53L0X_RangingMeasurementData_t measure6;

  lidar_RF.rangingTest(&measure1, false); // pass in 'true' to get debug data printout!
  //lidar_RF.rangingTest(&measure1, true); // pass in 'true' to get debug data printout!
  if (measure1.RangeStatus != 4)  // phase failures have incorrect data
  {
    RF_Dist_mm = measure1.RangeMilliMeter;
    if (RF_Dist_mm > MAX_LIDAR_RANGE_MM) RF_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RF_Dist_mm = 0;

  delay(100);

  lidar_RC.rangingTest(&measure2, false); // pass in 'true' to get debug data printout!
  //lidar_RC.rangingTest(&measure2, true); // pass in 'true' to get debug data printout!
  if (measure2.RangeStatus != 4)  // phase failures have incorrect data
  {
    RC_Dist_mm = measure2.RangeMilliMeter;
    if (RC_Dist_mm > MAX_LIDAR_RANGE_MM) RC_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RC_Dist_mm = 0;

  delay(100);

  lidar_RR.rangingTest(&measure3, false); // pass in 'true' to get debug data printout!
  //lidar_RR.rangingTest(&measure3, true); // pass in 'true' to get debug data printout!
  if (measure3.RangeStatus != 4)  // phase failures have incorrect data
  {
    RR_Dist_mm = measure3.RangeMilliMeter;
    if (RR_Dist_mm > MAX_LIDAR_RANGE_MM) RR_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RR_Dist_mm = 0;

  //07/16/20 added left side
  lidar_LF.rangingTest(&measure4, false); // pass in 'true' to get debug data printout!
  //lidar_LF.rangingTest(&measure4, true); // pass in 'true' to get debug data printout!
  if (measure4.RangeStatus != 4)  // phase failures have incorrect data
  {
    LF_Dist_mm = measure4.RangeMilliMeter;
    if (LF_Dist_mm > MAX_LIDAR_RANGE_MM) LF_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else LF_Dist_mm = 0;

  delay(100);

  lidar_LC.rangingTest(&measure5, false); // pass in 'true' to get debug data printout!
  //lidar_LC.rangingTest(&measure5, true); // pass in 'true' to get debug data printout!
  if (measure5.RangeStatus != 4)  // phase failures have incorrect data
  {
    LC_Dist_mm = measure5.RangeMilliMeter;
    if (LC_Dist_mm > MAX_LIDAR_RANGE_MM) LC_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else LC_Dist_mm = 0;

  delay(100);

  lidar_LR.rangingTest(&measure6, false); // pass in 'true' to get debug data printout!
  //lidar_LR.rangingTest(&measure6, true); // pass in 'true' to get debug data printout!
  if (measure6.RangeStatus != 4)  // phase failures have incorrect data
  {
    LR_Dist_mm = measure6.RangeMilliMeter;
    if (LR_Dist_mm > MAX_LIDAR_RANGE_MM) LR_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RR_Dist_mm = 0;

  Serial.printf("%lu\t%d\t%d\t%d\t%d\t%d\t%d\n", millis(), RF_Dist_mm, RC_Dist_mm, RR_Dist_mm,
    LF_Dist_mm, LC_Dist_mm, LR_Dist_mm);

  delay(250);
}

And here's a representative output from the above program

Code:
Opening port
Port open
Teensy Hex VL53L0X Demo
In Adafruit_VL53L0X::begin(2A,0,7D0)
In Adafruit_VL53L0X::begin(2B,0,7D0)
In Adafruit_VL53L0X::begin(2C,0,7D0)
Scanning Wire1...I2C device found at address 0x2A  !
I2C device found at address 0x2B  !
I2C device found at address 0x2C  !
done

In Adafruit_VL53L0X::begin(2D,0,6F8)
In Adafruit_VL53L0X::begin(2E,0,6F8)
In Adafruit_VL53L0X::begin(2F,0,6F8)

Init complete
 
VL53L0X API Simple Ranging example
 
Scanning Wire2...I2C device found at address 0x2D  !
I2C device found at address 0x2E  !
I2C device found at address 0x2F  !
done

Msec	Front	Center	Rear	Steer
11527	1000	1000	146	471	399	364
12421	1000	1000	144	472	307	200
13315	1000	1000	147	129	101	88
14209	1000	1000	146	335	95	72
15103	1000	1000	147	351	98	74
15996	1000	1000	144	290	102	73
16890	1000	1000	144	246	96	76
17784	1000	1000	143	309	103	75
18677	1000	1000	142	104	91	50
19571	1000	1000	145	89	80	46
20464	1000	1000	143	57	60	38
21357	1000	1000	147	32	55	60
22251	1000	1000	143	61	63	34
23144	1000	1000	141	46	61	56
24037	1000	1000	147	48	55	36
24930	1000	1000	146	39	44	35
25825	1000	1000	148	363	274	279
26719	1000	1000	143	359	296	304
27613	1000	1000	145	339	321	379
28507	1000	1000	148	316	311	304
29400	1000	1000	140	51	37	22
30293	1000	1000	32	38	44	22
31186	1000	1000	46	36	44	70
32079	1000	1000	28	40	43	23
32972	1000	1000	66	40	48	240
33865	1000	1000	25	60	42	27
34758	1000	1000	53	30	43	18
35652	1000	1000	148	269	285	384
36547	1000	1000	149	435	1000	1000
37441	1000	1000	148	439	1000	1000

Port closed


Regarding power consumption: I have measured the total current to the entire circuit (at the USB connector on my laptop using a 'ChargerDoctor' adaptor) at about 150 mA. As I had noted before, the current to all three sensor on one side (Wire1 or Wire2) was about 15mA (average) so I'm having some trouble believing this is a power issue, although I have to admit that a power consumption problem would fit the known symptoms.
 
Note: I think you are still missing that may be important:
Code:
 digitalWrite(RR_XSHUT_PIN, HIGH);
  if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, false, &Wire1))
  //if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, true, &Wire1))
After you set the pin High, the internal chip may need some time to come up to speed. That is why I believe they had a delay(10), which I showed in RED (not as to remove but to add)...
So the segment should read:
Code:
 digitalWrite(RR_XSHUT_PIN, HIGH);
  delay(10); // give startup time to device. 
  if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, false, &Wire1))
  //if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, true, &Wire1))
 
I also meant to mention again, that you might again look at return codes to see if anything is indicated.

Example from your code:
Code:
   lidar_LF.rangingTest(&measure4, false); // pass in 'true' to get debug data printout!
  //lidar_LF.rangingTest(&measure4, true); // pass in 'true' to get debug data printout!
  if (measure4.RangeStatus != 4)  // phase failures have incorrect data
  {
    LF_Dist_mm = measure4.RangeMilliMeter;
    if (LF_Dist_mm > MAX_LIDAR_RANGE_MM) LF_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else LF_Dist_mm = 0;
You check the Magic number RangeStatus != 4... BUT again lets look up at the line:
Code:
lidar_LF.rangingTest(&measure4, false);  // pass in 'true' to get debug data printout!

You see in the library that it does:
Code:
  VL53L0X_Error
  rangingTest(VL53L0X_RangingMeasurementData_t *pRangingMeasurementData,
              boolean debug = false) {
    return getSingleRangingMeasurement(pRangingMeasurementData, debug);
  };
So simply calling off to: ... Note I edited out debug code
Code:
VL53L0X_Error Adafruit_VL53L0X::getSingleRangingMeasurement(
    VL53L0X_RangingMeasurementData_t *RangingMeasurementData, boolean debug) {
  VL53L0X_Error Status = VL53L0X_ERROR_NONE;
  FixPoint1616_t LimitCheckCurrent;

  /*
   *  Step  4 : Test ranging mode
   */

  if (Status == VL53L0X_ERROR_NONE) {
    Status = VL53L0X_PerformSingleRangingMeasurement(pMyDevice,
                                                     RangingMeasurementData);

  }

  return Status;
}
So again just calling VL53L0X_PerformSingleRangingMeasurement

Which is:
Code:
VL53L0X_Error VL53L0X_PerformSingleRangingMeasurement(
    VL53L0X_DEV Dev,
    VL53L0X_RangingMeasurementData_t *pRangingMeasurementData) {
  VL53L0X_Error Status = VL53L0X_ERROR_NONE;

  LOG_FUNCTION_START("");

  /* This function will do a complete single ranging
   * Here we fix the mode! */
  Status = VL53L0X_SetDeviceMode(Dev, VL53L0X_DEVICEMODE_SINGLE_RANGING);

  if (Status == VL53L0X_ERROR_NONE)
    Status = VL53L0X_PerformSingleMeasurement(Dev);

  if (Status == VL53L0X_ERROR_NONE)
    Status = VL53L0X_GetRangingMeasurementData(Dev, pRangingMeasurementData);

  if (Status == VL53L0X_ERROR_NONE)
    Status = VL53L0X_ClearInterruptMask(Dev, 0);

  LOG_FUNCTION_END(Status);
  return Status;
}
Which I could go farther in walking the code here, but you see that only if it makes it through the first several step with Status == VL53L0X_ERROR_NONE
will it call off to anything touching your data structure: VL53L0X_GetRangingMeasurementData(Dev, pRangingMeasurementData)

So for example one of the first calls fail, and returns a status...

So again if it were me, I would add code that maybe looks like:

Code:
VL53L0X_Error vlerr = lidar_LF.rangingTest(&measure4, false); // pass in 'true' to get debug data printout!
if (vlerr != VL53L0X_ERROR_NONE) {
    Serial.printf(" lidar_LF.rangingTest failed err:%d\n", vlerr);
    // maybe set some of your data to some error status value...
}
 
I also inserted a 1-Ohm resistor in the +V line to all six sensors to look at the dynamic current flow. It appears the peak current is about 20-30 mA, and this occurs for 10-20 mSec ever 100 mSec or so.
 
Again did you insert the delays between digitalWrite and begin?
Did you check return values to see if rangingTest failed or not?
 
Adding delays does not appear to change the symptoms. As you might have surmised looking at the commented-out lines, have looked at the error codes before, but they aren't very helpful. Here is an output run with 'debug' enabled for the measurement call.

Code:
Msec	Front	Center	Rear	Steer
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 46 : No Update
RANGE IGNORE THRESHOLD: 32.00
Measured distance: 11194
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 6.49
Measured distance: 11
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 11.66
Measured distance: 105
11839	1000	11	105	384	388	376
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 46 : No Update
RANGE IGNORE THRESHOLD: 32.00
Measured distance: 11194
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 6.49
Measured distance: 11
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 11.74
Measured distance: 105
12733	1000	11	105	391	394	382
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 46 : No Update
RANGE IGNORE THRESHOLD: 32.00
Measured distance: 11194
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 6.49
Measured distance: 11
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 11.63
Measured distance: 107
13627	1000	11	107	389	386	381
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 46 : No Update
RANGE IGNORE THRESHOLD: 32.00
Measured distance: 11194
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 6.49
Measured distance: 11
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 11.85
Measured distance: 106
14522	1000	11	106	386	394	387
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 46 : No Update
RANGE IGNORE THRESHOLD: 32.00
Measured distance: 11194
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 6.49
Measured distance: 11
sVL53L0X: PerformSingleRangingMeasurement
Range Status: 0 : Range Valid
RANGE IGNORE THRESHOLD: 11.52
Measured distance: 106
15416	1000	11	106	393	401	381

Port closed

As you can see, I'm getting a NO UPDATE status for sensor #1 which (if I understood what NO UPDATE implied) might give me a clue. However, the #2 sensor reports 'Range Valid' even though it always shows the same range (11 in the above run). I have a 100 mSec delay between each sensor measurement call, and a 250 mSec delay after each set of six measurements, so I'm mystified as to how a timing issue could be causing this.

Still baffled....
 
Yes, the delays are in there and they have no effect. Here's the latest code. However, I have to say that I have been running a 3-sensor array successfully for months without any delay between the XSHUT line state change and the begin() call. It's hard to see how I wouldn't have already seen problems with 3 sensors if it was a timing issue. Also, since I'm now running them on different I2C buses, I think the two different 3-sensor arrays can be considered independent in terms of inter-sensor timing issues. YMMV ;-).

Code:
/*
    Name:       Teensy_Hex_VL53L0X_Demo_V3.ino
    Created:	7/22/2020 11:31:10 AM
    Author:     FRANKNEWXPS15\Frank
*/

/*
    Name:       Teensy_Hex_V53L0X.ino
    Created:	7/16/2020 2:56:35 PM
    Author:     FRANKNEWXPS15\Frank

    Notes:
      07/18/20: I discovered that only 3 VL53L0X units can share the I2C bus. The GY530
                modules have internal 10K pullups on the I2C lines, and the 4th one on the
                bus reduces the combined pullup value too much.

                Instead, I put the right-hand array of 3 on Wire1 and the left-hand array
                of 3 on Wire2.
*/


/*
    Name:       Teensy_Triple_VL53L0X_V2.ino
    Created:	5/23/2020 1:16:52 PM
    Author:     FRANKNEWXPS15\Frank
*/


/* This code copied from the following post on the Adafruit forum:
https://forums.adafruit.com/viewtopic.php?f=19&t=164589&p=808693&hilit=vl53l0x#p808693

And adapted to use it on the Wire1 I2C bus on a Teensy 3.5
*/


#include <Wire.h>
#include "Adafruit_VL53L0X.h"

Adafruit_VL53L0X lidar_RF = Adafruit_VL53L0X(); //Right-front LIDAR (angled 30 deg forward)
Adafruit_VL53L0X lidar_RC = Adafruit_VL53L0X(); //Right-center LIDAR
Adafruit_VL53L0X lidar_RR = Adafruit_VL53L0X(); //Right-rear LIDAR (angled 30 deg rearward)

//07/16/20 added three left-side sensors
Adafruit_VL53L0X lidar_LF = Adafruit_VL53L0X(); //Left-front LIDAR (angled 30 deg forward)
Adafruit_VL53L0X lidar_LC = Adafruit_VL53L0X(); //Left-center LIDAR
Adafruit_VL53L0X lidar_LR = Adafruit_VL53L0X(); //left-rear LIDAR (angled 30 deg rearward)


//XSHUT required for I2C address init 
const int RF_XSHUT_PIN = 23;
const int RC_XSHUT_PIN = 22;
const int RR_XSHUT_PIN = 21;
const int LF_XSHUT_PIN = 5;
const int LC_XSHUT_PIN = 6;
const int LR_XSHUT_PIN = 7;

const int MAX_LIDAR_RANGE_MM = 1000; //make it obvious when nearest object is out of range

uint16_t RF_Dist_mm;
uint16_t RC_Dist_mm;
uint16_t RR_Dist_mm;
uint16_t LF_Dist_mm;
uint16_t LC_Dist_mm;
uint16_t LR_Dist_mm;

void setup()
{
  Serial.begin(115200); //Teensy ignores rate parameter

  pinMode(RF_XSHUT_PIN, OUTPUT);
  pinMode(RC_XSHUT_PIN, OUTPUT);
  pinMode(RR_XSHUT_PIN, OUTPUT);
  pinMode(LF_XSHUT_PIN, OUTPUT);
  pinMode(LC_XSHUT_PIN, OUTPUT);
  pinMode(LR_XSHUT_PIN, OUTPUT);

  // wait until serial port opens for native USB devices
  while (!Serial)
  {
    delay(1);
  }

  delay(3000); //05/28/20 - this delay is necessary AFTER Serial instantiation!

  Serial.printf("Teensy Hex VL53L0X Demo\n");

  //initialize all six LIDAR sensors
// 1. Reset all sensors by setting all of their XSHUT pins low for delay(10), then set
//    all XSHUT high to bring out of reset

  //right side
  digitalWrite(RF_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(RC_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(RR_XSHUT_PIN, LOW);
  delay(10);

  //right side
  digitalWrite(RF_XSHUT_PIN, HIGH);
  digitalWrite(RC_XSHUT_PIN, HIGH);
  digitalWrite(RR_XSHUT_PIN, HIGH);

  delay(1000);

  // 2. Keep sensor #1 awake by keeping XSHUT pin high, and put all other sensors into
  //    shutdown by pulling XSHUT pins low

  //right side
  digitalWrite(RC_XSHUT_PIN, LOW);
  digitalWrite(RR_XSHUT_PIN, LOW);

  // 4. Initialize sensor #1 
  if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, false, &Wire1))
    //if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RF"));
    while (1);
  }
  
    delay(100);

  // 5. Keep sensor #1 awake, and now bring sensor #2 out of reset by setting its XSHUT pin high.
  // 6. Initialize sensor #2 with lox.begin(new_i2c_address) Pick any number but 0x29 and whatever
  //    you set the first sensor to
  digitalWrite(RC_XSHUT_PIN, HIGH);
  delay(10);
  if (!lidar_RC.begin(VL53L0X_I2C_ADDR + 2, false, &Wire1))
  //if (!lidar_RC.begin(VL53L0X_I2C_ADDR + 2, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RC"));
    while (1);
  }

  delay(100);

  // 7. Repeat for each sensor, turning each one on, setting a unique address.
  digitalWrite(RR_XSHUT_PIN, HIGH);
  delay(10);
  if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, false, &Wire1))
  //if (!lidar_RR.begin(VL53L0X_I2C_ADDR + 3, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RR"));
    while (1);
  }

  //delay(100);

  //07/21/20 now go back and verify that all I2C addresses are unique
  Serial.printf("Scanning Wire1...");

  int nDevices = 0;

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    //Wire.beginTransmission(address);
    Wire1.beginTransmission(address);
    byte error = Wire1.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    }
    else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }

  delay(2000);


  //left side
  digitalWrite(LF_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(LC_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(LR_XSHUT_PIN, LOW);
  delay(10);

  digitalWrite(LF_XSHUT_PIN, HIGH);
  digitalWrite(LC_XSHUT_PIN, HIGH);
  digitalWrite(LR_XSHUT_PIN, HIGH);

  digitalWrite(LC_XSHUT_PIN, LOW);
  digitalWrite(LR_XSHUT_PIN, LOW);


  //07/15/20 do same thing for left side sensors
   //2. Keep sensor #1 awake by keeping XSHUT pin high, and put all other sensors into
   //   shutdown by pulling XSHUT pins low

  // 4. Initialize sensor #1 
  if (!lidar_LF.begin(VL53L0X_I2C_ADDR + 4, false, &Wire2))
    // if (!lidar_LF.begin(VL53L0X_I2C_ADDR + 4, true, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_LF"));
    while (1);
  }

  delay(100);

  // 5. Keep sensor #1 awake, and now bring sensor #2 out of reset by setting its XSHUT pin high.
  // 6. Initialize sensor #2 with lox.begin(new_i2c_address) Pick any number but 0x29 and whatever
  //    you set the first sensor to
  digitalWrite(LC_XSHUT_PIN, HIGH);
  delay(10);
  if (!lidar_LC.begin(VL53L0X_I2C_ADDR + 5, false, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_RC"));
    while (1);
  }

  //delay(100);

  // 7. Repeat for each sensor, turning each one on, setting a unique address.
  digitalWrite(LR_XSHUT_PIN, HIGH);
  delay(10);
  if (!lidar_LR.begin(VL53L0X_I2C_ADDR + 6, false, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_LR"));
    while (1);
  }

  delay(100);


  Serial.printf("\nInit complete\n \nVL53L0X API Simple Ranging example\n \n");
  Serial.printf("Scanning Wire2...");

  nDevices = 0;

  for (byte address = 1; address < 127; ++address) {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    //Wire.beginTransmission(address);
    Wire2.beginTransmission(address);
    byte error = Wire2.endTransmission();

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.print(address, HEX);
      Serial.println("  !");

      ++nDevices;
    }
    else if (error == 4) {
      Serial.print("Unknown error at address 0x");
      if (address < 16) {
        Serial.print("0");
      }
      Serial.println(address, HEX);
    }
  }
  if (nDevices == 0) {
    Serial.println("No I2C devices found\n");
  }
  else {
    Serial.println("done\n");
  }

  delay(2000);

  Serial.printf("Msec\tFront\tCenter\tRear\tSteer\n");
}

void loop()
{
  VL53L0X_RangingMeasurementData_t measure1;
  VL53L0X_RangingMeasurementData_t measure2;
  VL53L0X_RangingMeasurementData_t measure3;
  VL53L0X_RangingMeasurementData_t measure4;
  VL53L0X_RangingMeasurementData_t measure5;
  VL53L0X_RangingMeasurementData_t measure6;

  //lidar_RF.rangingTest(&measure1, false); // pass in 'true' to get debug data printout!
  lidar_RF.rangingTest(&measure1, true); // pass in 'true' to get debug data printout!
  if (measure1.RangeStatus != 4)  // phase failures have incorrect data
  {
    RF_Dist_mm = measure1.RangeMilliMeter;
    if (RF_Dist_mm > MAX_LIDAR_RANGE_MM) RF_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RF_Dist_mm = 0;

  delay(100);

  //lidar_RC.rangingTest(&measure2, false); // pass in 'true' to get debug data printout!
  lidar_RC.rangingTest(&measure2, true); // pass in 'true' to get debug data printout!
  if (measure2.RangeStatus != 4)  // phase failures have incorrect data
  {
    RC_Dist_mm = measure2.RangeMilliMeter;
    if (RC_Dist_mm > MAX_LIDAR_RANGE_MM) RC_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RC_Dist_mm = 0;

  delay(100);

  //lidar_RR.rangingTest(&measure3, false); // pass in 'true' to get debug data printout!
  lidar_RR.rangingTest(&measure3, true); // pass in 'true' to get debug data printout!
  if (measure3.RangeStatus != 4)  // phase failures have incorrect data
  {
    RR_Dist_mm = measure3.RangeMilliMeter;
    if (RR_Dist_mm > MAX_LIDAR_RANGE_MM) RR_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RR_Dist_mm = 0;

  //07/16/20 added left side
  lidar_LF.rangingTest(&measure4, false); // pass in 'true' to get debug data printout!
  //lidar_LF.rangingTest(&measure4, true); // pass in 'true' to get debug data printout!
  if (measure4.RangeStatus != 4)  // phase failures have incorrect data
  {
    LF_Dist_mm = measure4.RangeMilliMeter;
    if (LF_Dist_mm > MAX_LIDAR_RANGE_MM) LF_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else LF_Dist_mm = 0;

  delay(100);

  lidar_LC.rangingTest(&measure5, false); // pass in 'true' to get debug data printout!
  //lidar_LC.rangingTest(&measure5, true); // pass in 'true' to get debug data printout!
  if (measure5.RangeStatus != 4)  // phase failures have incorrect data
  {
    LC_Dist_mm = measure5.RangeMilliMeter;
    if (LC_Dist_mm > MAX_LIDAR_RANGE_MM) LC_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else LC_Dist_mm = 0;

  delay(100);

  lidar_LR.rangingTest(&measure6, false); // pass in 'true' to get debug data printout!
  //lidar_LR.rangingTest(&measure6, true); // pass in 'true' to get debug data printout!
  if (measure6.RangeStatus != 4)  // phase failures have incorrect data
  {
    LR_Dist_mm = measure6.RangeMilliMeter;
    if (LR_Dist_mm > MAX_LIDAR_RANGE_MM) LR_Dist_mm = MAX_LIDAR_RANGE_MM;
  }
  else RR_Dist_mm = 0;

  Serial.printf("%lu\t%d\t%d\t%d\t%d\t%d\t%d\n", millis(), RF_Dist_mm, RC_Dist_mm, RR_Dist_mm,
    LF_Dist_mm, LC_Dist_mm, LR_Dist_mm);

  delay(250);
}
 
Good luck,

Sorry I can give you much more hints nor test without the hardware...

But even with your latest stuff, you see difference in handling the first three and handling the 2nd three on startup.

On the first three you give a 1 second delay after you set all three sensor high...
Code:
  //right side
  digitalWrite(RF_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(RC_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(RR_XSHUT_PIN, LOW);
  delay(10);

  //right side
  digitalWrite(RF_XSHUT_PIN, HIGH);
  digitalWrite(RC_XSHUT_PIN, HIGH);
  digitalWrite(RR_XSHUT_PIN, HIGH);
[COLOR="#008000"]
  delay(1000);[/COLOR]

  // 2. Keep sensor #1 awake by keeping XSHUT pin high, and put all other sensors into
  //    shutdown by pulling XSHUT pins low

  //right side
  digitalWrite(RC_XSHUT_PIN, LOW);
  digitalWrite(RR_XSHUT_PIN, LOW);

  // 4. Initialize sensor #1 
  if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, false, &Wire1))
    //if (!lidar_RF.begin(VL53L0X_I2C_ADDR + 1, true, &Wire1))
  {
    Serial.println(F("Failed to boot lidar_RF"));
    while (1);
  }
So these servos had some startup time shown in green. again maybe not necessary.
But on the 2nd set, you do:
Code:
  //left side
  digitalWrite(LF_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(LC_XSHUT_PIN, LOW);
  delay(10);
  digitalWrite(LR_XSHUT_PIN, LOW);
  delay(10);

  digitalWrite(LF_XSHUT_PIN, HIGH);
  digitalWrite(LC_XSHUT_PIN, HIGH);
  digitalWrite(LR_XSHUT_PIN, HIGH);

  digitalWrite(LC_XSHUT_PIN, LOW);
  digitalWrite(LR_XSHUT_PIN, LOW);


  //07/15/20 do same thing for left side sensors
   //2. Keep sensor #1 awake by keeping XSHUT pin high, and put all other sensors into
   //   shutdown by pulling XSHUT pins low

  // 4. Initialize sensor #1 
  if (!lidar_LF.begin(VL53L0X_I2C_ADDR + 4, false, &Wire2))
    // if (!lidar_LF.begin(VL53L0X_I2C_ADDR + 4, true, &Wire2))
  {
    Serial.println(F("Failed to boot lidar_LF"));
    while (1);
And in this case there is no delay between when you set the 3 pnis high, set two back to low and then call begin on the LF one...
So again it might or might not help if you put a delay somewhere in that range of setting those 5 digital writes (3 high and 2 low) and the call to begin...

And again I would check the return code from the call to rangingTest to see if the two failed, as I suspect they have.
 
Kurt,

I really appreciate the time and effort you have put into this, but I'm not sure where you are going with the delay questions. Do you have a theory that explains why sensors 1 & 2 operate properly when the sensors on the Wire2 bus are not present, but sensors 1 & 2 (but not #3) initialize OK but fail to return a good measurement when the sensors on Wire2 are present?
 
I am trying to help you do process of elimination.

If the first three work one way and the second three don't work and they are not done the same was as the first three, than take a minute or two to make them the same to see if it eliminates the issue...

Theory - maybe with no delay between setting three high and two low and calling the begin method one of the set address internal functions did not work. And maybe the next one either did not work or set both of them to the new same address. Which then would cause one or both of them not to work... But again theory...

Also suggest to look at return values in calls in loop. I would probably print out both the return value of the calls to rangingTest for each of the units, in addition I would be curious of the values of RangeStatus. Like the examples you check for the magic value 4 (whatever that means) but what values are they?
I would probably check the return value for 0, likewise the measure1.RangeStatus, and if either was not 0, would print them. Also in the RangeStatus stuff, there is
a member: printRangeStatus that should hopefully print some strings with information on what the status value means...

Also again if it were me. I would create functions that Initialize each sensor and ones that read each sensor... Where I simply pass in pointer or reference to the sensor object and other data like in the init case the ID and the pin number to enable... Again so we know everything done the same...
But again that is maybe more work than it is worth.

Again good luck, I do hope it works... Who knows maybe I will order some to put on my Hexapod...
 
Another quick update...

I sort of remembered that these sensors looked familiar...

Turns out I purchased 3 of the Adafruit VL6180X sensors I was playing around with with ROS Turtle stuff maybe 1.5-2.0 years ago...
So for the heck of it I ordered 4 VL53... sensors from amazon (2 in a pack for price of 1 Adafruit...)

So I will maybe get close to approximating your stuff.

But I also through I would throw out to you another option. One that I was playing with back then. I purchased an Adafruit I2C multiplexer.
https://learn.adafruit.com/adafruit-tca9548a-1-to-8-i2c-multiplexer-breakout, with that you don't have to go through all of the stuff to give each one a unique ID.
You simply tell the multiplexer whch one you wish to talk to and then do normal I2C communications to that device.

Which will be fine if I actually use these devices and choose to use the limited ability currently Exposed in the Adafruit libraries. Right now they have a single ping type function that waits to complete before it returns. So setting a multiplexer would not hurt much. If however break up the API such that you can startup a ping on all of them and then go back to see when they are done, then having them all individual IDs may be good. Or could maybe use the freed up IO pins and setup for interrupt when complete and then maybe with different IDS or with multiplexer get the results..
 
Well, it is officially time to eat crow.* After all the whooping and hollering I've done, it turns out the entire problem was a hidden ground wire*break in I2C daisy-chain cable attached to the Teensy 3.5's Wire2 I2C bus.* After repairing the break, I can now demonstrate operation of six VL53L0X sensors on two different I2C buses on the Teensy 3.5, as shown in photo and Excel plot below (the repaired ground wire is visible at the upper right-hand corner of the image).

IMG_3370.JPG

200801 Six VL53L0X on Teensy 3.5 Wire1 and Wire2.jpg

In my defense, the wire break was inside the 4-pin connector housing that was part of the 3-sensor daisy chain cable, so it was impossible to see visually. It was also impossible to detect the break with multimeter and/or O'scope measurements, because the break was in a ground wire, but the measurements all used the system ground point for reference, meaning everything measured correctly. I finally tracked it down by running a combination of Pololu and GY530 sensors on the single I2C bus on an Arduino Mega, eliminating the daisy chain cabling entirely. When I re-added this particular cable to the mix, the last two sensors on the cable dropped out. Then I started doing some continuity checking, and this exposed the problem. I had to physically extract the ground pin from this particular connector in order to see the break.

Thanks to everyone for helping me with this problem, and my apologies for disparaging the Teensy 3.5's reputation!

Frank
 
Glad you got it working!

FYI - I now have 4 of those sensors, as well as the VL6180X ones.
For the fun of it, I extended the capabilities of the VL6... sensors to allow for things like, allow you to start up a range operation without waiting for it to complete.

I also in the example had two different ways to wait for the range to complete, either by query(or wait) and by checking the IO pin of the device to see if it went low...

Also added continuous mode, where in that mode I just checked the IO pin for each ...


Yesterday the 4 VL5... arrived and I started playing using the Adafruit library. It is REALLY limited. Thinking of adding some of these same capabilities, although I might instead look more into playing with the Pololu: https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
It is a lot more complete. However it is hard wired to Wire object...

May see if they would take a PR to allow the user to choose which Wire object to use.

But thought I would throw that out to you as an option to look at.
 
Glad you got it working!

FYI - I now have 4 of those sensors, as well as the VL6180X ones.
For the fun of it, I extended the capabilities of the VL6... sensors to allow for things like, allow you to start up a range operation without waiting for it to complete.

I also in the example had two different ways to wait for the range to complete, either by query(or wait) and by checking the IO pin of the device to see if it went low...

Also added continuous mode, where in that mode I just checked the IO pin for each ...


Yesterday the 4 VL5... arrived and I started playing using the Adafruit library. It is REALLY limited. Thinking of adding some of these same capabilities, although I might instead look more into playing with the Pololu: https://github.com/pololu/vl53l0x-arduino/blob/master/VL53L0X.cpp
It is a lot more complete. However it is hard wired to Wire object...

May see if they would take a PR to allow the user to choose which Wire object to use.

But thought I would throw that out to you as an option to look at.


Kurt,

Glad you are playing with the VLXXX sensors. I agree the Adafruit library is very limited, but it does allow for different Wire objects - a necessity for me. Also, my application is pretty slow (wall-following robot) so I don't mind waiting for measurements to complete.

The Pololu library seems easier to use, and more complete; but as you said it is hard-wired to Wire. There actually is a PR pending (submitted by PJRC forum contributor and Teensy 3.5 user mjs513) to allow the user to select the Wire object. Unfortunately the PR has been pending for 3 years now with no action, so maybe Pololu is gunning for the Arduino record of more than 8 years between the time the first PR was submitted to fix the infamous Wire hangup problem to the time it was actually fixed just a month or so ago ;-).

I downloaded and tried mjs513's Pololu library fork, but I haven't been able to get it to work yet with multiple Wire objects. I'm sure it's just something I'm doing wrong, but....

Regards,

Frank
 
I also just pushed up a branch to github: https://github.com/KurtE/vl53l0x-arduino/tree/multi_wire

Which allows you pass in the wire object to use as an optional parameter to the constructor. I have not tried it with my multiple sensor setup yet. I did try their example (modified to set all of the shutdown pins to just one of them HIGH... And it appears to work. May try one with the 4 sensors. Maybe I will split to two per I2C device just to make sure.
 
Just an example sketch... Modified their single sketch to handle 4 sensors two on Wire 2 on Wire1...

Will probably extend more to try out some different things...

Code:
#include <Wire.h>
#include <VL53L0X.h>

typedef enum {SENSE_DEFAULT = 0, SENSE_LONG_RANGE, SENSE_HIGH_SPEED, SENSE_HIGH_ACCURACY} sensorSense_t;
typedef struct {
  VL53L0X *psensor;           // pointer to object
  int     id;                 // id for the sensor
  int     shutdown_pin;       // which pin for shutdown;
  int     interrupt_pin;      // which pin to use for interrupts.
  sensorSense_t init_options; // options for how to use the sensor
} sensorList_t;

// Actual object, could probalby include in structure above
VL53L0X sensor1(&Wire);
VL53L0X sensor2(&Wire);
VL53L0X sensor3(&Wire1);
VL53L0X sensor4(&Wire1);

// Setup for 4 sensors
sensorList_t sensors[] = {
  {&sensor1, 0x30, 0, 1, SENSE_DEFAULT},
  {&sensor2, 0x31, 2, 3, SENSE_LONG_RANGE},
  {&sensor3, 0x32, 4, 5, SENSE_HIGH_SPEED},
  {&sensor4, 0x33, 6, 7, SENSE_HIGH_ACCURACY}
};

const int COUNT_SENSORS = sizeof(sensors) / sizeof(sensors[0]);

/*
    Reset all sensors by setting all of their XSHUT pins low for delay(10), then set all XSHUT high to bring out of reset
    Keep sensor #1 awake by keeping XSHUT pin high
    Put all other sensors into shutdown by pulling XSHUT pins low
    Initialize sensor #1 with lox.begin(new_i2c_address) Pick any number but 0x29 and it must be under 0x7F. Going with 0x30 to 0x3F is probably OK.
    Keep sensor #1 awake, and now bring sensor #2 out of reset by setting its XSHUT pin high.
    Initialize sensor #2 with lox.begin(new_i2c_address) Pick any number but 0x29 and whatever you set the first sensor to
*/
void Initialize_sensors() {
  bool found_any_sensors = false;
  // Set all shutdown pins low to shutdown sensors
  for (int i = 0; i < COUNT_SENSORS; i++) digitalWrite(sensors[i].shutdown_pin, LOW);
  delay(10);

  for (int i = 0; i < COUNT_SENSORS; i++) {
    // one by one enable sensors and set their ID
    digitalWrite(sensors[i].shutdown_pin, HIGH);
    delay(10); // give time to wake up.
    if (sensors[i].psensor->init()) {
      sensors[i].psensor->setAddress(sensors[i].id);
      switch (sensors[i].init_options) {
        case SENSE_DEFAULT:
          break;
        case SENSE_LONG_RANGE:
          // lower the return signal rate limit (default is 0.25 MCPS)
          sensors[i].psensor->setSignalRateLimit(0.1);
          // increase laser pulse periods (defaults are 14 and 10 PCLKs)
          sensors[i].psensor->setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
          sensors[i].psensor->setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);
          break;
        case SENSE_HIGH_SPEED:
          sensors[i].psensor->setMeasurementTimingBudget(20000);
          break;
        case SENSE_HIGH_ACCURACY:
          // increase timing budget to 200 ms
          sensors[i].psensor->setMeasurementTimingBudget(200000);
      }
      found_any_sensors = true;
    }
    else {
      Serial.print(i, DEC);
      Serial.print(": failed to start\n");
    }
  }
  if (!found_any_sensors) {
    Serial.println("No valid sensors found");
    while (1) ;
  }
}

void read_sensors() {
  // First use simple function
  for (int i = 0; i < COUNT_SENSORS; i++) {
    uint16_t range_mm = sensors[i].psensor->readRangeSingleMillimeters();
    Serial.print(i, DEC);
    Serial.print(":");
    Serial.print(range_mm, DEC);
    if (sensors[i].psensor->timeoutOccurred()) Serial.print("(TIMEOUT) ");
    else Serial.print("          ");
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire1.begin();

  // wait until serial port opens ... For 5 seconds max
  while (! Serial && millis() < 5000) ;

  // initialize all of the pins.
  Serial.println("VL53LOX_multi start, initialize IO pins");
  for (int i = 0; i < COUNT_SENSORS; i++) {
    pinMode(sensors[i].shutdown_pin, OUTPUT);
    digitalWrite(sensors[i].shutdown_pin, LOW);

    if (sensors[i].interrupt_pin >= 0) pinMode(sensors[i].interrupt_pin, INPUT_PULLUP);
  }
  Serial.println("Starting...");
  Initialize_sensors();

}

void loop() {

  read_sensors();
  delay(100);
}
 
Kurt,

Great that you put up a PR for this! I'm having an odd problem downloading your repo (https://github.com/KurtE/vl53l0x-arduino/tree/multi_wire), as the 'Code' button on your Github account insists on retrieving the Pololu master instead of your fork. However, if I select 'download ZIP', I get your version. Is this the expected behavior of these choices?

If you go to your repo, click the <CODE> button, and the hover the mouse pointer over the 'Open with Github Desktop' and 'download ZIP' menu items, you'll see that they actually point to different repos - weird!

Frank
 
Git/Github is a three headed monster that sometimes has a mind of it's own, especially when you use GUI interface to talk to it.

It tries to make assumptions and what you really want... And I don't really know what it currently does when you try to clone a fork of the master library...
That is my version up there is a fork or the Pololu library...

I am totally NOT an expert on using GIT, but can usually make my way around and the GUI for windows is improving all of the time.

Also workflows with it change depending on if you are interested in the ability to make changes

But as I mentioned up on the Pololu forum, it looks like there are three different Pull Requests done by three different people including @mjs513 and BadVibes who also added additional things.

So How might try out the version by BadVibes?


Note: Again I am working with Github for Windows, I have the library forked and cloned. It should be able to do similar if you just cloned it.

So I have the version downloaded and it is the current library shown in github for desktop. Now I end up using a command prompt: I hit <CTRL>`
to open it. But it is also a command under the Repository Menu.

There is a complication, that he did the changes in master otherwise you could simply check it out...
Here is some of the stuff from my command prompt:
Code:
D:\GitHub\vl53l0x-arduino>git remote -v
origin  https://github.com/KurtE/vl53l0x-arduino.git (fetch)
origin  https://github.com/KurtE/vl53l0x-arduino.git (push)
upstream        https://github.com/pololu/vl53l0x-arduino.git (fetch)
upstream        https://github.com/pololu/vl53l0x-arduino.git (push)

D:\GitHub\vl53l0x-arduino>git remote add badvibes https://github.com/badVibes--/vl53l0x-arduino

D:\GitHub\vl53l0x-arduino>git remote -v
badvibes        https://github.com/badVibes--/vl53l0x-arduino (fetch)
badvibes        https://github.com/badVibes--/vl53l0x-arduino (push)
origin  https://github.com/KurtE/vl53l0x-arduino.git (fetch)
origin  https://github.com/KurtE/vl53l0x-arduino.git (push)
upstream        https://github.com/pololu/vl53l0x-arduino.git (fetch)
upstream        https://github.com/pololu/vl53l0x-arduino.git (push)

D:\GitHub\vl53l0x-arduino>git fetch badvibes
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Total 14 (delta 6), reused 6 (delta 6), pack-reused 8
Unpacking objects: 100% (14/14), done.
From https://github.com/badVibes--/vl53l0x-arduino
 * [new branch]      master     -> badvibes/master

D:\GitHub\vl53l0x-arduino>git checkout badvibes/master
Note: checking out 'badvibes/master'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 0497bda Update VL53L0X.cpp

D:\GitHub\vl53l0x-arduino>
Now if his branch like mine for a Pull Request is in a new branch, than one you for example do the:
Code:
git remote add badvibes https://github.com/badVibes--/vl53l0x-arduino
Which adds a remote logical name to your list of places (Not sure exactly what GIT calls this)

Then grab (fetch) his version:
Code:
git fetch badvibes

At this point, if you did this with me (KurtE) instead of badVibes then you should be able to back to the Github for desktop window and go to the
branches list, there would then be a branch kurte\multi_wire which is what my Pull request changes are in...

Again I know this is as clear as mud!
 
Kurt,

Ironically, I used to be a sysadmin for a 50+ PC research lab, and most of the PC's were dual-boot Win x and Linux. Back in the day, as a sysadmin for a 50+ Linux box research lab, I could command-line with the best of them. Sadly, I'm now reduced to depending on GUI's to navigate ;-).

Anyway, I got your fork running on my PC by downloading the ZIP and installing from Arduino that way. And, it's now happily running my 6-sensor dual-I2C bus Teensy 3.5 setup on my wall-following robot.

IMG_3439[1].jpg

Code:
/*
    Name:       Teensy_Hex_VL53L0X_V4.ino
    Created:	7/31/2020 7:56:43 PM
    Author:     FRANKNEWXPS15\Frank
*/

/* This example shows how to get single-shot range
 measurements from the VL53L0X. The sensor can optionally be
 configured with different ranging profiles, as described in
 the VL53L0X API user manual, to get better performance for
 a certain application. This code is based on the four
 "SingleRanging" examples in the VL53L0X API.

 The range readings are in units of mm. */

#include <Wire.h>
#include <VL53L0X.h>

VL53L0X sensor1(&Wire1);
VL53L0X sensor2(&Wire1);
VL53L0X sensor3(&Wire1);

VL53L0X sensor4(&Wire2);
VL53L0X sensor5(&Wire2);
VL53L0X sensor6(&Wire2);

const int XSHUT1 = 23;
const int XSHUT2 = 22;
const int XSHUT3 = 21;

const int XSHUT4 = 5;
const int XSHUT5 = 6;
const int XSHUT6 = 7;

int dist1;
int dist2;
int dist3;

int dist4;
int dist5;
int dist6;


const int DEFAULT_VL53L0X_ADDR = 0x29;

// Uncomment this line to use long range mode. This
// increases the sensitivity of the sensor and extends its
// potential range, but increases the likelihood of getting
// an inaccurate reading because of reflections from objects
// other than the intended target. It works best in dark
// conditions.

//#define LONG_RANGE


// Uncomment ONE of these two lines to get
// - higher speed at the cost of lower accuracy OR
// - higher accuracy at the cost of lower speed

//#define HIGH_SPEED
//#define HIGH_ACCURACY


void setup()
{
  Serial.begin(115200);
  delay(2000);
  Wire.begin();
  Wire1.begin();
  Wire2.begin();

  // wait until serial port opens ... For 5 seconds max
  while (!Serial && millis() < 5000);

  pinMode(XSHUT1, OUTPUT);
  pinMode(XSHUT2, OUTPUT);
  pinMode(XSHUT3, OUTPUT);

  pinMode(XSHUT4, OUTPUT);
  pinMode(XSHUT5, OUTPUT);
  pinMode(XSHUT6, OUTPUT);

  //Put all sensors in reset mode by pulling XSHUT low
  digitalWrite(XSHUT1, LOW);
  digitalWrite(XSHUT2, LOW);
  digitalWrite(XSHUT3, LOW);

  digitalWrite(XSHUT4, LOW);
  digitalWrite(XSHUT5, LOW);
  digitalWrite(XSHUT6, LOW);

  //now bring sensor1 only out of reset and set it's address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(XSHUT1, INPUT);
  delay(10);

  sensor1.setTimeout(500); //msec, I think
  if (!sensor1.init())
  {
    Serial.println("Failed to detect and initialize sensor1!");
    while (1) {}
  }

  sensor1.setAddress(DEFAULT_VL53L0X_ADDR + 1);
  Serial.printf("Sensor1 address is 0x%x\n", sensor1.getAddress());

  //now bring sensor2 only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(XSHUT2, INPUT);

  sensor2.setTimeout(500); //msec, I think
  delay(10);
  if (!sensor2.init())
  {
    Serial.println("Failed to detect and initialize sensor2!");
    while (1) {}
  }

  sensor2.setAddress(DEFAULT_VL53L0X_ADDR + 2);
  Serial.printf("Sensor2 address is 0x%x\n", sensor2.getAddress());

  //now bring sensor3 only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(XSHUT3, INPUT);
  delay(10);

  sensor3.setTimeout(500); //msec, I think
  if (!sensor3.init())
  {
    Serial.println("Failed to detect and initialize sensor3!");
    while (1) {}
  }

  sensor3.setAddress(DEFAULT_VL53L0X_ADDR + 3);
  Serial.printf("Sensor3 address is 0x%x\n", sensor3.getAddress());

  //now bring sensor4 only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(XSHUT4, INPUT);
  delay(10);

  sensor4.setTimeout(500); //msec, I think
  if (!sensor4.init())
  {
    Serial.println("Failed to detect and initialize sensor4!");
    while (1) {}
  }

  sensor4.setAddress(DEFAULT_VL53L0X_ADDR + 4);
  Serial.printf("Sensor4 address is 0x%x\n", sensor4.getAddress());

  //now bring sensor5 only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(XSHUT5, INPUT);
  delay(10);

  sensor5.setTimeout(500); //msec, I think
  if (!sensor5.init())
  {
    Serial.println("Failed to detect and initialize sensor5!");
    while (1) {}
  }

  sensor5.setAddress(DEFAULT_VL53L0X_ADDR + 5);
  Serial.printf("Sensor5 address is 0x%x\n", sensor5.getAddress());

  //now bring sensor6 only out of reset, initialize it, and set its address
  //input w/o pullups sets line to high impedance so XSHUT pullup to 3.3V takes over
  pinMode(XSHUT6, INPUT);

  sensor6.setTimeout(500); //msec, I think
  if (!sensor6.init())
  {
    Serial.println("Failed to detect and initialize sensor6!");
    while (1) {}
  }

  sensor6.setAddress(DEFAULT_VL53L0X_ADDR + 6);
  Serial.printf("Sensor6 address is 0x%x\n", sensor6.getAddress());
}

void loop()
{
  dist1 = sensor1.readRangeSingleMillimeters();
  dist2 = sensor2.readRangeSingleMillimeters();
  dist3 = sensor3.readRangeSingleMillimeters();

  dist4 = sensor4.readRangeSingleMillimeters();
  dist5 = sensor5.readRangeSingleMillimeters();
  dist6 = sensor6.readRangeSingleMillimeters();

  Serial.printf("%d\t%d\t%d\t%d\t%d\t%d\n", dist1, dist2, dist3, dist4, dist5, dist6);

  delay(100);

}

Frank
 
Status
Not open for further replies.
Back
Top