ClothesLine Racers Timer
Introduction
Make Magazine #28, page 66, contained an article about vehicles designed to move along a clothesline or a cable, called Clothesline Racers. This article explains how to construct an accurate timer for those racers. It can also be Used in any race where the vehicle passes by the same spot twice.
Introduction
When I presented the Make article on Clothesline Racers to my electronics students, they went wild. In a matter of two days the Sophmores and Seniors in my electronics classes formed fifteen teams of two or three students each to manufacture clothesline racers and compete with them at the end of the quarter. My first thought was that with fifteen to twenty teams competing, we needed a timer to accurately show the times for each racer. Since several of my classes center around the Arduino Development board (www.arduino.cc) I decided to use the Arduino to make a timer for these races. To provide redundancy and to make sure at least one timer was ready for the races, two other teams volunteered to design and construct timers. This is the story of my timer.
Overview
Displaying Elapsed Time with 7-Segment LEDs
I had previous experience with 7-segment LEDs while teaching digital electronics classes. I did not relish the thought of multiplexing five or six 7-segment LEDs. The design of hardware or software based multiplexing, and the wiring for all that seemed tedious and boring. I looked for a way to simplify this part of the project. Additionally, I wanted a project that could be assembled and programmed in one sitting. With the parts assembled and the source code typed into the Arduino IDE, this project should be up and running in an hour or two.
It only took five minutes and one Google search for "Arduino 7-segment shield" to find the Gravitech 7-segment LED shield for the Arduino Development Board. I could see from the description of this shield that purchasing this product would remove my concerns about wiring and programming multiple LEDs.
What Input Sensor???
Input Sensor Selection
Since the contest was to be held outside, I suspected that an ultrasonic sensor would be the best choice. Either I or my students had experienced trouble with sensors in the visible or infrared range in a bright sunny environment. I decided to experiment and test several options. It appeared to me that three good ways to sense the passing of a racing robot are:
- Use visible light. i.e. a beam of light or a laser shining across the path of the racer. The racer breaking the beam can be used to record the start and finish. I tried both a small laser and a super bright LED shining towards a phototransistor. With the proper voltage divider resistor, the laser provided the most dramatic voltage difference between seeing and not seeing the beam. The problem was that the laser beam, although the strongest source, also had to be the most precise. It was extremely difficult to keep the laser focused on the phototransistor. The least bit of vibration and the laser beam moved enough to provide a false signal.
- The super bright LED idea also worked well in the lab, but I confirmed that as soon as the rig was transferred into a Southern California bright sunny day, that there would be trouble. The difference in light detected from the LED seen vs. unseen became negligible.
- With the field narrowed, the ultrasonic sensor seemed to provide the best detection with the least interference from the environment. I had programmed and used the Ping)))™ Acoustic sensor from Parallax.com on several robots. It worked well with the Basic Stamp chips and I recently used it successfully with an Arduino project. Several of my students reported success and satisfaction with the LV-MaxSonar-EZ0 ultrasonic distance sensor, so I purchased one and did the testing to confirm that it is much easier to operate that the Ping))). The Ping))) requires a signal from the microcontroller to trigger the sonar transmission, while the MaxSonar only has one signal wire that sends a constant analog voltage indicating the distance. This simplifies the programming.
Hardware Required
The components needed for this project are:
| DESCRIPTION | PHOTO | SOURCE | COST |
| Arduino Uno Development Board | |
Arduino.cc | $39.00 |
| Gravitech 7-Segment Shield |
|
Gravitech.com | $49.99 |
| MB1000 LV-MaxSonar®-EZ0™ |
|
http://www.maxbotix.com/products/MB1000.htm Get the data sheet here. | $29.95 |
| 9 Volt Battery | |
Drug, hardware or electronics store | $2.00 to $4.00 |
| Misc. Parts: 90 degree angle bracket |
![]() |
Any hardware store | Varies - $2.00 to $3.00 |
| Misc. Parts:
small nuts and bolts Spacers or standoffs velcro or double sided sticky tape |
No photo | Any hardware store | Varies - $1.00 to $4.00 |
Assembly of Hardware
Wiring and Hookup
Once the parts are in hand, assembly is straightforward.

The colors mentioned here match the ones in the photo above. You may use other colors.
- Fit the LED shield on to the Arduino Uno.
- Run a RED wire from the positive terminal of a nine volt battery to the Vin connection on the LED shield.
- Run a BLACK wire from the negative terminal of the nine volt battery to a ground connection on the LED shield.
- Solder a RED wire to the sonar transducer terminal labeled +5 and then run that to the power, 5V Pin, on the Uno.
- Solder a BLACK wire to the sonar transducer Ground terminal labeled GND. Run the other end to a Ground connector on the Uno.
- Solder a GREEN wire to the analog output, labeled AN on the sonar transducer, and run the other end to the Uno Analog input 0 Pin (A0).
Mechanical Considerations
There are a number of ways to arrange the mechanics for this type of device.
I decided I wanted to be able to see the LED readout from 'behind' and have the acoustic sensor pointing 'forward'. I chose to get a 90 degree angle bracket and mount the Uno Board and the LED shield to one side of it and mount the battery and the sonar transducer on the other as in these photos.

