Notifications
Clear all

Pinewood Derby Drag Racing Style Time

15 Posts
4 Users
0 Reactions
737 Views
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Let me start off with saying coding is most definitely my weak link and this project has me scratching my head a bit.

We have a Pinewood Derby track at work that is used a once a year. A event looked forward to by all. Long story short I have inherited the care and maintenance of the track which was originally was built by others. When it was handed to me a few years ago the computer was missing. I quickly figured out the timer is Arduino Uno based and communicates with a PC via Processing 3, developed by Bill V. at Minimal Hardware Designs.

https://billvsderbytimers.weebly.com/minimal-hw-designs.html

The issue now is we use the track in more of a drag racing style than the Boy Scout style it currently is. I remember that Bill V"s site also offered drag racing timers. The one I'm interested in is the one for RC car and could very easily modified to suit our needs. It uses an Arduino Mega, has all the bells and whistles we wish to have. The program provides the outputs for a drag racing tree, the reaction time/separate start.  Via Processing 3 it will display status on the computer screen same as the Boy Scout timer (which we also connect to an LCD projector) and you can even connect a printer for time slips! Very cool! All that, but it is missing one feature, an end of track display, where everyone's eyes will be as the race ends.

My first thought was that I can add this via I2C LED displays and a couple of standard LED's to show the winning lane. Simple Right? it probably would be, the issue is the Processing software is connected via the USB port which is also the only I2C port the Arduino Mega has.

Any ideas anyone?

As the INO file is quite long, so I have added it as a attachment

Thanks,

Steve

 


   
Quote
TFMcCarthy
(@tfmccarthy)
Member
Joined: 10 months ago
Posts: 331
 

The winner of the race is indicated by blinking the green "GO" light for the winning lane on the Xmas tree lights at the start of the track (I assume they're at the track start). The winner light blinks for 15 seconds (you can adjust the blink time). The PC software should show the race times.

Mirror the "GO" lights at the end of the track.

Lane 1 is pin 32
Lane 2 is pin 33

The one who has the most fun, wins!


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Yes that would be easy enough. I also wish to display, for each lane, the elapsed time at the end of the track and maybe the miles per hour with an LED win light on top. I have used the I2C displays from Spark Fun for other projects. They are very easy to use and work great and I still have some.

https://www.sparkfun.com/products/16916

The major issue with that is the Processing 3 is hogging up the serial port that is also the I2C port.


   
ReplyQuote
TFMcCarthy
(@tfmccarthy)
Member
Joined: 10 months ago
Posts: 331
 

@stevenr8062 

OK. The code currently uses the PC program (the Processing 3 program) for all display. The Ardunio sketch is a sensor. That's a change in the circuit from sensor to sensor + display. Fine.

I don't know enough about Arduino Mega I2C or any I2C conflict with the serial port to comment on that. SparkFun doesn't address how to handle the Serial port conflict?

You might consider adding a second serial port?

Use Multiple Serial Ports on the Arduino Mega | Arduino Documentation

Then change the sketch to use that port instead of Serial and free the Serial port for SparkFun. 

The one who has the most fun, wins!


   
ReplyQuote
(@davee)
Member
Joined: 4 years ago
Posts: 1963
 

Hi @stevenr8062,

 RE: it probably would be, the issue is the Processing software is connected via the USB port which is also the only I2C port the Arduino Mega has.

It is probably just me getting confused, but the two different schematics I found for the Arduino Mega at

https://content.arduino.cc/assets/MEGA2560_Rev3e_sch.pdf

and

https://www.arduino.cc/en/uploads/Main/arduino-mega-schematic.pdf

seem to show the USB finds its way to 2560 chip pins 2 & 3 of the 2560, also known as PE0 and PE1

I2C uses D20 D21, according to https://docs.arduino.cc/learn/communication/wire/

which according to the schematic are the chip pins 43 and 44, also labelled PD0 and PD1, and SDA and SCL

------------------

Perhaps the USB port you have in mind is not the one on the Mega board, or some other confusion.

Sorry, if it is a red herring, but I am hoping it is helpful.

Best wishes, Dave

 


   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@stevenr8062 How is the serial port also the I2C port? Totally different hardware!

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Ok perhaps I'm not seeing the problem clearly. So as mentioned early on programing is my weak link. So I did do some research and must have misunderstood the connections. So I guess the question how do I keep the data going were it should go when both use the serial monitor and the serial begin command? Second what pins do I connect the new I2C devices? Below is the communication portion of the program.

 

 //Software Setup
  Turnoff_All_Lights();               //Call subroutine to turn off Xmas Tree lights
  Serial.begin(9600);                 //Enable serial comms to PC at 9600 baud on Port0
  trackstate = NotReady;              //Set initial track state to not ready
  Ln1_State = NotReady;               //Set Lane1 initial race state to not ready
  Ln2_State = NotReady;               //Set Lane2 initial race state to not ready
  Foul_Flag[0] = 0;                   //Preset Lane1 foul flag to 0
  Foul_Flag[1] = 0;                   //Preset Lane2 foul flag to 0
  XTmode = 0;                         //Set Xmas tree sequence to Std mode
  Autostart_Flag=false;               //Preset Autostart flag to "manual" start
  Lane_Usage = 3;                     //Preset for 2-racer mode
  Grd_Enable = true;                  //Enable Guard Beam capability if they're used
}

   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Opps! I mean to say: Below is the communication setup portion of the program.


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

