Skipping disconnected sensor in code

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
davidestevens
 
Posts: 35
Joined: Thu Oct 20, 2022 7:44 am

Skipping disconnected sensor in code

Post by davidestevens »

hi,
I hope this is the correct forum for this...

I have a working setup with 4 ToF (VL53L4CD) sensors connected to an Arduino Micro via a MUX (TCA9548A). This is part of a system that will go into a school, and so I want to make sure that the code keeps running if one of the sensors gets pulled out.

So something like: in void loop() check pin X on the TCA, if there's no data (implying that the sensor is no longer connected), go to the next pin and check that.
(At this point I'm not too worried whether or not the sensor is connected at start up (which would make void setup() fail), though I'm hoping the solution for the loop will transfer to setup.)

At the moment, the loop looks something like this (I'll post the code below, but I want to get the abstract idea out of my head first)...

void loop() {
set local variables;
select pin 0 of the TCA; as soon as new data is ready, clear the interupt, then get the new data, and report it;
select pin 1 of the TCA: as soon as new data is ready, clear the interupt, then get the new data, and report it;
etc for pins 2 & 3.

I've tried various things with if & while. The closest I got was:
select pin 0; if (NewDataReady !=0); {then the existing code} else { select the next pin etc}.

If I added this extra code in the sketch after the first pin was selected, and then bracketed {} the code for the second pin, the sketch not only ran, but continued running if I disconnected the first sensor. (Though if I reconnected it, the sensor was still not seen).
But when I added the extra code to the parts of the code for the other sensors, the { } brackets got out of control (it looked like they would have gto be endlessly recursive!), and I couldn't see how to include an }else{ after the last sensor so that it would return to the beginning of the loop.

Surely there's a much simpler way of implementing an "if (!x) then goto" in my code??

Here's the code that's running ok.
And below that is the part of the sketch I modified that almost works (except for the endlessly recursive nesting)

Code: Select all

/* Includes ------------------------------------------------------------------*/
#include <Arduino.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <vl53l4cd_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

#define TCAADDR 0x70

#ifndef _BV
#define _BV(bit) (1 << (bit)) 
#endif


void tcaselect(uint8_t i) {
  if (i > 7) return;
 
  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

// Components.
// VL53L4CD sensor_vl53l4cd_sat(&DEV_I2C, A1);
VL53L4CD vl1(&DEV_I2C, 0);
VL53L4CD vl2(&DEV_I2C, 0);
VL53L4CD vl3(&DEV_I2C, 0);
VL53L4CD vl4(&DEV_I2C, 0);

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

void setup()
{
  SerialPort.begin(115200);

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

tcaselect(0);
vl1.begin();  // Configure VL53L4CD satellite component.
vl1.VL53L4CD_Off();  // Switch off VL53L4CD satellite component.
vl1.InitSensor();  //Initialize VL53L4CD satellite component.
vl1.VL53L4CD_SetRangeTiming(200, 0); // Program the highest possible TimingBudget, without enabling the
  // low power mode. This should give the best accuracy
vl1.VL53L4CD_StartRanging(); // Start Measurements

tcaselect(1);
vl2.begin(); 
vl2.VL53L4CD_Off(); 
vl2.InitSensor();  
vl2.VL53L4CD_SetRangeTiming(200, 0); 
vl2.VL53L4CD_StartRanging(); 

tcaselect(2);
vl3.begin(); 
vl3.VL53L4CD_Off(); 
vl3.InitSensor();  
vl3.VL53L4CD_SetRangeTiming(200, 0); 
vl3.VL53L4CD_StartRanging(); 

tcaselect(3);
vl4.begin(); 
vl4.VL53L4CD_Off(); 
vl4.InitSensor();  
vl4.VL53L4CD_SetRangeTiming(200, 0); 
vl4.VL53L4CD_StartRanging(); 

}

void loop()
{
  uint8_t NewDataReady = 0;
  VL53L4CD_Result_t results;
  uint8_t status;
  char report[64];
 

  tcaselect(0);

  do {
    status = vl1.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);

  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl1.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl1.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status1 %3u  Distance1  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }

   tcaselect(1);

     do {
    status = vl2.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);


  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl2.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl2.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status2  %3u  Distance2  %5u  \r\n",
             results.range_status,       
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }

  tcaselect(2);

     do {
    status = vl3.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);


  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl3.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl3.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status3  %3u  Distance3  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }

  tcaselect(3);

     do {
    status = vl4.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);


  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl4.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl4.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status4  %3u  Distance4  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }
  
}

