Laser Sensor not registering quickly enough?

Hello!
This is my first Arduino project, and my second post on this forum. I'm having a lot of fun learning about Arduino and happy with the progress I've made on my own, but I'm at a place where I think I need help. I would welcome feedback on how to make my question more clear to this group, if needed!

Project Overview:
I'm attempting to create a Laser Lap Timer to use outdoors for a dirtbike event my friends have set for later in the summer. The timer should record:

  • Current Lap Time
  • Current Lap number
  • Last Lap
  • Best Lap

The Hardware being used:

The Library that I'm using for the TFT display is:

Because I'm still learning about how to make things with Arduino, the project lives as a mashup between two existing projects. I took the example project from the TFT_HX8357 library (TFT_Print_Test) and inserted what I believed to be the important portions of code from this Laser Lap Timer project that I found on Gadgetronicx >> [Lap timer for slot cars using Laser and Arduino] Note: because I'm a new user, I'm only allowed to post two links, so I'm unable to link the project here. However it is the first thing that comes up when doing a Google Search for the words in BOLD.

Initially I was struggling with getting the TFT display to work, but thanks to this forum I was able to figure that, and a few other things out in this thread, if you're interested, or it helps someone else in the future.

I thought I had everything figured out, until I set it up for testing with a slot car track. After lining everything up and ensuring the car would break the beam on the pass, the timer isn't registering new laps. It appears that the break in the laser beam doesn't last long enough to trigger a new lap. The delay on this portion of the setup is set to 1, and I've messed around with the debounce time (i'm still learning about this, so it truly was "messing around") but no positive outcome.

The curious part is that in a previous test to learn about the laser sensors and the basics of Arduino - I set up a buzzer to ring when the beam is broken. This involved no display, just the sensor, and the buzzer and it worked perfectly. I could flick a pencil between the beam and it would register a sound.

I'm at a loss on how to troubleshoot from here. So my question,

  • Is there a solve for getting the program to register very quick breaks in the laser beam?

As a secondary question/topic, because I am still very new at this, I would welcome any feedback on the code as a whole! As stated above it is a bit of a "mish-mash" of two previously existing projects/examples. I'm very confident that there are more elegant ways to accomplish what I'm trying to do.

Thank you for your help.

Here is a youtube video of the test from today.

And here is my code, as it sits today.

#include <TFT_HX8357.h>  // Hardware-specific library

TFT_HX8357 tft = TFT_HX8357();  // Invoke custom library

// laps info
unsigned long currentRunStartMillis;
unsigned long lastRunInMillis;
unsigned long bestRunInMillis;
int currentLap;
unsigned long savedMillis;

// global for display
int min_val, sec_val, milli_val;

// laser gate
const int gateSensorPin = 2;         // the number of the gate sensor pin
int gateSensorState;                 // the current reading from the sensor
int lastgateSensorState = LOW;       // the previous reading from sensor
unsigned long lastDebounceTime = 0;  // the last time the sensor pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup(void) {
  tft.init();
  tft.setRotation(3);
  // pin mode
  pinMode(gateSensorPin, INPUT);
  delay(1);  // to late the sensor and laser work, so we wont get the lap triggered.
             // reset params
  currentRunStartMillis = 0;
  lastRunInMillis = 0;
  bestRunInMillis = 0;
  currentLap = 0;
  
  tft.fillScreen(TFT_BLACK);
}