After thought... I guess should also mention the Spark Fun I2C calls the print() function to display messages. So if I add the print() for displays, will it confuse the communication to the computer? I guess this the part that I don't understand.


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Below is the first part of the main loop dealing with serial monitor.

 

//======================================== MAIN LOOP ============================================
void loop() {
//CHECK & PROCESS INCOMING SERIAL DATA
if (Serial.available() > 0) { //Check if serial data
serial_data = Serial.readStringUntil('\n'); //And get it if so
Serial.flush();
if (serial_data.indexOf('R') >= 0) { //Check for Reset request from PC
timer_reset(); //Call subroutine to reset the timer
}
if (serial_data.indexOf('S') >= 0) { //Check for race start request from PC
if (trackstate == Staged) { //Check if vehicles are staged
Set_Dialin_Delays(); //Call subroutine to set dial-in delays
trackstate = Countdown; //Set race state to Countdown mode
if (Lane_Usage==1 || Lane_Usage==3) Ln1_State = Countdown; //Set Lane1 state to Countdown state if used
if (Lane_Usage==2 || Lane_Usage==3) Ln2_State = Countdown; //Set Lane2 state to Countdown state if used
Serial.println(SMsg3); //Send Countdown state message to PC
}
}
if (serial_data.indexOf('H') >= 0) { //Check for hold request from PC
Hold_Flag = true; //Set Hold flag
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf('C') >= 0) { //Check for continue request from PC
Hold_Flag = false; //Clear Hold flag
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf("LT") >= 0) { //Check for Xmas tree lamp test request from PC
Lamptest(); //Call Xmas Tree Lamp Test subroutine
Serial.println("@"); //Send Acknowledge message to PC
}
if (serial_data.indexOf("M0") >= 0) { //Check for Xmas Tree "Std" mode request from PC
XTmode = 0; //Set Xmas Tree to Std Mode if true
Serial.println("@"); //Send Acknowledge message to PC
}
if (serial_data.indexOf("M1") >= 0) { //Check for Xmas Tree "Pro" mode request from PC
XTmode = 1; //Set Xmas Tree to Std Mode if true
Serial.println("@"); //Send Acknowledge message to PC
}
if (serial_data.indexOf("M2") >= 0) { //Check for Xmas Tree "Outlaw" mode request from PC
XTmode = 2; //Set Xmas Tree to Std Mode if true
Serial.println("@"); //Send Acknowledge message to PC
}
if (serial_data.indexOf("A1") >= 0) { //Check for timer auto-start request from PC
Autostart_Flag = true; //Set timer to Auto-start mode
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf("A0") >= 0) { //Check for timer manual-start request from PC
Autostart_Flag = false; //Set timer to Manual-start mode
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf("P1") >= 0) { //Check for single racer on Lane1 request from PC
Lane_Usage = 1; //Set to single racer on Lane 1 mode
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf("P2") >= 0) { //Check for single racer on Lane2 request from PC
Lane_Usage = 2; //Set to single racer on Lane 2 mode
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf("P3") >= 0) { //Check for dual racer request from PC
Lane_Usage = 3; //Set to dual racer mode
Serial.println("@"); //Send acknowledge message to PC
}
if (serial_data.indexOf("D1:") >= 0) { //Check for receipt of Lane1 dial-in time from PC
//Input string format: D1:xxxxx where xxxxx is Lane1 dial-in time in milliseconds
//Valid range: 00000 - 09999 (0.0 to 9.999 seconds)
if (serial_data.length() == 8) { //Check integrity of input string
Str1 = serial_data.substring(3, 8); //Extract Lane1 dial-in time
Dialin1 = Str1.toInt(); //And convert to integer
Serial.println("@"); //Send acknowledge message to PC
}
}
if (serial_data.indexOf("D2:") >= 0) { //Check for receipt of Lane2 dial-in time from PC
//Input string format: D2:xxxxx where xxxxx is Lane2 dial-in time in milliseconds
//Valid range: 00000 - 09999 (0.0 to 9.999 seconds)
if (serial_data.length() == 8) { //Check integrity of input string
Str1 = serial_data.substring(3, 8); //Extract Lane2 dial-in time
Dialin2 = Str1.toInt(); //And convert to integer
Serial.println("@"); //Send acknowledge message to PC
}
}
//max_duration
if (serial_data.indexOf("TO:") >= 0) { //Check for receipt of timer timeout duration from PC
//Input string format: TO:xx where xx is the timeout duration in seconds
//Valid range: 05 - 30
if (serial_data.length() == 5) { //Check integrity of input string
Str1 = serial_data.substring(3, 5); //Extract timeout duration in seconds
max_duration = Str1.toInt() * 1000; //And convert to milliseconds
Serial.println("@"); //Send acknowledge message to PC
}
}
}


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Ok lets say I don't care about the status of the displays or want to send status to the serial monitor and lets see if I have the basic concept of adding the displays to the program correct. 

  • include the wire.h library
  • include the SparkFun_Alphanumeric_Display.h  library
  • in the setup loop wire.begin() //to join the I2C bus
  • in the main loop find the final timer results for lane1 and lane 2
    Lane 1
        if (!Fin1_Flag) {                               //Check & skip if Lane1 already crossed finish line
          if (digitalRead(Fin_Sense1)==trip) {          //Is Car over Lane1 Finish Line?
            Fin_time[0] = Clk_Time;                     //Yes...get time
            Ln1_State = Finished;                       //Set Lane1 state to finished
            Fin1_Flag = true;                           //Set Lane1 finished sensed flag
            digitalWrite(XtreeGrn1,HIGH);               //Turn off Lane1 Green Go Indicator
          }
    Lane 2
        if (!Fin2_Flag) {                               //Check & skip if Lane2 already crossed finish line
          if (digitalRead(Fin_Sense2)==trip) {          //Is Car over Lane2 Finish Line?
            Fin_time[1] = Clk_Time;                     //Yes...get time
            Ln2_State = Finished;                       //Set Lane2 state to finished
            Fin2_Flag = true;                           //Set Lane2 finished sensed flag
            digitalWrite(XtreeGrn2,HIGH);               //Turn off Lane2 Green Go Indicator
          }
     
  • display.print(Fin_time[0] = Clk_Time) // for lane one display
  • display.print(Fin_time[1] = Clk_Time) // for lane two display

I have not found a good example of how use to the addressing of the displays. The default address of the displays 0x70 and can be changed via jumpers. So assuming lane one is 0x70 and lane 2 is 0x71 what else do I need to add? Also how to blank the displays during the timer/track reset subroutine?

Open to suggestions so I may better learn to use I2C devices. The spark fun examples are not very clear on how to do this. What other library examples might I use give some insight?

Thank you everyone

Steve


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Wow! what time it? lol. 

OK, did some more digging. To solve the serial port issues I decided to go go with an Arduino Due. It has a second serial port and is about the same price as the Mega. Had a long talk with ChatGPT and a direction. As I suspected it is way more involved that I thought, but at least it is a start. The concept is to add a end of track display with forward and aft displays. Also a win light via a standard LED. Also as expected it did not verify as is. Got some work to do to integrate it into the original code. Below is what I come up with:

//Reset Variables and Displays:
//The resetTrack() function resets the timing and speed variables (guardToFinishTimeLane1, guardToFinishTimeLane2, speedLane1, speedLane2) to zero and clears the alphanumeric displays.
//
//Track Reset Subroutine:
//Call resetTrack() whenever the race is restarted. This subroutine handles clearing displays, resetting race variables, and printing a reset message to Serial1.
//
//Display Reset:
//The resetDisplays() function is called within resetTrack() to reset each of the displays, showing "----" as a placeholder when the displays are cleared.
//
//How It Works:
//Before Each Race: When the track reset subroutine is called (via resetTrack()), all timing data is cleared, and the displays are reset to show placeholder.
//After Each Race: The elapsed time and speed are displayed, followed by a delay, allowing the user to read the data before the next reset.
//Testing:
//You can call resetTrack() at any point in your code, such as after a race, or tie it to a physical button or other track reset mechanisms.
//
#include <Wire.h>
#include <SparkFun_Alphanumeric_Display.h>

// Initialize the displays for each lane and position
HT16K33 displayLane1Start;  // Display at the start line for lane 1
HT16K33 displayLane1Down;   // Display down the track for lane 1
HT16K33 displayLane2Start;  // Display at the start line for lane 2
HT16K33 displayLane2Down;   // Display down the track for lane 2

// Track-related variables
float guardToFinishTimeLane1 = 0;
float guardToFinishTimeLane2 = 0;
float speedLane1 = 0;
float speedLane2 = 0;

void setup() {
  Serial.begin(9600);      // Communication with Serial Monitor via Serial1
  Serial1.begin(9600);     // Debugging on Serial1

  Wire.begin();

  // Initialize each display with its unique I2C address
  displayLane1Start.begin(0x70);  // Lane 1 Start Line Display
  displayLane1Down.begin(0x71);   // Lane 1 Down Track Display
  displayLane2Start.begin(0x72);  // Lane 2 Start Line Display
  displayLane2Down.begin(0x73);   // Lane 2 Down Track Display

  // Clear displays at startup
  resetDisplays();
}

void loop() {
  // Simulate race completion with example guard-to-finish times for each lane
  guardToFinishTimeLane1 = 0.421;  // Example elapsed time for lane 1 in seconds
  guardToFinishTimeLane2 = 0.438;  // Example elapsed time for lane 2 in seconds

  // Calculate speed (using 1.4 feet distance for trap sensor)
  speedLane1 = calculateSpeed(guardToFinishTimeLane1);
  speedLane2 = calculateSpeed(guardToFinishTimeLane2);

  // Display the elapsed time on all displays
  displayElapsedTime(guardToFinishTimeLane1, guardToFinishTimeLane2);
 
  // Introduce a readable delay before displaying speed
  delay(2000);

  // Display the speed on all displays
  displaySpeed(speedLane1, speedLane2);

  // Introduce a readable delay before the next race
  delay(2000);

  // Simulate track reset
  resetTrack();
}

// Function to calculate speed based on elapsed time between guard and finish sensor (distance = 1.4 feet)
float calculateSpeed(float elapsedTime) {
  float distance = 1.4;  // Distance in feet
  return (distance / elapsedTime) * 0.681818;  // Speed in mph (conversion factor)
}

// Function to display the elapsed time on the displays
void displayElapsedTime(float time1, float time2) {
  // Display Lane 1 elapsed time
  displayLane1Start.clear();
  displayLane1Down.clear();
  displayLane1Start.displayText(String(time1, 3).c_str(), 0);  // Show elapsed time with 3 decimal places
  displayLane1Down.displayText(String(time1, 3).c_str(), 0);

  // Display Lane 2 elapsed time
  displayLane2Start.clear();
  displayLane2Down.clear();
  displayLane2Start.displayText(String(time2, 3).c_str(), 0);
  displayLane2Down.displayText(String(time2, 3).c_str(), 0);

  // Report the elapsed times to Serial1 for verification
  Serial1.print("Lane 1 Elapsed Time: ");
  Serial1.print(time1, 3);
  Serial1.println(" seconds");

  Serial1.print("Lane 2 Elapsed Time: ");
  Serial1.print(time2, 3);
  Serial1.println(" seconds");
}

// Function to display the speed on the displays
void displaySpeed(float speed1, float speed2) {
  // Display Lane 1 speed
  displayLane1Start.clear();
  displayLane1Down.clear();
  displayLane1Start.displayText(String(speed1, 1).c_str(), 0);  // Show speed with 1 decimal place
  displayLane1Down.displayText(String(speed1, 1).c_str(), 0);

  // Display Lane 2 speed
  displayLane2Start.clear();
  displayLane2Down.clear();
  displayLane2Start.displayText(String(speed2, 1).c_str(), 0);
  displayLane2Down.displayText(String(speed2, 1).c_str(), 0);

  // Report the speeds to Serial1 for verification
  Serial1.print("Lane 1 Speed: ");
  Serial1.print(speed1, 1);
  Serial1.println(" mph");

  Serial1.print("Lane 2 Speed: ");
  Serial1.print(speed2, 1);
  Serial1.println(" mph");
}

// Function to reset the track, displays, and variables
void resetTrack() {
  // Reset track-related variables
  guardToFinishTimeLane1 = 0;
  guardToFinishTimeLane2 = 0;
  speedLane1 = 0;
  speedLane2 = 0;

  // Clear the displays
  resetDisplays();

  Serial1.println("Track reset complete.");
}

// Function to clear the alphanumeric displays
void resetDisplays() {
  // Clear Lane 1 displays
  displayLane1Start.clear();
  displayLane1Down.clear();
  displayLane1Start.displayText("----", 0);
  displayLane1Down.displayText("----", 0);

  // Clear Lane 2 displays
  displayLane2Start.clear();
  displayLane2Down.clear();
  displayLane2Start.displayText("----", 0);
  displayLane2Down.displayText("----", 0);
}
Hope it helps someone with similar project. I will post any updates or changes and the final code when I get working. 🙂

   
ReplyQuote
Ron
 Ron
(@zander)
Father of a miniature Wookie
Joined: 5 years ago
Posts: 8047
 

@stevenr8062 In the future, please read the HELP to paste the code. It is also a good idea to do a Tools/Auto Format before the copy as well. Can't find the Help, do a page find for Code

Screenshot 2024 08 18 at 15.16.24

First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.


   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

My apologies. Was up quite late working this project and should have waited until today to post when i could pay attention to detail.

let's give it another go, hope it is not considered to long.

 

//Reset Variables and Displays:
//The resetTrack() function resets the timing and speed variables (guardToFinishTimeLane1, guardToFinishTimeLane2, speedLane1, speedLane2) to zero and clears the alphanumeric displays.
//
//Track Reset Subroutine:
//Call resetTrack() whenever the race is restarted. This subroutine handles clearing displays, resetting race variables, and printing a reset message to Serial1.
//
//Display Reset:
//The resetDisplays() function is called within resetTrack() to reset each of the displays, showing "----" as a placeholder when the displays are cleared.
//
//How It Works:
//Before Each Race: When the track reset subroutine is called (via resetTrack()), all timing data is cleared, and the displays are reset to show placeholder.
//After Each Race: The elapsed time and speed are displayed, followed by a delay, allowing the user to read the data before the next reset.
//Testing:
//You can call resetTrack() at any point in your code, such as after a race, or tie it to a physical button or other track reset mechanisms.
//
#include <Wire.h>
#include <SparkFun_Alphanumeric_Display.h>

// Initialize the displays for each lane and position
HT16K33 displayLane1Start;  // Display at the start line for lane 1
HT16K33 displayLane1Down;   // Display down the track for lane 1
HT16K33 displayLane2Start;  // Display at the start line for lane 2
HT16K33 displayLane2Down;   // Display down the track for lane 2

// Track-related variables
float guardToFinishTimeLane1 = 0;
float guardToFinishTimeLane2 = 0;
float speedLane1 = 0;
float speedLane2 = 0;

void setup() {
  Serial.begin(9600);      // Communication with Serial Monitor via Serial1
  Serial1.begin(9600);     // Debugging on Serial1

  Wire.begin();

  // Initialize each display with its unique I2C address
  displayLane1Start.begin(0x70);  // Lane 1 Start Line Display
  displayLane1Down.begin(0x71);   // Lane 1 Down Track Display
  displayLane2Start.begin(0x72);  // Lane 2 Start Line Display
  displayLane2Down.begin(0x73);   // Lane 2 Down Track Display

  // Clear displays at startup
  resetDisplays();
}

void loop() {
  // Simulate race completion with example guard-to-finish times for each lane
  guardToFinishTimeLane1 = 0.421;  // Example elapsed time for lane 1 in seconds
  guardToFinishTimeLane2 = 0.438;  // Example elapsed time for lane 2 in seconds

  // Calculate speed (using 1.4 feet distance for trap sensor)
  speedLane1 = calculateSpeed(guardToFinishTimeLane1);
  speedLane2 = calculateSpeed(guardToFinishTimeLane2);

  // Display the elapsed time on all displays
  displayElapsedTime(guardToFinishTimeLane1, guardToFinishTimeLane2);
  
  // Introduce a readable delay before displaying speed
  delay(2000);

  // Display the speed on all displays
  displaySpeed(speedLane1, speedLane2);

  // Introduce a readable delay before the next race
  delay(2000);

  // Simulate track reset
  resetTrack();
}

// Function to calculate speed based on elapsed time between guard and finish sensor (distance = 1.4 feet)
float calculateSpeed(float elapsedTime) {
  float distance = 1.4;  // Distance in feet
  return (distance / elapsedTime) * 0.681818;  // Speed in mph (conversion factor)
}

// Function to display the elapsed time on the displays
void displayElapsedTime(float time1, float time2) {
  // Display Lane 1 elapsed time
  displayLane1Start.clear();
  displayLane1Down.clear();
  displayLane1Start.displayText(String(time1, 3).c_str(), 0);  // Show elapsed time with 3 decimal places
  displayLane1Down.displayText(String(time1, 3).c_str(), 0);

  // Display Lane 2 elapsed time
  displayLane2Start.clear();
  displayLane2Down.clear();
  displayLane2Start.displayText(String(time2, 3).c_str(), 0);
  displayLane2Down.displayText(String(time2, 3).c_str(), 0);

  // Report the elapsed times to Serial1 for verification
  Serial1.print("Lane 1 Elapsed Time: ");
  Serial1.print(time1, 3);
  Serial1.println(" seconds");

  Serial1.print("Lane 2 Elapsed Time: ");
  Serial1.print(time2, 3);
  Serial1.println(" seconds");
}

// Function to display the speed on the displays
void displaySpeed(float speed1, float speed2) {
  // Display Lane 1 speed
  displayLane1Start.clear();
  displayLane1Down.clear();
  displayLane1Start.displayText(String(speed1, 1).c_str(), 0);  // Show speed with 1 decimal place
  displayLane1Down.displayText(String(speed1, 1).c_str(), 0);

  // Display Lane 2 speed
  displayLane2Start.clear();
  displayLane2Down.clear();
  displayLane2Start.displayText(String(speed2, 1).c_str(), 0);
  displayLane2Down.displayText(String(speed2, 1).c_str(), 0);

  // Report the speeds to Serial1 for verification
  Serial1.print("Lane 1 Speed: ");
  Serial1.print(speed1, 1);
  Serial1.println(" mph");

  Serial1.print("Lane 2 Speed: ");
  Serial1.print(speed2, 1);
  Serial1.println(" mph");
}

// Function to reset the track, displays, and variables
void resetTrack() {
  // Reset track-related variables
  guardToFinishTimeLane1 = 0;
  guardToFinishTimeLane2 = 0;
  speedLane1 = 0;
  speedLane2 = 0;

  // Clear the displays
  resetDisplays();

  Serial1.println("Track reset complete.");
}

// Function to clear the alphanumeric displays
void resetDisplays() {
  // Clear Lane 1 displays
  displayLane1Start.clear();
  displayLane1Down.clear();
  displayLane1Start.displayText("----", 0);
  displayLane1Down.displayText("----", 0);

  // Clear Lane 2 displays
  displayLane2Start.clear();
  displayLane2Down.clear();
  displayLane2Start.displayText("----", 0);
  displayLane2Down.displayText("----", 0);
}

 

 

 

 Again the concept is to add a end of track display with forward and aft displays to Bill V's RC car timer (check my first post for details). Also added a win light via a standard LED. The above is only the changes to add the display and I have got some work to do to integrate it into the original code. One thing to note is the original code was designed for a 132 foot track and this code is adjusted for a 35 foot track. The speed trap sensor is now moved to 1.4 foot from the finish. The half track sensor is 17.5 (was 66 foot) from the guard sensor. Finally the 6 foot sensor is 1.59  foot from the guard senor. Also as expected it did not verify as is.
 
 
 

   
ReplyQuote
stevenr8062
(@stevenr8062)
Member
Joined: 3 years ago
Posts: 22
Topic starter  

Ok, perhaps a little review is in order. when first started this project it had been a couple of years since the first version of our track. Our needs and the way we use the have changed greatly since then, we do more of drag racing style race event, rather than a Boy Scout style were everybody races everybody. It was also the time since I had worked with Arduino programming at all. I began with the RC car timer that is Arduino Mega based and communicates with a PC via Processing 3, developed by Bill V. at Minimal Hardware Designs. It has all the bells and whistles we wish to have, except one an end of track display (see my first post). 

https://billvsderbytimers.weebly.com/minimal-hw-designs.html

 

The thought was it would easy enough with I2C displays and a couple LED's for lane win lights. It turns out that wiring it is the easy part, but the code is much involve than could have imaged. Unfortunately for me the code is my weak link. I happy to report that with the help of friends and the use of Chat GPT, I am improving my coding skills. My advice here is, if truly wish to improve your coding skills, do not use these kind of assets as crutch and slow down take the time to learn what is before you.

I had not worked with but a couple of very simple I2C projects and now have a slightly better understanding on how to implement them. First problem was was that when adding changes the reports to the serial monitor conflicted with the reports to Processing 3. Second was a bit confused on how the Mega, and other similar boards handle I2C. Admittedly, I am still working on that. I am sure there is a way to avoid these conflicts, but for now the solution for was to change boards and use the Arduino Due. Going this route allows Processing 3 to report to Serial and I2C to report to Serial1.

The displays I have use used are from Spark Fun and can found here:

https://www.sparkfun.com/products/16916

This created the next issue. The library examples provided are a bit vague. When trying to verify the code using:

SFE_Alphanumeric display;

or in my case:

SFE_Alphanumeric displayLane1Start;
SFE_Alphanumeric displayLane1Down;
SFE_Alphanumeric displayLane2Start;
SFE_Alphanumeric displayLane2Down;

 

IDE reported class errors.

The fix was:

HT16K33 displayLane1Start;
HT16K33 displayLane1Down;
HT16K33 displayLane2Start;
HT16K33 displayLane2Down;

The next issue was to deal with scaling down the track size. The original code is designed for a 132 foot track. The new track when it arrives will be 35 foot. Speed is measured from the trap sensors to the finish line sensors. A change in the way speed calculated is needed.To adjust the speed calculation for a different spacing between the trap and finish line sensors, you need to modify the equation used to calculate speed based on the new distance.

Current Formula:

The current formula is:

S=3600Finish Time−Trap TimeS = \frac{3600}{\text{Finish Time} - \text{Trap Time}}S=Finish Time−Trap Time3600​

This formula assumes the distance between the trap and finish line sensors is 5.28 feet, simplifying the calculations to convert the time difference directly into miles per hour.

Adjusting for a New Distance:

If you move the trap closer to the finish line, say to a new distance DDD feet, you'll need to update the formula to account for this change:

  1. New Formula:

The speed SSS in miles per hour can be calculated as:

S=D×3600Finish Time−Trap TimeS = \frac{D \times 3600}{\text{Finish Time} - \text{Trap Time}}S=Finish Time−Trap TimeD×3600​

Here, DDD is the new distance in feet between the trap and the finish line.

  1. Example Calculation:
    • Let’s say you move the trap 3 feet away from the finish line:

S=3×3600Finish Time−Trap TimeS = \frac{3 \times 3600}{\text{Finish Time} - \text{Trap Time}}S=Finish Time−Trap Time3×3600​

In the end the spacing will be:

Updated Sensor Positions:

  • 6-Foot Sensor: Adjusted to 1.59 feet from the start line.
  • 66 Ft Sensor: This will be modified to a position that is halfway on the new 35-foot track, i.e., 17.5 feet.
  • The trap sensor: The trap sensor's primary role is to calculate the speed of the car over a known distance. The original code calculates speed over the 5.28-foot segment before the finish line; this needs to be updated to reflect the 1.4-foot segment. 

The exact calculated positions are given above, your mileage my vary.

As this post is getting a bit long I will end it here. As the code is a bit please se the attachments.

 The first is all the changes to add the display and second is that file blended with with the original RC timer file. The blended file did verify, but is yet untested.


   
ReplyQuote