Unreliable 'WHILE' loop -

I'm building a model tramway. I've been using infrared BEAM sensors (from Pi Hut). I have found them to be reliable. However, I have encountered an issue and would be grateful for guidance.

When the sensor beam is blocked it returns a 0.
I use the following line of code to read the sensor status.

while (digitalRead(SENSOR1) == 1) {delay(50);}

or

while (digitalRead(SENSOR1) != 0) {delay(50);}

If the sensor returns a 0 (zero), the sketch moves on. This has always worked reliably for me but I now have a sensor that seems to get random zeros and the code moves on whether the input is a zero or not.
I have two of these sensors using identical code. One works well, the other doesn't. I've replaced the sensors (twice), replaced the cables, even replaced the Arduino Mega.
One of the sensors is connected to pin 12, the other to pin 13. If I swap the pins, the fault switches to the other sensor. This would indicate that the wiring and sensors are not the issue. Also, as the code is identical, it can't be the code.
If I reverse the code, ie

while (digitalRead(SENSOR1) == 0) {delay(50);}

the sensor works. (except that it's the opposite of what I want to achieve.

So, the question is, Is there a better way than using the while command?
I need the sensor to wait until the model tram passes, then move to the next bit of the sketch.
I appreciate that there's not much detail here and I may not have been sufficiently clear but I'm completely stuck as to why two identical sensors, with identical wiring and code, should behave differently. I'd be very grateful for some better way to read the sensor so that the sketch doesn't progress until the tram passes the sensor.
Many thanks

Snippets of code just isn’t enough.


In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.

Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch.


In simple sketches suggest you never use while() {}

Thanks. I will attempt to edit the above to include the setup part of my code.
I have pulled out only the parts of the sketch that make these two sensors function as it's a big sketch.

Just add it in a new post.

Always show us a good schematic of your proposed circuit.

Show us a good image of your ‘actual’ wiring. Give links to components.

While loops are completely blocking.
You can't do anything else than waiting for the condition to become false.

There are much better ways to code the same functionality in a so called non-blocking manner which will enable to do multiple things in parallel.

And for example check beam-break if the beam-break endures a minimum of time to filter out short disturbings

If you post your complete sketch I can add debugging to make visible what is going on in your code



// SERVO LIBRARY - loads servo control library
    #include <Servo.h>
    

// ASSIGN PINS

    int SENSOR2 = 13; // White 
    int SENSOR1 = 12; // yellow
    
    
void setup() {
  
      Serial.begin(9600); 
      
      // INITIALISE PINS
      
      pinMode(SENSOR2, INPUT_PULLUP);
      pinMode(SENSOR1, INPUT_PULLUP);
      
}

//____________________________________ MAIN LOOP ______________________________________

void loop() {
  
     
// Checks HILLBOTTOM sensor - if SENSOR1 is HIGH, while loops until SENSOR2 goes low
        Serial.println(digitalRead(SENSOR1)); 
        while (digitalRead(SENSOR1) == 1) {delay(50);} 
        //<code here changes PWM duty cycle to increase average voltage>


// Checks HILLTOP sensor - SENSOR2 is HIGH, 'while' loops until SENSOR2 goes LOW
        Serial.println(digitalRead(SENSOR2));
        while (digitalRead(SENSOR2) == 1) {delay(50);}
        //<code here changes PWM duty cycle to decrease average voltage>

                       
      }




  • Tram reaches bottom of hill and passes SENSOR1.
  • SENSOR1 goes LOW
  • increase average voltage to climb hill
  • Tram passes SENSOR2 at top of hill
  • SENSOR2 goes LOW
  • decrease average voltage

There's a whole lot more to this sketch involving servos, PWM module and relays. This all works fine.
SENSOR1 seems to mostly ignore its status.
SENSOR2 works fine.

If is swap the signal cables over (pin 12 & 13), the fault moves to the other sensor.

I have run a 'cut down' version of the sketch to ensure it's not other components or sections of code causing the issue.

Posting only parts of the sketch loads the work on you to distribute the lines of code that I want to add to the right place.

So simply post your complete sketch. It doesn't matter if your sketch has 3000 lines of code

Thank you.
I have included as much of the code as I dare below.
I can include the whole lot if required.

I agree with WHILE LOOPS being blocking. I don't really need to do other things in sequence as the sketch is a very linear process. However, a better and uncomplicated method would be much appreciated.

So here is a code-version with debug-printing
inside the while-loops the value of sensor 1 / sensor 2 gets printed only if the value changes

// MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START * MACRO-START *
// a detailed explanation how these macros work is given in this tutorial
// https://forum.arduino.cc/t/comfortable-serial-debug-output-short-to-write-fixed-text-name-and-content-of-any-variable-code-example/888298

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  }

