Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
nfarrier
Published © GPL3+

Race Photogate

Two sets of timing gates for racing cars in a school solar car challenge that track and report the speed of each car in each of two lanes.

AdvancedFull instructions provided4,457
Race Photogate

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
HC-05 Bluetooth Module
HC-05 Bluetooth Module
×1
5V 650nm 5mW Red Dot Diode Laser Head Red Laser Diode 6mm Red Laser Diode Laser Head
×4
High Brightness LED, White
High Brightness LED, White
×4
Jumper wires (generic)
Jumper wires (generic)
×4
Male/Female Jumper Wires
Male/Female Jumper Wires
×4
Adafruit Tiny Breadboard
×1
Through Hole Resistor, 200 kohm
Through Hole Resistor, 200 kohm
×5
Adafruit Photo transistor light sensor
×4
Through Hole Resistor, 2k ohm
×1
1/2" PVC pipe and connectors
×1
Alphanumeric LCD, 16 x 2
Alphanumeric LCD, 16 x 2
×1
Neodymium Disc Magnets
×16
Android device
Android device
×1

Software apps and online services

MIT App Inventor 2
MIT App Inventor 2

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Gorilla Glue
Hot glue gun (generic)
Hot glue gun (generic)
To attach the sensor to the paper covering the sheet metal.
Quick setting epoxy resin
To glue the neodymium magnets onto the PVC pipe
3D Printer (generic)
3D Printer (generic)
OPTIONAL to make LCD cover and protective wedge

Story

Read more

Custom parts and enclosures

PVC protective wedge

An optional triangular wedge can be made to prevent any car from sideswiping the center laser base of the final photogate--either from wood or with this 3D printed version.

LCD screen cover

An optional cover for the enclosure with mounting posts for a 16x2 Arduino LCD screen.

Schematics

Laser Photogate 2 Lane Schematic

Laser Photogate Schematic for 2 Lanes

Code

Laser_Photogate-2_lanes

Arduino
Code to calibrate lasers, time, display results and send them via optional Bluetooth module
/* PhototransistorVoltage Solar Car Timer by Nelson Farrier
 * Power each phototransistor with 5v and GND
 * Connect the 2 phototransistors yellow wires 
 *       into A0 & A1 to detect start and stop of car.
 * Use 4 pins (below) to indicate laser alignment
 * Use 4 analog pins (A0-A3) for detecting photo transistor voltage
 */
#include <LiquidCrystal_I2C.h>  // Must be downloaded separtely & put in your Arduino's libraries folder

/*----- LCD Pin configuration-----*/
//GND - GND
//VCC - 5V
//SDA - ANALOG Pin 4
//SCL - ANALOG pin 5

LiquidCrystal_I2C lcd(0x27, 16, 2);

const boolean testing = false;

const int align1Pin = 8;  // pin to show laser is on 1st mark
const int align2Pin = 9;  // pin to show laser is on 2nd mark
const int align3Pin = 10;  // pin to show laser is on 1st mark
const int align4Pin = 11;  // pin to show laser is on 2nd mark

const int interval = 10;  // accuracy of timer in milliseconds
float threshold = 0.75;    // voltage where phototransistor shows car is in the way
float v1, v2, v3, v4, timer1Count, timer2Count;
boolean A, B, C, D, doneLtiming, doneRtiming, triggeredL, triggeredR, overLmtL, overLmtR, statusLCDshown, statusRshown, statusLshown, finishLshown, finishRshown;  // A & C on one lane, B & D on other
int count=0, msg = 0;
char inSerial[15];
const int StartL = 1;
const int StartR = 2;
const int FinishL = 3;
const int FinishR = 4;
const int leftLane = 0;
const int rightLane = 1;
const float overtime = 60000; // 1 minute

// bluetooth codes
const int Reset = 9;
const int Other = 10;

