1
2
3
4
5
6
7
8
9
10
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);
20
21
22
23#define DEBUG false
24#define DEBUG_SENSORS false
25#define COMP_OUTPUT true
26#define SPEED_CRITICAL false
27
28const int L_LANE_LED=10;
29const int R_LANE_LED=11;
30const unsigned int MaxWaitTime=10000;
31const int buzzer=5;
32
33const int LEFT=1, RIGHT=2, BOTH=11;
34const int RFDataIn = A1, RRDataIn = A3, LFDataIn = A0, LRDataIn = A2;
35
36const int IRLEDThreshold = 800;
37const int DISTANCE_BETWEEN_SENSORS = 8;
38const int L_LANE = 1, R_LANE = 2;
39const int HIGH_TONE = 2400, LOW_TONE = 800;
40const float SENSOR_DISTANCE_FACTOR = float(288);
41
42
43int FrontSensor[2], RearSensor[2];
44
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);
56 pinMode(L_LANE_LED, OUTPUT);
57
58 pinMode(buzzer, 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];
78
79
80
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
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;
117 lcd.clear();
118 lcd.setCursor(4,0);
119 lcd.print("GO GO GO!");
120 StartTime = millis();
121
122 WaitTime = StartTime;
123
124
125 do {
126
127 SampleTime = SampleSensors(Sound);
128 if (Sound && SampleTime>(StartTime+800)) Sound = false;
129
130
131
132 #if (DEBUG)
133 TestSensors(false);
134 #endif
135
136 if (!L_LANEFinish && (FrontSensor[L_LANE] < IRLEDThreshold)) {
137
138 FrontSensorTime[L_LANE] = SampleTime;
139 if (!R_LANEFinish) {
140
141 Winner = L_LANE;
142 digitalWrite(L_LANE_LED, HIGH);
143 }
144 L_LANEFinish = true;
145 WaitTime = millis();
146 }
147 if (!R_LANEFinish && (FrontSensor[R_LANE] < IRLEDThreshold)) {
148
149 FrontSensorTime[R_LANE] = SampleTime;
150 if (!L_LANEFinish) {
151
152 Winner = R_LANE;
153 digitalWrite(R_LANE_LED, HIGH);
154 }
155 R_LANEFinish = true;
156 WaitTime = millis();
157 }
158
159 if (L_LANEFinish && !L_LANESpeedTrap && RearSensor[L_LANE] < IRLEDThreshold) {
160
161 RearSensorTime[L_LANE] = SampleTime;
162 L_LANESpeedTrap = true;
163 }
164 if (R_LANEFinish && !R_LANESpeedTrap && RearSensor[R_LANE] < IRLEDThreshold) {
165
166 RearSensorTime[R_LANE] = SampleTime;
167 R_LANESpeedTrap = true;
168 }
169
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
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
189
190
191switch (Winner) {
192 case R_LANE:
193 RaceTime = float (FrontSensorTime[R_LANE]-StartTime)/1000;
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
205 if (WinningDiff == 0) Winner=555;
206
207
208
209
210
211
212
213 timediff = RearSensorTime[L_LANE] - FrontSensorTime[L_LANE];
214 if (timediff>0) {
215 LSpeed = SENSOR_DISTANCE_FACTOR / float(timediff);
216 #if (DEBUG)
217
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);
229 #if (DEBUG)
230
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
240 LScaleSpeed=LSpeed*float(64);
241 RScaleSpeed=RSpeed*float(64);
242
243
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
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 *");
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 *");
314
315 if (Winner != 555) {
316
317
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
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);
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);
359 else lcd.setCursor(10,0);
360 }
361 if (RScaleSpeed >0) lcd.print(RScaleSpeed, 1);
362 else lcd.print("DNF");
363
364
365 delay(5000);
366}
367
368
369unsigned long SampleSensors(bool sound) {
370
371
372 FrontSensor[R_LANE] = analogRead(RFDataIn);
373 FrontSensor[L_LANE] = analogRead(LFDataIn);
374
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}
Anonymous user
2 years ago
My 12-y.o. and I just built this. It works great! The schematic and design were perfect.. It took maybe 4-5 hours to build, mostly doing the wiring for the LEDs and sensors. It works great on cars and we've had a lot of good races. We've also used it to measure Nerf speeds. The time resolution at Nerf-like speeds isn't as good as it is for cars, but it's still very reliable at catching the dart as it passes. The default sensitivity settings worked fine. A couple of minor comments: - Final device size is 12 cm on each side. We were able to cut all the parts out of a 12 cm x 60 cm Perspex sheet, 3 mm thickness. I used an Arduino Nano Every, on a breadboard. - There are a few quirks in the version of the code as posted. A) The RHS scale speed does not display at all (or rather, it's printed on top of the LHS speed). To fix, there's SetCursor(10,0) command in line 360, which should be (10,1). B) The scale speed only prints three three digits (e.g., 1002 kph is truncated to 002 kph). It's pretty easy to exceed this. To fix, change the formatting in lines 345 - 365. - The biggest problem was shoving all of the wires inside the case when done! I cut them all far too generously. - The LCD we have appears to match the one you link to in the description, but I had to use a different driver to get it to work. - As an upgrade, I may add a switch to restart the clock, rather than wait for it to time out. The switch could also toggle it between two-track mode, and single-track mode. Oftentimes we don't need both tracks -- we just want to measure speed along a single track.