Laser gate timer delay coding question

Hello, I'm very new to arduino and coding. It's a miracle I've gotten this project this far. I wanted to create a simple laser lap timer for my backyard pumptrack. I'm pretty sure I have the code doing what I want. Count laps, record current lap time in seconds and milliseconds, show last lap time, show best lap time. However, while testing I realized that if the laser beam is broken by walking through it, the first leg crossing it breaks the beam (LOW), then the space in between the legs allows the beam to hit the sensor (HIGH), then the second leg breaks the beam again (LOW). I need to program in some sort of delay in my coding, so that when the front bike wheel breaks the beam, the back bike wheel doesn't stop the timer. I'm assuming this is very easy, but I'm having trouble understanding what to put and where to put it in my code.

Here is my code

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


LiquidCrystal_I2C lcd(0x27, 20, 4);


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


int sec_val, milli_val;  // this is for two variables that are used to show the time on the dispay in seconds and milliseconds


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


void setup() {
  pinMode(gateSensorPin, INPUT);
  delay(50);  // this delay is to let the sensor and laser work, so we dont get the lap triggered.

  lcd.init();       // initialize the lcd
  lcd.backlight();  // turn on the LCD screen backlight

  lcd.setCursor(0, 0);
  lcd.print("Pumptrack");
  lcd.setCursor(0, 1);
  lcd.print("Laser Lap Timer  ");
  lcd.setCursor(0, 2);
  lcd.print("1.0");
  lcd.setCursor(0, 3);
  lcd.print("Let's Ride");

  delay(5000);
  lcd.clear();

  // Print text on LCD screen
  lcd.setCursor(0, 0);
  lcd.print("Lap ");
  lcd.setCursor(0, 1);
  lcd.print("Current Lap ");
  lcd.setCursor(0, 2);
  lcd.print("Last Lap ");
  lcd.setCursor(0, 3);
  lcd.print("Best Lap ");


  //Serial.begin(9600);
  delay(1500);


  // reset parameters
  currentRunStartMillis = 0;
  lastRunInMillis = 0;
  bestRunInMillis = 0;
  currentLap = 0;
}

void loop() {
  int reading = digitalRead(gateSensorPin);
  if (reading != lastgateSensorState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != gateSensorState) {
      gateSensorState = reading;

      if (gateSensorState == LOW) {
        savedMillis = millis();
        if (currentLap > 0) {
          lastRunInMillis = savedMillis - currentRunStartMillis;
          if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
            bestRunInMillis = lastRunInMillis;
          }
        }

        currentRunStartMillis = savedMillis;

        currentLap++;
      }
    }
  }

  lastgateSensorState = reading;


  // print laps
  lcd.setCursor(4, 0);
  lcd.print(currentLap);

  // save current millis
  savedMillis = millis();

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

  //code for writing the lap times on the lcd display.

  // Current Lap Time
  lcd.setCursor(12, 1);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);

  // Last Lap TIme
  calcResultFromMillis(lastRunInMillis, &sec_val, &milli_val);
  lcd.setCursor(9, 2);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);

  // Best Lap Time
  calcResultFromMillis(bestRunInMillis, &sec_val, &milli_val);
  lcd.setCursor(9, 3);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);
}


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

Adjust the code to ignore any beam breaks during the lockout period. You can modify your code like this.

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

LiquidCrystal_I2C lcd(0x27, 20, 4);

// laps info
unsigned long currentRunStartMillis;
unsigned long lastRunInMillis;
unsigned long bestRunInMillis;
int currentLap;
unsigned long savedMillis;
unsigned long lockoutUntilMillis = 0; // New variable to manage lockout period

int sec_val, milli_val;  // variables to show the time on the display in seconds and milliseconds

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