void setup()                                 // Built-in initialization block
{
  Serial.begin(9600);                        // Set data rate to 9600 bps
  pinMode(align1Pin,OUTPUT);
  pinMode(align2Pin,OUTPUT);
  pinMode(align3Pin,OUTPUT);
  pinMode(align4Pin,OUTPUT);
  Serial.println("!Waiting to connect to Bluetooth...");      
  lcd.begin();
  lcd.clear();
  lcd.setCursor(16,2);   
  lcd.backlight();
  lcd.setCursor(0 ,0); 
}

void loop() {  
  lcd.setCursor(16,2);   // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight();

  A = B = C = D = doneLtiming = doneRtiming= triggeredL = triggeredR = overLmtL = overLmtR = statusLCDshown = false;  // A & C on one lane, B & D on other
  finishLshown = finishRshown = statusLshown = statusRshown = false;
  boolean wait = true;
  waitForAlignment();
  timer1Count = 0;
  timer2Count = 0;
  count = 0;
  
  delay(1000);
  Serial.print("!Ready...");       
  lcd.clear();
  lcd.setCursor(0 ,0); //Start at character 4 on line 0
  lcd.print("Ready...");
  delay(1000);
  
  Serial.println("Set...");
  lcd.print("Set...");
  
  while ((!doneLtiming) || (!doneRtiming)) {
    if (triggeredL || triggeredR) {
      if (!statusLCDshown) {
      lcd.setCursor(0 ,1);
      lcd.print("Race Started!");
      if (testing) { 
        Serial.println(A); 
        Serial.println(B); 
        Serial.println(C); 
        Serial.println(D); 
        }
      statusLCDshown = true;
      }
      if ((triggeredL) && (!statusLshown)) {
        Serial.println("Race started in left lane");
        statusLshown = true;
      }
      if ((triggeredR) && (!statusRshown)) {
        Serial.println("Race started in right lane");
        statusRshown = true;
      }
      
    }     

    if (triggeredL && !doneLtiming) {             //  add interval to timer because L lane has started 
      timer1Count = timer1Count+interval; 
    }
    if (triggeredR && !doneRtiming) {             //  add interval to timer because R lane has started 
      timer2Count = timer2Count+interval; 
    }
    if (timer1Count > overtime) {
      doneLtiming = overLmtL = true;
    }
    if (timer2Count > overtime) {
      doneRtiming = overLmtR = true;
    }
    v1 = volts(A0);                    
    v2 = volts(A1);                    
    v3 = volts(A2);                    
    v4 = volts(A3);                    
    if (v1 < threshold) { digitalWrite(align1Pin, LOW); }   // turn off (and leave off) LED's as they are triggered
    if (v2 < threshold) { digitalWrite(align2Pin, LOW); }
    if (v3 < threshold) { digitalWrite(align3Pin, LOW); }
    if (v4 < threshold) { digitalWrite(align4Pin, LOW); }

    if (!triggeredL) {
      triggeredL = (v1 < threshold) || (v3 < threshold);       // initial trigger L (front or back)
      A = (v1 < threshold);
      C = (v3 < threshold);
    }
    else {
      if (A) {                                                 // final trigger L (front or back)
        if (v3 < threshold) { 
          doneLtiming = true; 
          if (!finishLshown) {
            Serial.println("Left lane finished"); 
            showLaneTime(leftLane,timer1Count,overLmtL);
            finishLshown = true; 
          }  
        }
       }
      if (C) {
        if (v1 < threshold) { 
          doneLtiming = true; 
          if (!finishLshown) {
            Serial.println("Left lane finished"); 
            showLaneTime(leftLane,timer1Count,overLmtL);
            finishLshown = true;
          }   
        }
      }
    }
    
    if (!triggeredR) {
      triggeredR = (v2 < threshold) || (v4 < threshold);        // initial trigger R (front or back)
      B = (v2 < threshold);
      D = (v4 < threshold);
    }
    else {
      if (B) {                                                  // final trigger R (front or back)
        if (v4 < threshold) { 
          doneRtiming = true; 
          if (!finishRshown) {
            Serial.println("Right lane finished"); 
            showLaneTime(rightLane,timer2Count,overLmtR);
            finishRshown = true;
          }   
        }
        }
      if (D) {
        if (v2 < threshold) { 
          doneRtiming = true; 
          if (!finishRshown) {
            Serial.println("Right lane finished"); 
            showLaneTime(rightLane,timer2Count,overLmtR);
            finishRshown = true;
          }   
        }
        }
      }

    delay(interval);                 // Delay for defined time
  }

  // end timer count and display results
  flashScreen();
  delay(80);
  if (testing) {
    Serial.print(triggeredL);
    Serial.print(triggeredR);
    Serial.print(doneLtiming);
    Serial.println(doneRtiming);
  }
  Serial.println("!Race Complete");  
  showLaneTime(leftLane,timer1Count,overLmtL);
  showLaneTime(rightLane,timer2Count,overLmtR);
  Serial.println("");                  // print blank line
       
//loop until bluetooth "Reset" sent or rebooted


while (wait) {
  int i=0;
  int m=0;
  delay(500);
  if (Serial.available() > 0) {
  while (Serial.available() > 0) {
  inSerial[i]=Serial.read();
  i++;
  }
  inSerial[i]='\0';
  msg = Check_Protocol(inSerial);
  wait = (! msg == Reset);
  if (msg == Reset) {
    flashLED(8);
  }
  }
}
}