This photo shows how the timer is mounted on a standard tripod. This is a 45 degree view, similar to what the racer would see when approaching the timer.

This would be the view from the racer just as it passes by the timer. Behind the 9 volt battery on the left and the sonar transducer on the right, you can see the silver of the 90 degree angle bracket. Behind the angle bracket you can barely see the top of the Arduino Uno Board which is mounted to the back side of the bracket.

This is the view of the operator from the other side. The white circuit board is the Gravitech 7-segment shield. Behind that shield, you can just see the right edge of the Arduino Uno Board. The Gravitech shield is mounted directly on the Uno Board. The Uno Board is stuck to the 90 degree angle bracket with velcro. The wires coming around the left side of the board are for 5 volts sonar power (Yellow), sonar ground (Black) and sonar analog signal (Green). The wires coming around the right side are the leads for the 9 volt battery positive (Red) and negative (Black and hard to see) terminals. The timer shows exactly two seconds and the RGB LED on the left is off.

This side view lets you see the mounting of the components. Everything is connected to the 90 degree angle bracket. You can see the silver of the bottom of the bracket under the 9 volt battery. The 90 degree section of the bracket is vertical, Just behind (to the left) of the battery. The 9 volt battery mounts in a standard battery clip that is bolted to the bracket. Not visible, behind the battery, the sonar transducer is mounted directly to the bracket. Vertically, on the far left is the Gravitech 7-segment shield which is mounted to the Uno board, the second vertical circuit board from the left. The bottom of the bracket was drilled and a 1/4 inch bolt used to attach the entire assembly to the standard camera mounting screw on the tripod.
Software Functional Description
The computer program, called a sketch by Arduino, has three main tasks:
- Recognize and record the start of a race.
- Record and display the elapsed time, and
- Record the end of the race and display the elapsed time.
The next two sections provide a summary of the action and then a detailed description of the software.
Initialization
The setup() function does the following:
- Turn RGB-LED red
- Initialize pins for RGB LED on 7-segment display shield
- Join I2C bus (to communicate with four 7-segment LEDs)
- Test 7-segment LEDs
- Turn RGB-LED blue
- Read sonar sensor until racer detected, then
- Turn RGB-LED green
- Get present time
- Go to loop() function
Main Loop
The following sequence is repeated until the race is over:
- What is the time now in milliseconds?
- Compute elapsed time (elapsed time = time_now minus start_time)
- Display the elapsed time
- Average ten distance readings
- If racer detected AND at least 3 seconds have elapsed, go to raceOver(), otherwise go back to step 1 above.
The Software
Here is a detailed description of the software. The code shown here has no comments to keep your view uncluttered. The code that you can download is littered with comments.
#includes, #defines Function Prototypes and Global Variables
The 7-Segment LED shield uses I2C protocol to speak to the Arduino microcontroller. Line 1 provides the header file for the I2C communication routines and Line 3 sets up hex address 0x38 to communicate between the Arduino Uno and the 7-segment shield.
Even the best of sensors gives an occasional false reading. The MB1000 Sonar returns very few false readings, but even one reading making it appear that the racer is in front of the sensor will cause a false start. To eliminate this problem, the average of a number of readings is taken. That way, one bad reading gets thrown into the average and that one bad reading doesn't trigger a false start. I experimented with several averages. Averaging three readings didn't work. There weren't enough readings to prevent the one bad (low) reading from making it appear that the racer was nearby. Twenty readings takes too long, so I found ten readings to work well and produce no false starts. Line 4 uses MAX to set the number of readings to be averaged. You can change this if you wish.
Lines 6 through 10 are the function prototypes. Each function is explained below. The functions send7SEG() and updateRGB() were taken from the temperature sensing code provided by Gravitech.com. The others were my creation.
Line 12 sets analog pin 0 as the input pin for the MB1000 Sonar.
Lines 13 and 14 declare some integers used in the program.
Line 18 sets up a lookup table to send the correct binary pattern for each of the numbers to the 7-segment display. The first ten numbers represent the digits zero through 9; the next six, the hexadecimal values A through F (not used in this program), and I added 0x00 on the end to blank out an LED.
Line 22 declares the unsigned long integers. The Arduino command 'millis()' returns the milliseconds since the program started and is stored as an unsigned long. When the race starts, the millis at the start are stored in 'start'. As the race proceeds, the program stores the present millis in 'now'. Subtracting start from now and storing the result in 'left' puts the race elapsed time in milliseconds into 'left'.
setup() Function
Arduino uses setup() to perform any tasks that need to be performed before loop() begins. Lines 26 through 28 set the pins 3, 5 and 6 as the RED, GREEN and BLUE pins on the 7-segment shield board. There is no wiring for this. When the 7-segment shield is plugged into the Arduino Uno, these pins are connected.
Line 29 turns the RGB LED to RED. This tells the operator that the processor is not ready, and not to start a race yet.
Lines 30-34 send the 7-segment shield initialization parameters to the 7-segment board. Each bit in the 'B01000111' in line 33 is explained in the datasheet for the 7-segmant hardware and also at the bottom of the source code you can download.
Lines 35-36 blank all four LEDs.
Lines 37-40 send a count down from 9 to 0 on the right-hand LED. This does nothing except look pretty and let observers see something happening.
Lines 41-42 sends zeros to all four LEDs.
Line 43 turns the LED BLUE, telling the operator it is OK to start the racer.
Lines 44-49 take continuous sets of ten distance readings and produces the average. Typical readings with nothing in front of the sensor were in the 140-160 range. (All raw data is used in this program. Readings are never converted to inches or centimeters.) A 'BAD' reading was usually in the 0-10 range, and the actual presence of the racer typically produced readings from 30 to 40. All of these numbers were based on experimentation with the actual readings in this event. You may need to adjust some of these numbers for your application. This loop goes on forever, or until the average drops below 56, which indicates the racer has just been detected. Once detected, the loop is exited.
Line 50 turns the LED GREEN indicating the race has started.
Line 51 records the start time. All future time readings have the start time subtracted from them to provide elapsed time.
loop() Function
loop() is the main part of the program. Its two main tasks are to record the elapsed time and to continually look for the return of the racer.
When the racer first starts, there will be a number of 'hits' or reports that the racer is close. Since racers are often suspended on two wheels, the timer could report the first wheel as the 'start' then think the second wheel passing by is the racer coming back or finishing. Line 56 sets up a delay of 3000 milliseconds (3 seconds) so that for three seconds after initial racer detection, all sonar readings are ignored.
Line 57 records the present time and stores it in now.
Line 58 does the subtraction to get the elapsed time in milliseconds.
Line 59 sends the elapsed time off to the showNumber() function which displays the elapsed time on the 7-segment LEDs.
Line 60 gets the average of ten sonar readings.
Line 61 asks two true/False questions: First, 'Is there a racer nearby?' and second, 'Has it been at least three seconds since the start?' If the answer to both of these questions is true, it means the racer has returned, the race is over, and program control is transferred to the raceOver() routine, otherwise we go round the loop() again.
getAverage() Function
Lines 65-74 take ten sonar readings (MAX is set to 10 in the globals at the top of the program, Line 4). The average is computed and returned to loop().
raceOver() Function
raceOver() is an intentional infinite loop. It continues to run until the board is reset. It takes no more time readings, therefore locking the time of the finish on the LEDs, and it flashes the LED as BLUE, telling the operator to record the race time and then reset the board
send7SEG() Function
send7SEG needs to know two things: Which LED is to be illuminated? and What digit do you wish to display? It then sends that digit to the appropriate LED.
showNumber() Function
This function decides how to display the elapsed time. I really wanted five or six digits for the display so I could display minutes, seconds and milliseconds, but with only four digits, that wouldn't work. Since some races might take more that one minute, I need to reserve at least one digit for seconds.
To maximize the information available with just four digits, I
decided on the following time format:
Key: M = Minutes, T = Tens of seconds, U = Units of seconds, t = tenths
of seconds, H = Hundredths of seconds
For times from zero to 59.99 seconds use the four digits like this:
TU.tH
00.01 (One one-hundredth of a second)
00.02 Two one-Hundredths of a second
...
...
59.58 (Fifty nine point 58 seconds)
59.59 (Fifty nine point 59 seconds)
...and for one minute or more, use two decimal points...
M.TU.t
1.23.4 (One minute 23 point 4 seconds)
5.43.2 (5 minutes, 43 point 2 seconds)
The variable 'whichFormat' tells which of the two formats above is used
for the time display. If whichFormat is zero, time is less than one
minute and the first format is used. If whichFormat is 1, the time is
one minute or more and the second format is used.
Lines 99-100 terminate the race at ten minutes. None of the races in this series can be that long.
If elapsed time is greater that 60000 milliseconds (one minute), lines 101-109 pick out the first digit. Line 105 adds a decimal point to this digit. OR-ing the displayed number with B10000000 sets the most significant bit to one which is the bit that lights up the decimal point.
Lines 97 and 197: The variable 'location' is which LED is to be illuminated. 4 is the left-hand LED and 1 is the right-hand LED. Line 197 shifts the 'to-be-displayed LED from 4 to 3. 'location--' is used to shift the display LED one position to the right.
Line 106 sends the location and digit to the display.
Line 108 locks in the time format to M.TU.H as shown above.
Lines 110-122 keep picking off the left-hand digit of the elapsed time, sending that digit to the display and subtracting the amount sent from the total time. Lines 110-112 extract the tens-of-seconds digit. Lines 114-118 extract the units-of-seconds digit, and adds the decimal point. Lines 119-122 extract the tenths-of-seconds digit.
Lines 123-128, which extract the hundredths-of-seconds digit, only apply if the displayed time is less that one minute.
updateRGB() Function
Sensd updateRGB a zero and the RGB LED will go dark. I only use the primary colors in this program.
Click here to download the source code. (timer.pde)Ta-Daaaa!
That's all. If you have any comments or suggestions, contact me at RoboticsProfessor@GMail.com
I hope you enjoyed reading about this project. If you build it or something like it, please let me know!