void setup() {
  pinMode(gateSensorPin, INPUT);
  delay(50);  // allow the sensor and laser to stabilize

  lcd.init();       // initialize the lcd
  lcd.backlight();  // turn on the LCD screen backlight

  lcd.setCursor(0, 0);
  lcd.print("Pumptrack");
  lcd.setCursor(0, 1);
  lcd.print("Laser Lap Timer  ");
  lcd.setCursor(0, 2);
  lcd.print("1.0");
  lcd.setCursor(0, 3);
  lcd.print("Let's Ride");

  delay(5000);
  lcd.clear();

  // Print initial text on LCD screen
  lcd.setCursor(0, 0);
  lcd.print("Lap ");
  lcd.setCursor(0, 1);
  lcd.print("Current Lap ");
  lcd.setCursor(0, 2);
  lcd.print("Last Lap ");
  lcd.setCursor(0, 3);
  lcd.print("Best Lap ");

  delay(1500);

  // reset parameters
  currentRunStartMillis = 0;
  lastRunInMillis = 0;
  bestRunInMillis = 0;
  currentLap = 0;
}

void loop() {
  int reading = digitalRead(gateSensorPin);
  if (reading != lastgateSensorState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != gateSensorState) {
      gateSensorState = reading;

      // Only process if not in lockout period
      if (gateSensorState == LOW && millis() > lockoutUntilMillis) {
        savedMillis = millis();
        if (currentLap > 0) {
          lastRunInMillis = savedMillis - currentRunStartMillis;
          if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
            bestRunInMillis = lastRunInMillis;
          }
        }

        currentRunStartMillis = savedMillis;
        currentLap++;

        // Set lockout until after the specified duration
        lockoutUntilMillis = millis() + lockoutDuration;
      }
    }
  }

  lastgateSensorState = reading;

  // print laps
  lcd.setCursor(4, 0);
  lcd.print(currentLap);

  // save current millis
  savedMillis = millis();

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

  // Update the display with lap times
  // Current Lap Time
  lcd.setCursor(12, 1);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);

  // Last Lap Time
  calcResultFromMillis(lastRunInMillis, &sec_val, &milli_val);
  lcd.setCursor(9, 2);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);

  // Best Lap Time
  calcResultFromMillis(bestRunInMillis, &sec_val, &milli_val);
  lcd.setCursor(9, 3);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);
}

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

1 Like

Nice.

I need a wiring diagram. I'm not good enough to figure where everything goes yet.

The wiring is the same as your wiring... but here is a way to simulate your project in a "wired-up" model...

  1. Go to WOKWI.COM...
  2. Click on Arduino (Uno, Nano, et c.)
  3. Scroll a few lines down and click on NANO
  4. On the left, in the "sketch.ino" tab, copy/paste this into "sketch.ino":
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 20, 4);

// laps info
unsigned long currentRunStartMillis;
unsigned long lastRunInMillis;
unsigned long bestRunInMillis;
int currentLap;
unsigned long savedMillis;
unsigned long lockoutUntilMillis = 0; // New variable to manage lockout period

int sec_val, milli_val;  // variables to show the time on the display in seconds and milliseconds

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

void setup() {
  pinMode(gateSensorPin, INPUT);
  delay(50);  // allow the sensor and laser to stabilize

  lcd.init();       // initialize the lcd
  lcd.backlight();  // turn on the LCD screen backlight

  lcd.setCursor(0, 0);
  lcd.print("Pumptrack");
  lcd.setCursor(0, 1);
  lcd.print("Laser Lap Timer  ");
  lcd.setCursor(0, 2);
  lcd.print("1.0");
  lcd.setCursor(0, 3);
  lcd.print("Let's Ride");

  delay(5000);
  lcd.clear();

  // Print initial text on LCD screen
  lcd.setCursor(0, 0);
  lcd.print("Lap ");
  lcd.setCursor(0, 1);
  lcd.print("Current Lap ");
  lcd.setCursor(0, 2);
  lcd.print("Last Lap ");
  lcd.setCursor(0, 3);
  lcd.print("Best Lap ");

  delay(1500);

  // reset parameters
  currentRunStartMillis = 0;
  lastRunInMillis = 0;
  bestRunInMillis = 0;
  currentLap = 0;
}