Code: Select all

void loop()
{
  uint8_t NewDataReady = 0;
  VL53L4CD_Result_t results;
  uint8_t status;
  char report[64];
 

  tcaselect(0);
  
  if (NewDataReaady != 0); {

  do {
    status = vl1.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);

  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl1.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl1.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status1 %3u  Distance1  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }
  else} {

   tcaselect(1);

User avatar
davidestevens
 
Posts: 35
Joined: Thu Oct 20, 2022 7:44 am

Re: Skipping disconnected sensor in code

Post by davidestevens »

So looking at some docs on if loops, it appears to me that all I need to do is have an "if (condition) { " statement after each pin is selected, and then a further curly bracket to close off that section...

Code: Select all

 tcaselect(0);
if (NewDataReady != 0); {
  do {
    status = vl1.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);

  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl1.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl1.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status1 %3u  Distance1  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
 } 
  }
This compiles, and runs. But if I pull one of the sensors the sketch stops. So either the logic is wrong, or I'm using the wrong criteria ( NewDataReady != 0) to determine whether to skip that bit of code or not.
Any thoughts?

Edit. Or perhaps it'd the MUX that's failing? So I would need to do something using the TCA9548A library ?
Last edited by davidestevens on Thu Mar 02, 2023 10:29 am, edited 1 time in total.

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: Skipping disconnected sensor in code

Post by adafruit_support_bill »

Code: Select all

