do {} while loops being bypassed

I’m designing a laser timer that uses two photo resistors to detect the time a 15cm object interrupts two separate laser beams and calculating the two speeds and a total time. The data is then displayed to an lcd in either meters per second or centimeters per second, to two decimal places. For some reason, the code seems to bypass the do while loops I have placed to wait until the sensor reaches a certain state, especially when the object interrupts the sensor for more than half of a second… this causes random zeros instead of useful numbers. I checked to make sure the sensor’s output is constant, so that is not the cause. I must be doing something wrong… Thank you for your help!

#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,16,2);
 int unit = A2;
 int sens1 = 4;
 int sens2 = 5;
 long starttime1;
 long starttime2;
 int speed1 = 0;
 int speed2 = 0;
 
void setup() {
  // put your setup code here, to run once:
  lcd.init();
  lcd.backlight();
  pinMode(sens1, INPUT);
  pinMode(sens2, INPUT);
  pinMode(unit, INPUT);
  lcd.setCursor(0,0);
  if (digitalRead(unit) == 0)
  lcd.print("m/s + s");
  else
  lcd.print("cm/s + s");
  lcd.setCursor(0,1);
  lcd.print("t=");
  lcd.setCursor(9,0);
  lcd.print("V1=");
  lcd.setCursor(9,1);
  lcd.print("V2=");  
}

void loop() {
  // put your main code here, to run repeatedly: 
Serial.begin(9600);
if ((digitalRead(4) == 1) && (speed1 == 0))
 {
  Serial.println("sens1 triggered");
  long starttime1 = millis();
  do {}
  while (digitalRead(4) == 1);
  int speed1 = 1500000 / (long)(millis() - (long)starttime1);  
  Serial.println(abs((float)(speed1 / 10000.000)),2);
  do {}
  while (digitalRead(5) == 0);
  long starttime2 = millis();
  Serial.println((long)starttime2);
  do {}
  while (digitalRead(5) == 0);
  int speed2 = 1500000 / (long)(millis() - (long)starttime2);
  Serial.println(abs((float)(speed2 / 10000.000)),2);
  lcd.setCursor(2,1);
  long totaltime = ((long)starttime2) - ((long)starttime1);
  Serial.println(((float)totaltime / 1000.000),2);
  lcd.print((float)(totaltime / 1000.000),2);
  if ((digitalRead(unit) == 1))
 {
   lcd.setCursor(12,0);
   lcd.print(abs((float)(speed1 / 100.000)),2);
   lcd.setCursor(12,1);
   lcd.print(abs((float)(speed2 / 100.000)),2);
 }
  else if ((digitalRead(unit) == 0))
 {
   lcd.setCursor(12,0);
   lcd.print(abs((float)(speed1 / 10000.000)),2);
   lcd.setCursor(12,1);
   lcd.print(abs((float)(speed2 / 10000.000)),2);
 }
 delay(5000);
}
else if (digitalRead(5) == 1 && (speed1 == 0))
 {
  Serial.println("sens2 triggered");
  unsigned long starttime1 = millis();
  do {}
  while (digitalRead(5) == 1);
  int speed1 = 1500000 / (long)(millis() - (long)starttime1);
  Serial.println(abs((float)(speed1 / 10000.000)),2);;
  do {}
  while (digitalRead(4) == 0);
  long starttime2 = millis();
  Serial.println((long)starttime2);
  delay(2);
  do {}
  while (digitalRead(4) == 0);
  int speed2 = 1500000 / (long)(millis() - (long)starttime2);
  Serial.println(abs((float)(speed2 / 10000.000)),2);
  lcd.setCursor(2,1);
  long totaltime = ((long)starttime2) - ((long)starttime1);
  Serial.println(((float)totaltime / 1000.000),2);
  lcd.print((float)(totaltime / 1000.000),2);
  if ((digitalRead(unit) == 1))
  {
   lcd.setCursor(12,0);
   lcd.print(abs((float)(speed1 / 100.000)),2);
   lcd.setCursor(12,1);
   lcd.print(abs((float)(speed2 / 100.000)),2);
 }
  else if ((digitalRead(unit) == 0))
 {
   lcd.setCursor(12,0);
   lcd.print(abs((float)(speed1 / 10000.000)),2);
   lcd.setCursor(12,1);
   lcd.print(abs((float)(speed2 / 10000.000)),2);
 }
 delay(5000);
 }

}
  while (digitalRead(4) == 0);
  long starttime2 = millis();
  Serial.println((long)starttime2);
  delay(2);
  do {}
  while (digitalRead(4) == 0);