//-----------------------------------------

void showLaneTime(int lane, float timerCount, boolean overLmt) {
if (lane == leftLane) {
  Serial.print("Left Lane Time:  ");       
  lcd.setCursor(0,0);
  lcd.print("L Time=");       
} else {
  Serial.print("Right Lane Time: ");       
  lcd.setCursor(0,1);
  lcd.print("R Time=");       

}
  if (!overLmt) {
    Serial.print(timerCount/1000);       // Display timerCount in #.## format
    Serial.println(" sec");             
    lcd.print(timerCount/1000);       
    lcd.println(" sec  ");             
  }
  else {
    Serial.print("past limit");
    lcd.println(" past lmt  ");             
  }
}

                                        
float volts(int adPin)                       // Measures volts at adPin
{                                            // Returns floating point voltage
 return float(analogRead(adPin)) * 5.0 / 1024.0;
}    

void flashScreen() {
    for(int i = 0; i< 3; i++)
  {
    lcd.backlight();
    delay(150);
    lcd.noBacklight();
    delay(150);
  }
  lcd.backlight(); // finish with backlight on  
}

int Check_Protocol(char inStr[]){
  int i=0;
  int m=0;
  uint32_t c=0;  // color of bounce
  
  Serial.println(inStr);
  if (!strcmp(inStr,"Reset")){    //Reset system  
    //Serial.println("Reset");
    return Reset;
    for(m=0;m<11;m++){
      inStr[m]=0;}
      i=0;
    }

  if (!strcmp(inStr,"other")){     
    Serial.println("Other");
    return Other;
    for(m=0;m<11;m++){
      inStr[m]=0;}
    i=0;
    }
  else{
    for(m=0;m<11;m++){
      inStr[m]=0;}
  i=0;
  }
}

void flashLED(int pinNum) {
    digitalWrite(pinNum, LOW);
    delay(200);
    digitalWrite(pinNum, HIGH);
    delay(200);
    digitalWrite(pinNum, LOW);
    delay(200);
    digitalWrite(pinNum, HIGH);
    delay(200);
    digitalWrite(pinNum, LOW);
    delay(200);
    digitalWrite(pinNum, HIGH);
    delay(200);
    digitalWrite(pinNum, LOW);
}

