Components and supplies
Resistor 220 ohm
Buzzer, Piezo
5mm LED IR Emitter
Resistor 100 ohm
Arduino Nano R3
Through Hole Resistor, 390 ohm
Resistor 10k ohm
5 mm LED: Green
Alphanumeric LCD, 16 x 2
Wire, Wrapping Wire
Approx A3 Perspex coloured sheet
5mm LED IR Receiver
Tools and machines
Saw
Multitool, Screwdriver
Solder Flux, Soldering
Soldering iron (generic)
Project description
Code
Race Timer/Start Finish Line Code
arduino
1// Hot Wheels Race Timer 2// v2.0 3// by Ian Cook 4// Check our YouTube channel at Cooks Projects 5// https://www.youtube.com/channel/UC5M_udtMhAwnymLyAY5qAkw 6// Released Feb 2021 under licence 7// Please acknowledge me if you use any of my code 8 9// A further refined program that allows dual function of constant speed measurement as well as the race timer is available for additional fee. 10// See website links or https://www.emmaalexandercook.com/cooks-projects/p/race-timer for details. 11 12#include <I2CIO.h> 13#include <FastIO.h> 14#include <Wire.h> 15#include <LCD.h> 16#include <LiquidCrystal.h> 17#include <LiquidCrystal_I2C.h> 18 19LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // double check your LCD address first using: i2C_scanner 20//NANO uses SDA on A4 and SCL on A5 21 22 23#define DEBUG false //Use to toggle the debug function on/off 24#define DEBUG_SENSORS false //Use to toggle the sensor debug function 25#define COMP_OUTPUT true //Use this to send info to a connected computer via serial interface. Use false if time is critical in detecting cars simultaneously 26#define SPEED_CRITICAL false //Set to TRUE to improve detection speeds of the SpeedClock() function so that simultaneous detection of cars >20Km/h is always possible. 27 28const int L_LANE_LED=10; //Lane 2 winning LED in D10 PIN 29const int R_LANE_LED=11; //lane 1 winning LED on D11 PIN 30const unsigned int MaxWaitTime=10000; //This is the maximum time to wait for second car to finish before recording result (millisecs) 31const int buzzer=5; //Digital PIN buzzer is attached to - has to allow PWM (pin 3,5,6 or 9) 32 33const int LEFT=1, RIGHT=2, BOTH=11; 34const int RFDataIn = A1, RRDataIn = A3, LFDataIn = A0, LRDataIn = A2; //Define the Anologue pins set up to which sensor. Reading Pin A3 causes Tone() to terminate early 35 //Beware NANO uses the LCD serial transmit (SDA,SCL) on pins A4 and A5 36const int IRLEDThreshold = 800; //Set to a value that indicates when the IR receiver is blocked. IR sensor returns 1024 at maximum brightness. Adjust for your configuration 37const int DISTANCE_BETWEEN_SENSORS = 8; //set this to the distance (cm) between the front and rear IR sensor 38const int L_LANE = 1, R_LANE = 2; 39const int HIGH_TONE = 2400, LOW_TONE = 800; 40const float SENSOR_DISTANCE_FACTOR = float(288); // to speed up program, manually calculate this factor and place here. =float(DISTANCE_BETWEEN_SENSORS/100000)/float(0.27777778); 41// SENSOR_DISTANCE_FACTOR is 288 if the DISTANCE_BETWEEN_SENSORS is 8cm 42 43int FrontSensor[2], RearSensor[2]; //For recording the sensor information and allowing all subroutines to be able to read them. 44// Although array[0] should be available, my analysis shows that using array[0] is unreliable when using global variables. 45 46 47void setup() { 48 49 Serial.begin(9600); 50 pinMode(RFDataIn, INPUT); 51 pinMode(RRDataIn, INPUT); 52 pinMode(LFDataIn, INPUT); 53 pinMode(LRDataIn, INPUT); 54 55 pinMode(R_LANE_LED, OUTPUT); //lane 1 56 pinMode(L_LANE_LED, OUTPUT); //lane 2 57 58 pinMode(buzzer, OUTPUT); // Set buzzer - as an output 59 60 digitalWrite(R_LANE_LED, HIGH); 61 digitalWrite(L_LANE_LED, HIGH); 62 63 lcd.begin(16, 2); 64 lcd.setCursor(3, 0); 65 lcd.print("Race Timer"); 66 lcd.setCursor(3, 1); 67 lcd.print("by:Ian Cook"); 68 delay(2000); 69 lcd.clear(); 70} 71 72void loop() { 73 int Winner; 74 bool R_LANEFinish, L_LANEFinish, R_LANESpeedTrap, L_LANESpeedTrap, Sound; 75 float LSpeed, LScaleSpeed=0, RSpeed, RScaleSpeed=0, WinningDiff, RaceTime; 76 unsigned long StartTime, WaitTime, SampleTime, timediff; 77 unsigned long FrontSensorTime[2], RearSensorTime[2]; //Using array[0] results in a bug with global variables in the compilier, so I haven't used array[0] 78 79 80 // Race reset - so reset variable values 81 FrontSensorTime[L_LANE]= 0; 82 FrontSensorTime[R_LANE]= 0; 83 RearSensorTime[L_LANE] = 0; 84 RearSensorTime[R_LANE] = 0; 85 Winner = 99; 86 LSpeed = 0; 87 RSpeed = 0; 88 WinningDiff = 999.999; 89 RaceTime = 0; 90 timediff =9999; 91 R_LANEFinish = false; 92 L_LANEFinish = false; 93 R_LANESpeedTrap = false; 94 L_LANESpeedTrap = false; 95 96 //Flash lights,sound buzzer and declare we are ready to race 97 lcd.clear(); 98 lcd.setCursor(3, 0); 99 lcd.print("Race Timer "); 100 delay(2000); 101 lcd.setCursor(0, 1); 102 lcd.print("Ready to race..."); 103 digitalWrite(R_LANE_LED, LOW); 104 digitalWrite(L_LANE_LED, LOW); 105 tone(buzzer, LOW_TONE, 600); 106 delay(900); 107 tone(buzzer, LOW_TONE,600); 108 digitalWrite(L_LANE_LED, HIGH); 109 delay(900); 110 tone(buzzer, LOW_TONE,600); 111 digitalWrite(R_LANE_LED,HIGH); 112 delay(900); 113 tone(buzzer, HIGH_TONE,800); 114 digitalWrite(R_LANE_LED, LOW); 115 digitalWrite(L_LANE_LED, LOW); 116 Sound = true; //flag to allow tone to sound as workaround for Arduino bug that stops tone when pin A3 is read! 117 lcd.clear(); 118 lcd.setCursor(4,0); 119 lcd.print("GO GO GO!"); 120 StartTime = millis(); //set the start time of the race which begins when lights go out 121 122 WaitTime = StartTime; //Set this so that we don't wait for ever for a finishing car that has crashed 123 124// Loop to wait until the first car crosses - the do Loop takes 1-2 ms which means speeds over 144 Km/h will sometimes not be read. 288km/h is the max theoretical speed detectable on Arduino Nano 125 do { 126//Read the sensor data and record the time 127 SampleTime = SampleSensors(Sound); 128 if (Sound && SampleTime>(StartTime+800)) Sound = false; //Workaround for Tone bug that stops tone when pin A3 read! 129 130//Set DEBUG flag for your setup to make sure the constant IRLEDThreshold value is accurate for your hardware 131//If DEBUG is left true then these serial prints will slow the sample rate considerably and reduce the max detectable speed to approx 7Km/h 132 #if (DEBUG) 133 TestSensors(false); //Debug routine to display the sensor recorded state 134 #endif 135 136 if (!L_LANEFinish && (FrontSensor[L_LANE] < IRLEDThreshold)) { 137 // Left Lane has finished 138 FrontSensorTime[L_LANE] = SampleTime; 139 if (!R_LANEFinish) { 140 //As the other lane hasn't finished yet, this lane is the winner 141 Winner = L_LANE; 142 digitalWrite(L_LANE_LED, HIGH); 143 } 144 L_LANEFinish = true; 145 WaitTime = millis(); //Reset the timer to ensure we don't wait if the other lane has crashed 146 } 147 if (!R_LANEFinish && (FrontSensor[R_LANE] < IRLEDThreshold)) { 148 // Right Lane has finished 149 FrontSensorTime[R_LANE] = SampleTime; 150 if (!L_LANEFinish) { 151 //As the other lane hasn't finished yet, this lane is the winner 152 Winner = R_LANE; 153 digitalWrite(R_LANE_LED, HIGH); 154 } 155 R_LANEFinish = true; 156 WaitTime = millis(); //Reset the timer to ensure we don't wait if the other lane has crashed 157 } 158 159 if (L_LANEFinish && !L_LANESpeedTrap && RearSensor[L_LANE] < IRLEDThreshold) { 160 //The car has crossed the line and now the rear sensor 161 RearSensorTime[L_LANE] = SampleTime; 162 L_LANESpeedTrap = true; 163 } 164 if (R_LANEFinish && !R_LANESpeedTrap && RearSensor[R_LANE] < IRLEDThreshold) { 165 //The car has crossed the line and now the rear sensor 166 RearSensorTime[R_LANE] = SampleTime; 167 R_LANESpeedTrap = true; 168 } 169// Exit the loop once all sensors have been triggered or once one lane has finished and there has been a delay > MaxWaitTime 170 } while (!((L_LANEFinish && R_LANEFinish && L_LANESpeedTrap && R_LANESpeedTrap) || ((L_LANEFinish || R_LANEFinish) && (millis() - WaitTime > MaxWaitTime)) ) ); 171 172 tone(buzzer, HIGH_TONE,150); 173 delay(200); 174 tone(buzzer, HIGH_TONE,150); 175 176// Send information to a connected computer which can be used in data analysis 177#if (COMP_OUTPUT) 178 Serial.println("Times:\ LEFT Front,\ Rear,\ RIGHT Front,\ Rear"); 179 Serial.print("\ "); 180 Serial.print(FrontSensorTime[L_LANE]); 181 Serial.print("\ "); 182 Serial.print(RearSensorTime[L_LANE]); 183 Serial.print("\ \ "); 184 Serial.print(FrontSensorTime[R_LANE]); 185 Serial.print("\ "); 186 Serial.println(RearSensorTime[R_LANE]); 187#endif 188//Race is over. Calculate and display times 189//Race Time is time for first car to finish 190 191switch (Winner) { 192 case R_LANE: 193 RaceTime = float (FrontSensorTime[R_LANE]-StartTime)/1000; //Recorded in Sec 194 if (FrontSensorTime[L_LANE]>0) WinningDiff = float(FrontSensorTime[L_LANE] - FrontSensorTime[R_LANE])/1000; 195 break; 196 case L_LANE: 197 RaceTime = float (FrontSensorTime[L_LANE]-StartTime)/1000; 198 if (FrontSensorTime[R_LANE]>0) WinningDiff = float(FrontSensorTime[R_LANE] - FrontSensorTime[L_LANE])/1000; 199 break; 200 default: 201 RaceTime = 0; 202} 203 204 //Check for a draw due to sample timings being equal 205 if (WinningDiff == 0) Winner=555; 206 207//The distance between the sensors is DistanceBetweenSensors (cm) so the speed is D cm/time taken (cm/ms *3 600 000 / 100 000 = km/h) :: Millis() returns milliseconds which 1/1000 s 208// Km/h = (D/100000)/(milli/(1000*60*60)) = D/(milli/3600) or D/3600 * 1/milli; D=8 then km/h = 288 / milli 209//Max speed capable of reading at 2ms sample rate is 144 Km/h! 210// Speed = Dist in KM (cm /100000) / time in hrs ( millis/1000/60/60) 211// distance = 8 cm then Speed = (8/100000)/(millis/.27777778) = (0.00008/0.27777778)/millis = 288/millis 212 213 timediff = RearSensorTime[L_LANE] - FrontSensorTime[L_LANE]; 214 if (timediff>0) { 215 LSpeed = SENSOR_DISTANCE_FACTOR / float(timediff); //km/h 216 #if (DEBUG) 217 //Use to check that the SENSOR_DISTANCE_FACTOR is correct for your set up 218 Serial.print ("Time Diff:\ "); 219 Serial.print (timediff); 220 Serial.print (" Lspeed calc "); 221 Serial.println (LSpeed); 222 #endif 223 } 224 else LSpeed = 0; 225 226 timediff = RearSensorTime[R_LANE] - FrontSensorTime[R_LANE]; 227 if (timediff>0) { 228 RSpeed = SENSOR_DISTANCE_FACTOR / float(timediff); //km/h 229 #if (DEBUG) 230 //Use to check that the SENSOR_DISTANCE_FACTOR is correct for your set up 231 Serial.print ("Time Diff:\ "); 232 Serial.print (timediff); 233 Serial.print (" Rspeed calc "); 234 Serial.println (RSpeed); 235 #endif 236 } 237 else RSpeed = 0; 238 239//Hot wheels cars are 1:64 scale so calculate scale speed 240 LScaleSpeed=LSpeed*float(64); 241 RScaleSpeed=RSpeed*float(64); 242 243//Send information to serial device (computer) for data analysis 244 #if (COMP_OUTPUT) 245 Serial.print("Race Time: "); 246 Serial.println(RaceTime); 247 Serial.println("Final Speeds"); 248 Serial.print("LEFT: "); 249 Serial.print(LSpeed,3); 250 Serial.print(" Km/h : Scale Spd= "); 251 Serial.print(LScaleSpeed,2); 252 Serial.println(" Km/h"); 253 Serial.print("RIGHT: "); 254 Serial.print(RSpeed,3); 255 Serial.print(" Km/h : Scale Spd= "); 256 Serial.print(RScaleSpeed,2); 257 Serial.println(" Km/h"); 258 259 Serial.print("Left Lane Race Time (ms)"); 260 Serial.println("\ Right Lane Race Time (ms)"); 261 Serial.print(FrontSensorTime[L_LANE]); 262 Serial.print("\ \ \ "); 263 Serial.println(FrontSensorTime[R_LANE]); 264 if (Winner== 555) Serial.print("This was a DRAW! "); 265 else if (Winner == R_LANE) Serial.print("Right car won by "); 266 else Serial.print("Left car won by "); 267 Serial.print(WinningDiff,3); 268 Serial.println(" s"); 269 #endif 270 271//Send the information to the LCD display on the hardware unit 272 lcd.clear(); 273 lcd.setCursor(0,0); 274 lcd.print("Race Time:"); 275 lcd.setCursor(10,0); 276 lcd.print(RaceTime,3); 277 lcd.setCursor(14,0); 278 lcd.print("s "); 279 lcd.setCursor(0,1); 280 if (Winner == L_LANE) { 281 lcd.print("L won by"); 282 } 283 else if (Winner == R_LANE) { 284 lcd.print("R won by"); 285 } 286 else { 287 lcd.print("DRAW!"); 288 } 289 lcd.setCursor(10,1); 290 lcd.print(WinningDiff,3); 291 lcd.setCursor(15,1); 292 lcd.print("s"); 293 delay(5000); 294 295 lcd.clear(); 296 lcd.setCursor(0, 0); 297 lcd.print("LSpd:"); 298 lcd.setCursor(5, 0); 299 if (LSpeed > 0.001) { 300 lcd.print(LSpeed, 2); 301 lcd.setCursor(11, 0); 302 lcd.print(" Km/h"); 303 } 304 else lcd.print(" * DNF *"); //This lane did not finish 305 lcd.setCursor(0, 1); 306 lcd.print("RSpd:" ); 307 lcd.setCursor(5, 1); 308 if (RSpeed > 0.001) { 309 lcd.print(RSpeed, 2); 310 lcd.setCursor(11, 1); 311 lcd.print(" Km/h"); 312 } 313 else lcd.print(" * DNF *"); //This lane did not finish 314 315 if (Winner != 555) { 316 //i.e. This is NOT a draw 317 //set cursor for Winnner text 318 delay(1000); 319 lcd.setCursor(11, (Winner-L_LANE)); 320 lcd.print("*WIN*"); 321 for (int i=0; i<6; i++) { 322 digitalWrite(Winner+9, LOW); 323 delay(400); 324 digitalWrite(Winner+9, HIGH); 325 delay(400); 326 } 327 } 328 else { 329 //This must be a draw 330 lcd.setCursor(12,0); 331 lcd.print ("DRAW"); 332 lcd.setCursor(12,1); 333 lcd.print ("DRAW"); 334 for (int i=0; i<6; i++) { 335 digitalWrite(L_LANE, LOW); 336 digitalWrite(R_LANE, LOW); 337 delay(400); 338 digitalWrite(R_LANE, HIGH); 339 digitalWrite(L_LANE, HIGH); 340 delay(400); 341 } 342 } 343 344 lcd.clear(); 345 lcd.setCursor(0, 0); 346 lcd.print("LScaleSpd:"); 347 if (LScaleSpeed<10) lcd.setCursor(12,0); 348 else { 349 if (LScaleSpeed<100) lcd.setCursor(11, 0); //line up the decimal points on the display 350 else lcd.setCursor(10,0); 351 } 352 if (LScaleSpeed >0) lcd.print(LScaleSpeed, 1); 353 else lcd.print("DNF"); 354 lcd.setCursor(0, 1); 355 lcd.print("RScaleSpd:"); 356 if (RScaleSpeed<10) lcd.setCursor(12,1); 357 else { 358 if (RScaleSpeed<100) lcd.setCursor(11, 1); //line up the decimal points on the display 359 else lcd.setCursor(10,0); 360 } 361 if (RScaleSpeed >0) lcd.print(RScaleSpeed, 1); 362 else lcd.print("DNF"); 363 364 // Delay before staring the race again. 365 delay(5000); 366} 367 368 369unsigned long SampleSensors(bool sound) { 370//Read the sensor data and return the time they are taken (ms) 371//The parameter sound is used to prevent the tone being terminated early due to Arduino Nano issue that reading A3 pin causes the tone to stop! 372 FrontSensor[R_LANE] = analogRead(RFDataIn); 373 FrontSensor[L_LANE] = analogRead(LFDataIn); 374//This section only compiles the relevant code if RRDataIn is connected to pin A3, defined in constants. This is to prevent cutting tone short during start sequence 375#if RRDataIn == A3 376 if (!sound) RearSensor[R_LANE] = analogRead(RRDataIn); 377#else 378 RearSensor[R_LANE] = analogRead(RRDataIn); 379#endif 380 RearSensor[L_LANE] = analogRead(LRDataIn); 381 return(millis()); 382}
Downloadable files
speedtraplogo_575uywYBWU.png
speedtraplogo_575uywYBWU.png
speedtraplogo_575uywYBWU.png
speedtraplogo_575uywYBWU.png
Comments
Only logged in users can leave comments