The second while will only wait if the signal is less than 2 ms HIGH, seems unlikely to me.

If you can move the sensors to pins that support external interrupts,
you could easily record timestamps and events,
interrupts on the current pins are too possible, but a little less easy.

I think micros() will give you a much better resolution.

I dislike many aspects of your sketch

  • delays
  • const omissions
  • tons of casts
  • strange type selection and mixing
  • the blocking approach
  • the superfluous do {} lines
  • hardcoded pins

https://www.arduino.cc/reference/en/language/structure/control-structure/dowhile/

larryd: https://www.arduino.cc/reference/en/language/structure/control-structure/dowhile/

https://www.arduino.cc/reference/en/language/structure/control-structure/while/

Why use do while with an empty do loop?

Whandall:
while - Arduino Reference

Why use do while with an empty do loop?

My implication.

.

If you want to use the blocking style, you could use pulseIn, that function knows pretty well how to wait, even with a timeout. Your code seems to try to reinvent pulseIn...

You only gave the 15 cm size of the object, but no distance between the sensors and no range for the speed.

if ((digitalRead(4) == 1) && (speed1 == 0))
 {
  Serial.println("sens1 triggered");
  long starttime1 = millis();
  do {}
  while (digitalRead(4) == 1);

I don't see anything wrong with your syntax, but you don't seem to be taking into account "debouncing." OTTH, I'm not sure that phototransistors would "bounce" in the circumstances you're using.

If the ball size is always 15 cm, why couldn’t you use 1 sensor? Interrupt when sensor is blocked and start timing, interrupt again when sensor is clear and stop timing, calculate velocity from elapsed time (in micros) and ball diameter.

https://www.arduino.cc/reference/en/language/structure/control-structure/while/

Why use do while with an empty do loop?

Ah! I hadn't even seen that, I had just followed one of the examples on the do while page which features an empty do.

If you want to use the blocking style, you could use pulseIn, that function knows pretty well how to wait, even with a timeout. Your code seems to try to reinvent pulseIn...

You only gave the 15 cm size of the object, but no distance between the sensors and no range for the speed.

You're right! I didn't even think of using the pulseIn function! The speed is timed separately for each sensor (initial and final velocity), and I'm just recording time between each. I don't need average speed, because the sensors will be moveable, not fixed distance.

If the ball size is always 15 cm, why couldn't you use 1 sensor? Interrupt when sensor is blocked and start timing, interrupt again when sensor is clear and stop timing, calculate velocity from elapsed time (in micros) and ball diameter.

I'm essentially doing that twice to get initial and final speeds, but also timing the overall time. But yes, that's how I'm detecting the speed.

If you can move the sensors to pins that support external interrupts, you could easily record timestamps and events, interrupts on the current pins are too possible, but a little less easy.

Which pins would those be on an Arduino nano and how much harder are they to use?

On a Nano all pins (except A6 and A7) support PinChangeInterrupts which is the more inconvenient way (it's functionality was not included in the Arduino core).

Interrupts howto

Libraries for PinChangeInterrupts (disclaimer: I have not used them)

https://github.com/GreyGnome/PinChangeInt https://github.com/NicoHood/PinChangeInterrupt

The more convenient solution (using external interrupts)

Information on how to use external interrupts

Thank you guys fir the help! I’ve got it working with pulsein. Thanks!