#define dbgc(myFixedText, variableName) \
  { \
    static long lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }

#define dbgcf(myFixedText, variableName) \
  { \
    static float lastState; \
    if ( lastState != variableName ){ \
      Serial.print( F(#myFixedText " "  #variableName" changed from ") ); \
      Serial.print(lastState); \
      Serial.print( F(" to ") ); \
      Serial.println(variableName); \
      lastState = variableName; \
    } \
  }
// MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END * MACRO-END *


// SERVO LIBRARY - loads servo control library
#include <Servo.h>


// ASSIGN PINS

int SENSOR2 = 13; // White
int SENSOR1 = 12; // yellow


void setup() {

  Serial.begin(115200);
  while(!Serial);  // wait for the serial interface to be ready
  Serial.println( F("Setup-Start") );
  PrintFileNameDateTime();
  // INITIALISE PINS
  pinMode(SENSOR2, INPUT_PULLUP);
  pinMode(SENSOR1, INPUT_PULLUP);
  Serial.println( F("leaving setup()") );
}

//____________________________________ MAIN LOOP ______________________________________

void loop() {
  // Checks HILLBOTTOM sensor - if SENSOR1 is HIGH, while loops until SENSOR2 goes low
  //Serial.println(digitalRead(SENSOR1));
  while (digitalRead(SENSOR1) == 1) {
    dbgc("01",digitalRead(SENSOR1) );
    dbgc("02",digitalRead(SENSOR2) );
    delay(50);
  }
  //<code here changes PWM duty cycle to increase average voltage>
  // if you would post this code more debug-printing could be added 
  // which would enable a more detailed analysis


  // Checks HILLTOP sensor - SENSOR2 is HIGH, 'while' loops until SENSOR2 goes LOW
  // Serial.println(digitalRead(SENSOR2));
  while (digitalRead(SENSOR2) == 1) {
    dbgc("01",digitalRead(SENSOR1) );
    dbgc("02",digitalRead(SENSOR2) );
    delay(50);
  }
  //<code here changes PWM duty cycle to decrease average voltage>
  // if you would post this code more debug-printing could be added 
  // which would enable a more detailed analysis

}

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

unsigned long MyTestTimer = 0;                   // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 2;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

change the baudrate in the serial monitor to the modern value 115200
and then flash this code-version into your arduino-uno
post what you get printed in the serial monitor

best regards Stefan

Use sensor change in state instead of sensor level.

When digitalRead(SENSOR1) == HIGH set a Flag1; reset the Flag1 when it is LOW.

Same with the other sensor with Flag2

Flags are used to enable/disable code.

If you use the inbuilt LED for anything, this also uses pin D13 on the Mega2560.
So if you are setting the LED on or off for any reason, that is also changing the digitalRead value of that pin independently of the sensor operation.
It will be stressing the digitalwrite components of the Mega and stressing the output components of the sensor.
Pin D13 also has a resistor 1k resistor in series with the LED to ground (0v) so this relatively strong pulldown could even affect your sensor output, even if you are not driving the LED on or off in the sketch.
Just try another pin, like D11.

xfacta has a point.

If that doesn't work --
The weak INPUT_PULLUP may be insufficient in this application.
Supplement that with a 10kΩ resistor (this will pull it up harder).

Thanks. I will pick my way through this although I might be a bit out of my depth.

Thank you. I had no knowledge of this. Changing to a different pin is no problem.

Thank you. So, would you place the resistor in series within the sensor signal cable?
I'm still puzzled as to why on sensor works well and the other doesn't. It's the one on pin 12 that's problematic. I have tried using other pins.

We really need to see a schematic and an image of your wiring and a link to the sensor.

Hmm. This sounds as if it has potential. Any chance of a snippet of example code?
Thank you.

You should post a datasheet of your sensor

Best method for analysing such problems is to use a digital storage oscilloscope
But this is an investment of $200 to $500
Not worth to solve a single problem.

another way is the debug-printing.
So simply upload my code-version and look into the serial monitor

Sure, please see post #17 first.