void  waitForAlignment() {
  boolean Start1 = false;
  boolean Start2 = false;
  boolean Finish1 = true;
  boolean Finish2 = true;
  Serial.println("!Checking Laser Alignment...");       // wait for either phototransistor to be dimmed
  lcd.clear();
  lcd.setCursor(0 ,0);     //Start at character 1 on line 0
  lcd.print("Checking Laser");
  lcd.setCursor(0 ,1);     //Start at character 1 on line 1
  lcd.print("Alignment...");
  delay(2000);
  boolean done = false;
  lcd.clear();
  while (!done) {
    v1 = volts(A0);
    digitalWrite(align1Pin, !(v1 < threshold));
    Start1 = !(v1 < threshold);
    alignmentCheck(Start1,StartL,v1);
    if (testing) {
      Serial.print( " ");                    
      Serial.print(v1);                  // Display measured A0 volts
      Serial.println(" volts ");       
    }

    v2 = volts(A1);
    digitalWrite(align2Pin, !(v2 < threshold));
    Start2 = !(v2 < threshold);
    alignmentCheck(Start2,StartR,v2);
    if (testing) {
      Serial.print( " ");                    
      Serial.print(v2);                  // Display measured A1 volts
      Serial.println(" volts ");       
    }

    v3 = volts(A2);
    digitalWrite(align3Pin, !(v3 < threshold));
    Finish1 = !(v3 < threshold);
    alignmentCheck(Finish1,FinishL,v3);
    if (testing) {
      Serial.print( " ");                     
      Serial.print(v3);                 // Display measured A2 volts
      Serial.println(" volts ");       
    }

    v4 = volts(A3);
    digitalWrite(align4Pin, !(v4 < threshold));
    Finish2 = !(v4 < threshold);
    alignmentCheck(Finish2,FinishR,v4);
    if (testing) {
      Serial.print( " ");                     
      Serial.print(v4);                 // Display measured A3 volts
      Serial.println(" volts ");        
    }
    
    Serial.println("  ");       
    delay(500);                               // Delay for 0.5 seconds
    done = (Start1 && Start2 && Finish1 && Finish2);
  }
  Serial.println("!Lasers Aligned");       // wait for either phototransistor to be dimmed
  lcd.clear();
  lcd.setCursor(0 ,0); //Start at character 4 on line 0
  lcd.print("Lasers Aligned");
  delay(2000);
}

void alignmentCheck(boolean aligned, int section, float v) {
    String fullTextOut, smTextOut;
    if (section == StartL) { 
      fullTextOut = "Left Start"; 
      smTextOut = "A"; 
      lcd.setCursor(0, 0);
      }
    if (section == StartR) {
      fullTextOut = "Right Start"; 
      smTextOut = "B"; 
      lcd.setCursor(8, 0);
      }
    if (section == FinishL) { 
      fullTextOut = "Left Finish"; 
      smTextOut = "C"; 
      lcd.setCursor(0, 1);
     }
    if (section == FinishR) { 
      fullTextOut = "Right Finish"; 
      smTextOut = "D"; 
      lcd.setCursor(8, 1);
      }
    if (aligned) {
      Serial.println(fullTextOut+" Aligned");                  
      lcd.print(smTextOut+"=OK   ");                            
    }
    else {
      Serial.print(fullTextOut+"=");                     // Display "Ax = "
      lcd.print(smTextOut+"=");
      Serial.print(v);                  // Display measured Ax volts
      Serial.println(" volts ");        // Display " volts" & newline
      lcd.print(v);                     // Display measured Ax volts
      lcd.print("v ");                  // Display " volts" & newline
    }
}

Solar Car Challenge (optional phone app for Bluetooth connection)

Scheme
MIT App Inventor Code (.aia) for Android Phone to see photogate results via Bluetooth and to reset photogate
No preview (download only).

Credits

nfarrier

nfarrier

1 project • 8 followers
I'm a retired science and technology teacher, and have an amateur radio license. I enjoy programming and making things.

Comments