void loop() {
  // read the state of the laser sensor:
  int reading = digitalRead(gateSensorPin);
  // If the switch changed, due to noise or pressing:
  if (reading != lastgateSensorState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }  //end if

  // if passes the debounce time
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != gateSensorState) {
      gateSensorState = reading;

      // If we went low, this mean the beam was broken
      if (gateSensorState == LOW) {
        // save the millis so all the math on it will be done with the same value.
        savedMillis = millis();
        // if its not the first lap
        if (currentLap > 0) {
          // save the last run
          lastRunInMillis = savedMillis - currentRunStartMillis;
          // if last run is faster then best run
          if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
            //save as best
            bestRunInMillis = lastRunInMillis;
          }  //end if
        }    //end if

        //reset the current
        currentRunStartMillis = savedMillis;

        // move lap counter
        currentLap++;
      }  //end if
    }    //end if
  }      //end if



  lastgateSensorState = reading;

  // save current milis
  savedMillis = millis();


  // if we start the first lap
  if (currentLap > 0) {
    calcResultFromMillis(savedMillis - currentRunStartMillis, &min_val, &sec_val, &milli_val);
  } else {
    calcResultFromMillis(0, &min_val, &sec_val, &milli_val);
  }  //end if



  tft.setCursor(20, 0, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(3);
  // We can now plot text on screen using the "print" class
  tft.println("CURRENT LAP");

  tft.fillRect(10, 50, 440, 60, TFT_BLACK);
  tft.setCursor(50, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  if (min_val < 10) {
    tft.print('0');
  }
  tft.println(min_val);
  tft.setCursor(132, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(150, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (sec_val < 10) {
    tft.print('0');
  }
  tft.println(sec_val);
  tft.setCursor(232, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(250, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (milli_val < 1) {
    tft.print('0');
  }
  tft.println(milli_val);


  tft.setCursor(20, 110, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(3);
  // We can now plot text on screen using the "print" class
  tft.println("LAST");

  calcResultFromMillis(lastRunInMillis, &min_val, &sec_val, &milli_val);

  tft.fillRect(50, 160, 400, 60, TFT_BLACK);
  tft.setCursor(50, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  if (min_val < 10) {
    tft.print('0');
  }
  tft.println(min_val);
  tft.setCursor(132, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(150, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (sec_val < 10) {
    tft.print('0');
  }
  tft.println(sec_val);
  tft.setCursor(232, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(250, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (milli_val < 1) {
    tft.print('0');
  }
  tft.println(milli_val);

  tft.setCursor(20, 220, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setTextSize(3);
  // We can now plot text on screen using the "print" class
  tft.println("BEST");

  calcResultFromMillis(bestRunInMillis, &min_val, &sec_val, &milli_val);

  tft.fillRect(50, 265, 400, 50, TFT_BLACK);
  tft.setCursor(50, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  if (min_val < 10) {
    tft.print('0');
  }
  tft.println(min_val);
  tft.setCursor(132, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(150, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (sec_val < 10) {
    tft.print('0');
  }
  tft.println(sec_val);
  tft.setCursor(232, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(250, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (milli_val < 1) {
    tft.print('0');
  }
  tft.println(milli_val);

  delay(1);
}

// calculate millis into 2 values, seconeds and millis for display
void calcResultFromMillis(unsigned long value, int *min_val, int *sec_val, int *milli_val) {
  *min_val = int(value / 60000);
  *sec_val = int(value / 1000);
  *milli_val = value - *sec_val * 1000;
  *sec_val = *sec_val % 60;
  *min_val = *min_val % 60;
}

I doubt you need to debounce the sensor. It is not a mechanical switch. Please post a link to the product page or data sheet for the light sensors.

My approach would be to simply record some trial beam break events, under realistic conditions, inspect the record, and then decide if some sort of corrective action needs to be taken.

As far as timing goes, photodiodes can respond to events with picosecond timing. Reaction time is not an issue.

1 Like

Looks like you update the screen at loop speed..
Recommend only updating screen when values have changed..
And take that delay(1) out of the loop..

untested sorry..

#include <TFT_HX8357.h>  // Hardware-specific library

TFT_HX8357 tft = TFT_HX8357();  // Invoke custom library

// laps info
unsigned long currentRunStartMillis;
unsigned long lastRunInMillis;
unsigned long bestRunInMillis;
int currentLap;
int lastLap:
unsigned long savedMillis;

// global for display
int min_val, sec_val, milli_val;

// laser gate
const int gateSensorPin = 2;         // the number of the gate sensor pin
int gateSensorState;                 // the current reading from the sensor
int lastgateSensorState = LOW;       // the previous reading from sensor
unsigned long lastDebounceTime = 0;  // the last time the sensor pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup(void) {
  tft.init();
  tft.setRotation(3);
  // pin mode
  pinMode(gateSensorPin, INPUT);
  delay(1);  // to late the sensor and laser work, so we wont get the lap triggered.
  // reset params
  currentRunStartMillis = 0;
  lastRunInMillis = 0;
  bestRunInMillis = 0;
  currentLap = 0;

  tft.fillScreen(TFT_BLACK);
  //cause a screen draw once..
  updateScreen();


}

void loop() {
  // read the state of the laser sensor:
  int reading = digitalRead(gateSensorPin);
  // If the switch changed, due to noise or pressing:
  if (reading != lastgateSensorState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }  //end if

  // if passes the debounce time
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != gateSensorState) {
      gateSensorState = reading;

      // If we went low, this mean the beam was broken
      if (gateSensorState == LOW) {
        // save the millis so all the math on it will be done with the same value.
        savedMillis = millis();
        // if its not the first lap
        if (currentLap > 0) {
          // save the last run
          lastRunInMillis = savedMillis - currentRunStartMillis;
          // if last run is faster then best run
          if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
            //save as best
            bestRunInMillis = lastRunInMillis;
          }  //end if
        }    //end if

        //reset the current
        currentRunStartMillis = savedMillis;

        // move lap counter
        currentLap++;
      }  //end if
    }    //end if
  }      //end if



  lastgateSensorState = reading;

  // save current milis
  savedMillis = millis();


  // if we start the first lap
  if (currentLap > 0) {
    calcResultFromMillis(savedMillis - currentRunStartMillis, &min_val, &sec_val, &milli_val);
  } else {
    calcResultFromMillis(0, &min_val, &sec_val, &milli_val);
  }  //end if

  if (currentLap != lastLap) {
    updateScreen();
    lastLap = currentLap;
  }


}

void updateScreen() {

  tft.setCursor(20, 0, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(3);
  // We can now plot text on screen using the "print" class
  tft.println("CURRENT LAP");

  tft.fillRect(10, 50, 440, 60, TFT_BLACK);
  tft.setCursor(50, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  if (min_val < 10) {
    tft.print('0');
  }
  tft.println(min_val);
  tft.setCursor(132, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(150, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (sec_val < 10) {
    tft.print('0');
  }
  tft.println(sec_val);
  tft.setCursor(232, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(250, 40, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (milli_val < 1) {
    tft.print('0');
  }
  tft.println(milli_val);


  tft.setCursor(20, 110, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(3);
  // We can now plot text on screen using the "print" class
  tft.println("LAST");

  calcResultFromMillis(lastRunInMillis, &min_val, &sec_val, &milli_val);

  tft.fillRect(50, 160, 400, 60, TFT_BLACK);
  tft.setCursor(50, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  if (min_val < 10) {
    tft.print('0');
  }
  tft.println(min_val);
  tft.setCursor(132, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(150, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (sec_val < 10) {
    tft.print('0');
  }
  tft.println(sec_val);
  tft.setCursor(232, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(250, 150, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (milli_val < 1) {
    tft.print('0');
  }
  tft.println(milli_val);

  tft.setCursor(20, 220, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setTextSize(3);
  // We can now plot text on screen using the "print" class
  tft.println("BEST");

  calcResultFromMillis(bestRunInMillis, &min_val, &sec_val, &milli_val);

  tft.fillRect(50, 265, 400, 50, TFT_BLACK);
  tft.setCursor(50, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  if (min_val < 10) {
    tft.print('0');
  }
  tft.println(min_val);
  tft.setCursor(132, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(150, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (sec_val < 10) {
    tft.print('0');
  }
  tft.println(sec_val);
  tft.setCursor(232, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(5);
  // We can now plot text on screen using the "print" class
  tft.println(':');
  tft.setCursor(250, 250, 2);
  // Set the font colour to be white with a black background, set text size multiplier to 1
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(5);
  if (milli_val < 1) {
    tft.print('0');
  }
  tft.println(milli_val);

}

// calculate millis into 2 values, seconeds and millis for display
void calcResultFromMillis(unsigned long value, int *min_val, int *sec_val, int *milli_val) {
  *min_val = int(value / 60000);
  *sec_val = int(value / 1000);
  *milli_val = value - *sec_val * 1000;
  *sec_val = *sec_val % 60;
  *min_val = *min_val % 60;
}

good luck.. ~q

It may be time for you to investigate interrupts. Take a look at them, attachInterrupt() - Arduino Reference
They may help

Ignore the above post.

If you can't successfully read the transition with a digital input, use of interrupts will only make the problem worse. Guaranteed.

thinking you can also move the calculations into the lap change check..


  if (currentLap != lastLap) {
  // if we start the first lap
  if (currentLap > 0) {
    calcResultFromMillis(savedMillis - currentRunStartMillis, &min_val, &sec_val, &milli_val);
  } else {
    calcResultFromMillis(0, &min_val, &sec_val, &milli_val);
  }  //end if

    updateScreen();
    lastLap = currentLap;
  }

saves a few more cycles..

~q

Thanks all for the advice - I had a small amount of time to try some of these things and already have found some improvements. I see now how the loop and delays were causing issues.

Excited to come back once I have more time and show the progress.

Thanks all!

Ok, I think I've made some progress, although it still isn't perfect.

Some learnings over the past few days:

  • Using @qubits-us code with updateScreen() along with removing the debounce worked flawlessly, no matter how quickly the beam was broken, it would register a new lap. It really illustrated @jremington note on photodiodes being able to respond to events with picosecond timing. Pretty neat.
  • The only issue, now, was that the Current Lap would not show the current time of the lap. It would forever remain at "00:00:00".

So I moved the "Current Lap" bits back into the Loop portion of the code, hoping it would show live time of current lap. It worked! However, not as well as having it structured in the updateScreen() portion. It is far and away better than the code I came here with originally, so that's good, but there are still instances where it won't register the lap change. There's also this bizzare issue where it will randomly print a longer registered lap as "Best Lap", and then will fail to update when a new "Best Lap" is posted as "Last"

To help illustrate this, I've made another short YouTube video HERE, wherein you can see:

  • Instances of the new lap not being registered (it gets stuck at Lap 10)
  • right before the video stops you can see how Lap 58 flips Best Lap from a .102ms to a .542ms lap. Then lap 63 doesn't register a .104ms lap as "Best"

It seems to only casue an issue when the laps are under 1 second, so I don't think that it would be an issue in real-world use, but it is still something I'd like to figure out.

So. My questions from these last steps:

  • Is there anything that I should look to to further decrease the cycles, or loop portion of the code? The goal would be to have no issues registering any laps. If there was a way to only print the MS time and have the MM:SS pieces roll over in the updateScreen() portion if milli_val > 1000?
  • Any thoughts around why Best lap resets to a higher value randomly, and how to solve for that?

I did have two other things I wanted to build into the program as well for the future. I'll ask them here, but if I should start a new thread for those questions please advise.

The TFT Screen has a button that is currently not being used. I would like very much to have the following functions associated to the button:

  • Click once to cycle through a list of riders printed on the Right Hand side of the screen, highlighting the "current rider" in Red.
  • Any rider that has earned the current "Best Lap" would have a star, or some symbol printed next to their name for the duration of the session, or until a new rider beats this time.
  • Click twice to reset all and start the program fresh.

I've looked around, but can't seem to find any examples to start digging into how to make this happen. Any advice would be appreciated!

Here is the code as it sits today. Thanks to all for the help so far! Very fun stuff!

// Stable 6/12 - untested on slot car



#include <TFT_HX8357.h>  // Hardware-specific library

TFT_HX8357 tft = TFT_HX8357();  // Invoke custom library

// laps info
unsigned long currentRunStartMillis;
unsigned long lastRunInMillis;
unsigned long bestRunInMillis;
int currentLap;
int lastLap;
unsigned long savedMillis;


// global for display
int min_val, sec_val, milli_val;

// laser gate
const int gateSensorPin = 2;         // the number of the gate sensor pin
int gateSensorState;                 // the current reading from the sensor
int lastgateSensorState = LOW;       // the previous reading from sensor


void setup(void) {
  tft.init();
  tft.setRotation(3);
  // pin mode
  pinMode(gateSensorPin, INPUT);
  
  // reset params
  currentRunStartMillis = 0;
  lastRunInMillis = 0;
  bestRunInMillis = 0;
  currentLap = 0;

  tft.fillScreen(TFT_BLACK);
  
  // Print "Current Lap"
  tft.setCursor(20, 0, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(3);
  tft.println("CURRENT LAP");
  
  // Print "Last Lap"
  tft.setCursor(20, 110, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(3);
  tft.println("LAST LAP");
  
  // Print "Best Lap"
  tft.setCursor(20, 210, 2);
  tft.setTextColor(TFT_GREEN, TFT_BLACK);
  tft.setTextSize(3);
  tft.println("BEST LAP");

  updateScreen();
}

void loop() {
  // read the state of the laser sensor:
  int reading = digitalRead(gateSensorPin);
  // If the switch changed, due to noise or pressing:
  if (reading != lastgateSensorState) {}  //end if
    if (reading != gateSensorState) {
      gateSensorState = reading;
      // If we went low, this mean the beam was broken
      if (gateSensorState == LOW) {
        // save the millis so all the math on it will be done with the same value.
        savedMillis = millis();
        // if its not the first lap
        if (currentLap > 0) {
          // save the last run
          lastRunInMillis = savedMillis - currentRunStartMillis;
          // if last run is faster then best run
          if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
            //save as best
            bestRunInMillis = lastRunInMillis;
          }  //end if
        }    //end if
        //reset the current
        currentRunStartMillis = savedMillis;
        // move lap counter
        currentLap++;
      }  //end if
    }    //end if

  lastgateSensorState = reading;

  // save current milis
  savedMillis = millis();

  // if we start the first lap
  if (currentLap > 0) {
    calcResultFromMillis(savedMillis - currentRunStartMillis, &min_val, &sec_val, &milli_val);
  } else {
    calcResultFromMillis(0, &min_val, &sec_val, &milli_val);
  }  //end if

   // Print "Current Lap" details
tft.setCursor(50, 40, 2);
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.setTextSize(4);
if (min_val < 10) {
    tft.print('0');  }
tft.println(min_val); // Print Current Lap "MM"
tft.setCursor(132, 40, 2);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextSize(4);
tft.println(':'); // Print Current Lap ":"
tft.setCursor(150, 40, 2);
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.setTextSize(4);
if (sec_val < 10) {
    tft.print('0');  }
tft.println(sec_val); // Print Current Lap "SS"
tft.setCursor(232, 40, 2);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextSize(4);
tft.println(':'); // Print Current Lap ":"
tft.setCursor(250, 40, 2);
tft.setTextColor(TFT_BLUE, TFT_BLACK);
tft.setTextSize(4);
 if (milli_val < 1) {
    tft.print('0');  }
tft.println(milli_val); // Print Current Lap "MS"
  
  if (currentLap != lastLap) {
    updateScreen();
    lastLap = currentLap;
  }
}

void updateScreen() {

 // print Lap # 
 tft.setCursor(375, 30, 2);
 tft.setTextColor(TFT_RED, TFT_BLACK);
 tft.setTextSize(5);
 if(currentLap < 10){
   tft.print('0');
 }
 tft.println(currentLap);

 

  // calcResultFromMillis for Last Lap
calcResultFromMillis(lastRunInMillis, &min_val, &sec_val, &milli_val);
  //Print "Last Lap" details
  tft.setCursor(50, 150, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(4);
  if (min_val < 10) {
    tft.print('0'); }
  tft.println(min_val); // Print Last Lap "MM"  
  tft.setCursor(132, 150, 2);
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(4);
  tft.println(':'); // Print Last Lap ":" 
  tft.setCursor(150, 150, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(4);
  if (sec_val < 10) {
    tft.print('0'); }
  tft.println(sec_val); // Print Last Lap "ss"
  tft.setCursor(232, 150, 2);
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(4);
  tft.println(':'); // Print Last Lap ":"
  tft.setCursor(250, 150, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(4);
  if (milli_val < 1) {
    tft.print('0');  }
  tft.println(milli_val); // Print Last Lap "MS"

  // calcResultFromMillis for Best Lap
  calcResultFromMillis(bestRunInMillis, &min_val, &sec_val, &milli_val);
  
  // Print "Best Lap" details 
  tft.setCursor(50, 250, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(4);
  if (min_val < 10) {
    tft.print('0'); }
  tft.println(min_val); // Print Best Lap "mm"
  tft.setCursor(132, 250, 2);
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(4);
  tft.println(':'); // Print Best Lap ":"
  tft.setCursor(150, 250, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(4);
  if (sec_val < 10) {
    tft.print('0'); }
  tft.println(sec_val); // Print Best Lap "ss"
  tft.setCursor(232, 250, 2);
  tft.setTextColor(TFT_YELLOW, TFT_BLACK);
  tft.setTextSize(4);
  tft.println(':'); // Print Best Lap ":"
  tft.setCursor(250, 250, 2);
  tft.setTextColor(TFT_BLUE, TFT_BLACK);
  tft.setTextSize(4);
  if (milli_val < 1) {
    tft.print('0');  }
  tft.println(milli_val); // Print Best Lap "MS"
}

// calculate millis into 2 values, seconeds and millis for display
void calcResultFromMillis(unsigned long value, int *min_val, int *sec_val, int *milli_val) {
  *min_val = int(value / 60000);
  *sec_val = int(value / 1000);
  *milli_val = value - *sec_val * 1000;
  *sec_val = *sec_val % 60;
  *min_val = *min_val % 60;
}

I simmed it..
MegaLapCounter
Had to change a few things to make it work..
Used different screen lib..
big change was setCursor, didn't have the third option for font, but your lib also can accept just x,y and don't really see why you were sending font into setCursor anyways..
Slight smaller screen so i adjusted things as needed..
calcResultFromMillis, confused me, i de-confused it..
moved some screen drawing back out of the loop, left current m,s,ms update only..

has a button instead of beam..
once i got the sim working, seem to be good..
with all that, this is completely wrong for..

This current design will only be good for one rider..
Multiple riders, need to re-think entire design..
Probably gonna end up with something on each rider..

good luck.. ~q

Ok first off, that sim program is pretty rad. That will come in handy in the future.

I'll spend some time this evening and play with it some more. In the meantime, just wanted to clarify this piece:

There would only be one rider on the track at any time, for what we have set up. For example, if we have a 1 hour "session" and each rider takes turns doing 10 laps at a time, that's where I'd like to press the button and indicate which rider is currently on the track, and have the program highlight which rider has the best time for that entire "session".

Hopefully that is more clear to what I'm trying to do? Spent some time during lunch looking at different libraries. There is this "ButtonKing" library that seems to offer functionality with single and double-clicks. The GitHub Library for ButtonKing can be found here.

I've looked at the pinout for the TFT that I'm using, but I can't seem to find anything on what pin is associated to the button on the board? The Amazon Product Page is here . Am I going about this wrong?

Thanks,
Chris

I don't see where that button is brought out to you either, maybe it's not??

And yes, it's clearer and doable again..

Is it possible to hook up another button(s) to the Mega or is the screen in the way??

really need at least 2 button, to navigate and select..
touch screen would have been nice..
my last purchase..

~q

Sorry for the delay in any updates. Have been struggling to find time to make a post on this.
In regards to the question:

Is it possible to hook up another button(s) to the Mega or is the screen in the way??

The answer is yes, I have hooked 3 buttons up currently and all is well.

The current challenge is wrapping my mind around how these menu's work. I've reviewed several libraries and tutorials on "Menu making made Easy" - however I am struggling to make any progress.

If I'm unable to figure this out, or make any headway on my own, I may make a new thread specifically for this issue, as the original question I started this thread has (thankfully!) been solved.

I'll be tinkering on this for a while, I think. In the meantime, if anyone has any insights to a beginner-friendly Menu Tutorial for a TFT screen and physical buttons it would be very appreciated!

Thanks!

got a few menu example something or others..
ArdsAndEnds
the diagram.txt is the WokWi diagram.json, for quick sims..
mostly, small screens, but underlying technique will be the same..
have fun.. ~q

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.