if (NewDataReady != 0); {
This statement is terminated by the semicolon and effectively does nothing. The following code block (enclosed by the brackets) will always execute - regardless of the value of NewDataReady.

So, if there is no device & no new data, you will drop into the do/while loop forever.

If you want the 'if' to control execution of the following code block, you need to remove the semicolon statement terminator.

User avatar
davidestevens
 
Posts: 35
Joined: Thu Oct 20, 2022 7:44 am

Re: Skipping disconnected sensor in code

Post by davidestevens »

OK, so I removed the ";" and now I'm _only getting the values from the first sensor, which I suppose means that it's stuck in the loop following the if ?? I get the feeling I'm going about this the wrong way... (And the sketch still stops if I pull the first sensor)

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: Skipping disconnected sensor in code

Post by adafruit_support_bill »

If you post your full code we can take a look.

Note that calling vl1.VL53L4CD_CheckForDataReady(&NewDataReady) to determine if a sensor is present is somewhat analogous to taking attendance by saying "anyone who is not present, please raise your hand."

The sensor cannot respond either positively or negatively if it is not present. And in some cases, attempting to communicate with a sensor that is not there may cause other problems.

User avatar
davidestevens
 
Posts: 35
Joined: Thu Oct 20, 2022 7:44 am

Re: Skipping disconnected sensor in code

Post by davidestevens »

The untinkered with full code is in my original post; here's where I've gotten to with fiddling with it. The easiest way to solve this dilemma (what to do if a sensor gets pulled out) would be to add a reset button to the Arduino board, so that a teacher can easily reset everything if a cable gets pulled out. So I'm going to do that anyway, but would still like to see if there's a way of doing it in code.

Code: Select all

/* Includes ------------------------------------------------------------------*/
#include <Arduino.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <vl53l4cd_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

#define TCAADDR 0x70

#ifndef _BV
#define _BV(bit) (1 << (bit)) 
#endif


void tcaselect(uint8_t i) {
  if (i > 7) return;
 
  Wire.beginTransmission(TCAADDR);
  Wire.write(1 << i);
  Wire.endTransmission();  
}

// Components.
// VL53L4CD sensor_vl53l4cd_sat(&DEV_I2C, A1);
VL53L4CD vl1(&DEV_I2C, 0);
VL53L4CD vl2(&DEV_I2C, 0);
VL53L4CD vl3(&DEV_I2C, 0);
VL53L4CD vl4(&DEV_I2C, 0);

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

void setup()
{
  SerialPort.begin(115200);

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

tcaselect(0);
vl1.begin();  // Configure VL53L4CD satellite component.
vl1.VL53L4CD_Off();  // Switch off VL53L4CD satellite component.
vl1.InitSensor();  //Initialize VL53L4CD satellite component.
vl1.VL53L4CD_SetRangeTiming(200, 0); // Program the highest possible TimingBudget, without enabling the
  // low power mode. This should give the best accuracy
vl1.VL53L4CD_StartRanging(); // Start Measurements

tcaselect(1);
vl2.begin(); 
vl2.VL53L4CD_Off(); 
vl2.InitSensor();  
vl2.VL53L4CD_SetRangeTiming(200, 0); 
vl2.VL53L4CD_StartRanging(); 

tcaselect(2);
vl3.begin(); 
vl3.VL53L4CD_Off(); 
vl3.InitSensor();  
vl3.VL53L4CD_SetRangeTiming(200, 0); 
vl3.VL53L4CD_StartRanging(); 

tcaselect(3);
vl4.begin(); 
vl4.VL53L4CD_Off(); 
vl4.InitSensor();  
vl4.VL53L4CD_SetRangeTiming(200, 0); 
vl4.VL53L4CD_StartRanging(); 

}

void loop()
{
  uint8_t NewDataReady = 0;
  VL53L4CD_Result_t results;
  uint8_t status;
  char report[64];
 

  tcaselect(0);
 
if (!NewDataReady) {
  do {
    status = vl1.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);

  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl1.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl1.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status1 %3u  Distance1  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
 } 
  } 

   tcaselect(1);

   if (!NewDataReady) {

     do {
    status = vl2.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);


  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl2.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl2.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status2  %3u  Distance2  %5u  \r\n",
             results.range_status,       
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }
   } 

  tcaselect(2);

   if (!NewDataReady) {

     do {
    status = vl3.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);


  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl3.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl3.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status3  %3u  Distance3  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }
  } 

  tcaselect(3);
 
   if (!NewDataReady) {

     do {
    status = vl4.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);


  if ((!status) && (NewDataReady != 0)) {
    // (Mandatory) Clear HW interrupt to restart measurements
    vl4.VL53L4CD_ClearInterrupt();

    // Read measured distance. RangeStatus = 0 means valid data
    vl4.VL53L4CD_GetResult(&results);
    snprintf(report, sizeof(report), "Status4  %3u  Distance4  %5u  \r\n",
             results.range_status,          
             results.distance_mm,
             results.signal_per_spad_kcps);
    SerialPort.print(report);
  }
  
} 
}

User avatar
adafruit_support_bill
 
Posts: 88086
Joined: Sat Feb 07, 2009 10:11 am

Re: Skipping disconnected sensor in code

Post by adafruit_support_bill »

Code: Select all

  uint8_t NewDataReady = 0;
  VL53L4CD_Result_t results;
  uint8_t status;
  char report[64];
 

  tcaselect(0);
 
if (!NewDataReady) {
  do {
    status = vl1.VL53L4CD_CheckForDataReady(&NewDataReady);
  } while (!NewDataReady);
This code will get stuck in the do/while forever if the sensor on channel 0 is not present.

If it is there, you will execute the rest of the code in the block, then proceed to:

Code: Select all

   tcaselect(1);

   if (!NewDataReady) {
At this point, NewDataReady will be true by definition (since you exited the previous do/while), so you will never execute the following code block.

Ditto for the remaining channels.

User avatar
davidestevens
 
Posts: 35
Joined: Thu Oct 20, 2022 7:44 am

Re: Skipping disconnected sensor in code

Post by davidestevens »

ok, I can see that now. I think this is beyond me at the moment, so I'll stick with the hardware reset solution.
But thanks again for your help in making sense of this.

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”