Laser Sensor Accuracy

I've been successful in creating a laser gate timer for my bike pumptrack. However, it doesn't work all of the time and I'm wondering if my sensors just are not capable of what I need them to do. At low speeds it seems to work fine, but when I break the laser very quickly, it does not register that there has been a beam break. Is there anyway to adjust the accuracy of the laser gate in the code or maybe just an upgrade in electronics. If an upgrade is needed, what would you recommend? I built this with little experience and lots of youtube.

IMG8666

I'm using an amazon a Laser Sensor Module Non-Modulator Tube Laser Receiver Module with a KY-008 650nm Laser Transmitter Module

I've tried removing sunlight from the equation. I've got the sensor in blackout box with just a hole to allow the laser light to enter and make contact with the sensor. I did notice that I was having issues with accuracy even more when the laser and sensor where about 5-6 feet apart. I've shorted the distance to about 3.5 feet. I can't really go shorter distance than that, or I risk running over the whole thing with my bike tire. I also made sure to have the gate just 1-2 inches off the ground, so that the tire and wheel break the beam, and not just the spokes on the wheel.

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;
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;
}


I think, that laser is too weak. On the image in your link there is 10k resistor, if that is in series with laser, you get next to nothing out... Make a test with really short distance to confirm that.
Without knowing your receiver that's the best I can offer.

I don't have any resistors except for those on the little circuit board that is part of the laser receiver module. I just tried some experiments at very close range, 6-8 inches apart and it might be better but still doesn't register very fast beam breaks.

I wrote laser, not receiver. You didn't post link to your receiver.
image
There is resistor 103, 10k ohm.

Anyway, you could also update your display only when laser is triggered. I don't know your display library and how much time it takes to do those updates, but whatever it is your sensor is not available those moments.

How are you powering the setup, post some simple wiring scheme.

I'm not sure you need a debounce delay since there are no mechanical button contacts involved to electrically bounce.

Would I just remove the code that you included? I'm not familiar enough with the code to know how to remove the debounce.

A common infrared LED, modulated with 38kHz, and a matching 3-pin receiver is a much better solution. The "beam" is as thick as the LED (5mm), it can do 1/000 sec, you won't have to aim it as precise, and it can be used in broad daylight outside.
You can find examples on this site if you enter beambreak in the search field on top of this page.
Leo..

So what's the difference of tsal6100 and tsal6400 ?

And I agree using ir-led/receiver instead of undocumented laser "module" with undocumented receiver.

look at the IDE example sketch StateChangeDetection

The 6100, with a smaller opening angle, has less spill to the sides. So you need less power to drive the LED for a given distance, or get further with the same power.
I bridged more than 50m outside, on a bright day, without optics, with just a short tube to keep direct sunlight out off the receiver. Both LEDs have a ~5mm thick beam (the size of the LED lense). It's all about shadows, not light.
Leo..

1 Like

I totally agree, but in practice that "spill" makes it less like a beam compared to some optically collimated setups. I personally see 6100 narrow opening and 6400 wider.
image

What emitter/receiver you were using? I'd like to try that as well.

Yes, but that has got little to do with beambreak.
Direct line of sight light is much stronger than spill.
The direct light beam is as thick as the LED and the lens on the receiver.
If you have a LED on your TV/stereo, then try to block it with a pencil in a stretched out hand and one eye closed. Then you see what I mean.

I remember using two TSAL6200 LEDs in series, powered by a two-transistor constant current circuit, set to 120mA peak. The receiver was a PNA4602 (round lens), just because I had one. It should ideally be a receiver for continuous IR.
See this page.
Leo..

1 Like

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