void loop() {
  int reading = digitalRead(gateSensorPin);
  if (reading != lastgateSensorState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != gateSensorState) {
      gateSensorState = reading;

      // Only process if not in lockout period
      if (gateSensorState == LOW && millis() > lockoutUntilMillis) {
        savedMillis = millis();
        if (currentLap > 0) {
          lastRunInMillis = savedMillis - currentRunStartMillis;
          if (lastRunInMillis < bestRunInMillis || bestRunInMillis == 0) {
            bestRunInMillis = lastRunInMillis;
          }
        }

        currentRunStartMillis = savedMillis;
        currentLap++;

        // Set lockout until after the specified duration
        lockoutUntilMillis = millis() + lockoutDuration;
      }
    }
  }

  lastgateSensorState = reading;

  // print laps
  lcd.setCursor(4, 0);
  lcd.print(currentLap);

  // save current millis
  savedMillis = millis();

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

  // Update the display with lap times
  // Current Lap Time
  lcd.setCursor(12, 1);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);

  // Last Lap Time
  calcResultFromMillis(lastRunInMillis, &sec_val, &milli_val);
  lcd.setCursor(9, 2);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);

  // Best Lap Time
  calcResultFromMillis(bestRunInMillis, &sec_val, &milli_val);
  lcd.setCursor(9, 3);
  lcd.print(sec_val);
  lcd.print(":");
  lcd.print(milli_val);
}

// calculate millis into 2 values, seconds and millis for display
void calcResultFromMillis(unsigned long value, int *sec_val, int *milli_val) {
  *sec_val = int(value / 1000);
  *milli_val = value - *sec_val * 1000;
}
  1. To the right of the "sketch.ino" tab is "diagram.json" tab... copy/paste this into "diagram.json" tab
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-arduino-nano",
      "id": "nano",
      "top": 51,
      "left": -75.3,
      "rotate": 90,
      "attrs": {}
    },
    {
      "type": "wokwi-lcd2004",
      "id": "lcd1",
      "top": -185.6,
      "left": -23.2,
      "attrs": { "pins": "i2c" }
    },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -191.24, "left": -48, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -115.2, "left": -58.2, "attrs": {} },
    {
      "type": "wokwi-pushbutton",
      "id": "btn1",
      "top": 57.5,
      "left": -140.5,
      "rotate": 90,
      "attrs": { "color": "green" }
    },
    {
      "type": "wokwi-resistor",
      "id": "r1",
      "top": 81.6,
      "left": -86.95,
      "rotate": 90,
      "attrs": { "value": "10000" }
    }
  ],
  "connections": [
    [ "lcd1:SCL", "nano:A5", "green", [ "h-19.2", "v221.1" ] ],
    [ "lcd1:SDA", "nano:A4", "green", [ "h-9.6", "v192.2" ] ],
    [ "gnd1:GND", "lcd1:GND", "black", [ "v-38.4", "h28.8" ] ],
    [ "vcc1:VCC", "lcd1:VCC", "red", [ "v0" ] ],
    [ "nano:A0", "r1:1", "green", [ "h0" ] ],
    [ "r1:1", "btn1:1.l", "green", [ "h0", "v-9.6" ] ],
    [ "nano:5V", "btn1:2.r", "red", [ "h0" ] ],
    [ "nano:GND.1", "r1:2", "black", [ "h0" ] ]
  ],
  "dependencies": {}
}
  1. Click the white arrow in the green circle to "run" the simulation
  2. Use the green button as the LASER beam to "start" and "lap"

Thanks, that works. I did come up with another solution after I posted this using "delay(1000);" inserted after the code counts the current lap. Just curious if doing it that way is worse/better. Ether way, it's working and I'm pretty happy about it.

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