Yacht races generally use flag signals and have a set routine for the starting sequence:
- Postponement signal - only used if the start is running late. The postponement flag, if used, is removed at T0 minus 6 minutes
- Class warning flag displayed - at T0 minus 5 minutes. Often 2 classes start together, so two flags are raised
- Preparatory signal flag displayed - at T0 minus 4 minutes
- Preparatory signal removed - at T0 minus 1 minute
- Start - class flag is removed at time T0
- Change seamlessly from count-down mode to count-up mode
Many attempts have been made by others to automate such a system, and if the resulting devices are easy to use, and reliable, they would be a great help to Race Officers. However, they can be expensive, and races tend to be run using wrist-watches, and writing down the finish times (usually on soggy paper).
Unless there are two or three people involved, it's impossible to raise and lower flags to the nearest second, sound a horn and record boat identities and finishing times, all at the same time. A smart octopus would be useful.
In the absence of an intelligent octopus, any automated gadget needs to be easy to use, and cheap (don't forget - it could conceivably be dropped overboard!). Remember all this is happening on board the Committee Boat (a motor boat which has a 12 V DC supply available).
So here's yet another attempt to make a gadget for this task - while trying to keep it simple - the YRT (yacht race timer). The Arduino can be a good timing device - there are no other programs running to cause interrupt problems - and it lends itself to driving other devices including lights, relays, horns etc. I did a quick check on the Arduino's timing accuracy, and found it to be 0.13 per cent fast. This is acceptable for the purpose of recording times of up to a couple of hours (just a few seconds out), as the yacht racing times are all relative to one another.
I got myself an LCD screen (£5.89, delivered, from Amazon), which is the 20 column x 4 row version of the Hitachi HD44780 compatible system, and which I thought would be more useful for this task than the 16 column x 2 row version. The characters are represented by 5 pixel x 8 pixel images. There are different sizes of these displays available.
Here's the circuit (this one shows the 16 x 2 character display, but the connections to the 20 x 4 display are the same). Note that not all 16 pins of the LCD display are required:
Arduino Pin 6 is pulled down to GND with a 10 kΩ resistor, and sent "High" when the breadboard button is pressed. The 10 kΩ trimmer is used to adjust the brightness of the LCD screen's backlight, which requires its own power supply.
Here's a video of my work in progress, with ideas as usual pinched from lots of other sources which I have tried to reference here. At the beginning of the video I use the Arduino's reset button to start the procedure from the beginning, and later the time-capturing button on the breadboard. Please note that the timing sequence has been speeded up by a factor of 100 to prevent boredom setting in, so you have to concentrate to see what's going on:
In line 69 of the code below, the number of count-down (negative value) seconds is set, and if that time exceeds 6 minutes, the first signal it gives is the 6 minute, followed by the 5 minute, 4 minute, one minute and then the T0 signal.
I thought it would be nice (ie useful) to have a visual indication of the state of the major flags, so I made some custom characters to represent:
These characters appear on the top left of the LCD display at the appropriate time.
Making custom characters was enabled using the extremely useful page at http://omerk.github.io/lcdchargen/ which generates the code for any symbols you can dream up within 5 x 8 pixels. Buzzers or relays, LEDs and lights can easily be added, but haven't yet been included in this version.
The time in the video is running so fast, 100 times normal, that it's not easy to see what's going on, but the postponement flag coming down is the first. If there is no postponement, then the first signal would be the 5-minute one.
The symbols eventually line up as but without the first one, as the postponement flag down symbol is intentionally written over by the next symbol (2 class flags up). This allows the Race Officer to visually check that the critical flags he has been flying, are down after the start of the race.
The main timer on the right of the LCD screen shows hours, minutes and seconds from T0. This is given as a negative time if it's before T0. As the time passes through the critical 6, 5, 4, 1 and 0 minutes, the above characters are displayed to remind the Race Officer that he should have already carried out those operations. These symbols could of course be displayed in advance with an audible warning to give him prior notice to raise or lower the flags.
Then comes the push button (on the breadboard). When the button is pressed, as the first boat crosses the start/finish line, the time (from T0) at which it was pressed is displayed on the 3rd row (row 2) starting at position zero (first column). This indicates the boat's finish time. The next finish time is recorded on the fourth row, below. Subsequent presses of the button over-write all previous ones on the fourth row, leaving the first finishing time on the 3rd row still visible.
This gives the Race Officer time to write down the times (or they could easily be stored in an array in the Atmega 328 chip's memory). The first finishing time is a useful one to have continuously displayed, as other rules of sailing come into play if any of the other boats finish out of time (eg more than 30 minutes after the first finisher). The Racing Rules of Sailing are more complicated than Arduino programming!
Here is the code as it currently stands:
1: /*
2: LiquidCrystal Arduino Library - Timing
3:
4: Demonstrates the use a 20 column x 4 row LCD display. The LiquidCrystal
5: library works with all LCD displays that are compatible with the
6: Hitachi HD44780 driver.
7:
8: This sketch prints count-down / count-up times to the LCD - and more!
9:
10: The circuit:
11: * LCD RS pin to digital pin 12
12: * LCD Enable pin to digital pin 11
13: * LCD D4 pin to digital pin 5
14: * LCD D5 pin to digital pin 4
15: * LCD D6 pin to digital pin 3
16: * LCD D7 pin to digital pin 2
17: * LCD R/W pin to ground
18: * Variable (eg 10K) resistor:
19: * ends to +5V and ground
20: * wiper to LCD VO pin (pin 3)
21:
22: Library originally added 18 Apr 2008 by David A. Mellis
23: library modified 5 Jul 2009 by Limor Fried (http://www.ladyada.net)
24: example added 9 Jul 2009 by Tom Igoe
25: modified 22 Nov 2010 by Tom Igoe
26: modified October 2013 by KC
27:
28: http://www.arduino.cc/en/Tutorial/LiquidCrystal
29: */
30: // include the library code:
31: #include <LiquidCrystal.h>
32:
33: // initialize the library with the numbers of the interface pins
34: LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
35:
36: // array of bits defining pixels for 8 custom characters
37: // pixel=1 : on and pixel=0 : off
38: // see Custom Character Generator at http://omerk.github.io/lcdchargen/
39: byte oneflagdown[8] =
40: {0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01100, 0b01100}; // 1 flag down
41: byte twoflagsup[8] =
42: {0b11011, 0b11011, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010}; // 2 flags up
43: byte oneflagup[8] =
44: {0b01100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100, 0b00100}; // 1 flag up
45: byte twoflagsdown[8] =
46: {0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b01010, 0b11011, 0b11011}; // 2 flags down
47: // also see Arduino Cookbook 2nd ed by Michael Margolis
48: int inPin = 6;
49: int outPin = 13; // the number of the output pin (LED)
50: int reading;
51: long debounce = 200; // debounce delay in milliseconds
52: int i = 2; // for lots more on button bounce, see http://arduino.cc/en/Tutorial/Debounce
53:
54: void setup() {
55: // set up the LCD's number of columns and rows:
56: lcd.begin(20, 4); // 20 columns & 4 rows
57: lcd.createChar(1, oneflagdown); // create first custom character
58: lcd.createChar(2, twoflagsup); // create second custom character
59: lcd.createChar(3, oneflagup); // create third custom character
60: lcd.createChar(4, twoflagsdown); // create fourth custom character
61: lcd.clear();
62: // Print the header on the LCD
63: lcd.print(" YRT h: m: s");// for "Yacht Race Timer"
64: pinMode(inPin, INPUT);
65: }
66:
67: void loop() {
68: int seconds;
69: int time = -405; // countdown seconds before time zero
70: // print the time since reset (at 'int time' seconds):
71: int sec0 = millis()/1000;
72: int secs = sec0 + time;
73: lcd.setCursor(0, 0);
74: if (secs == -360)
75: lcd.write(1); // display first character at 6 minutes (1 flag down)
76: if (secs == -300)
77: lcd.write(2); // display second character at 5 minutes (2 flags up)
78: lcd.setCursor(1, 0);
79: if (secs == -240)
80: lcd.write(3); // display third character (1 flag up)
81: lcd.setCursor(2, 0);
82: if (secs == -60)
83: lcd.write(1); // display first character (1 flag down)
84: lcd.setCursor(3, 0);
85: if (secs == 0)
86: lcd.write(4); // display fourth character (2 flags down)
87: clockDisplay(12, 1, secs);// display the running time from time zero
88:
89: reading = digitalRead(inPin);
90: if (reading == HIGH) // if the button is pressed
91: {
92: delay(debounce); // in case the button bounces
93: clockDisplay(0, i, secs);
94: i++; // move on to next line for next time
95: }
96: }
97:
98: void clockDisplay(int x, int y, int s){
99: // display hours, mins, secs from time zero, at required position
100: // x can be 0 to 12, (positions across LCD screen), y can be 0 to 3 (lines down screen)
101: int mins = s/60;
102: int hrs = mins/60;
103: int secs = s - (mins*60);
104: mins = mins - (hrs*60);
105: lcd.setCursor(x,y);
106: if (s > 0)
107: lcd.print("+");
108: else if (s < 0)
109: lcd.print ("-");
110: else
111: lcd.print (" "); // could sound a buzzer or flash a light .. THEY'RE OFF!!
112: lcd.setCursor(x+1, y);
113: lcd.print(abs(hrs));
114: lcd.print (":"); // trailing colon
115: lcd.setCursor(x+1, y);
116: lcd.print(abs(hrs));
117: lcd.print (":"); // trailing colon
118: lcd.setCursor(x+3, y);
119: if (abs(mins)<10) // include a leading zero
120: lcd.print("0" + String(abs(mins)));
121: else
122: lcd.print(abs(mins));
123: lcd.print (":"); // trailing colon
124: lcd.setCursor(x+6, y);
125: if (abs(secs)<10) // include a leading zero
126: lcd.print("0" + String(abs(secs)));
127: else
128: lcd.print(abs(secs));
129: }
130:
I have mentioned some suggestions above which I will probably continue to develop.
Things to further think about include:
- the power supply - direct from the Committee Boat's 12V supply, or connected in parallel with a set of re-chargeables which can re-charge while powering the YRT. The recommended input voltage to the Arduino is 7 V - 12 V, with input voltage limits of 6 V to 20 V, so to be safe, voltage regulation would be advisable.
- encasing the unit if it ever reaches fruition (including waterproofing).
- further button control to enter the count-down time, re-display (and even download) the array of finishing times.
- add a real-time clock (RTC) with its own coin battery, so that the device can actually display the correct time of day, even if it has been switched off and on again since the last time it was used. The RTC may provide a more accurate source of time (a possible factor of 10 in the accuracy) than the Arduino's millis() function (line 71 of the code, above).
Hi - great article. I'm putting something similar together myself, using a Uno, a 7 segment display http://www.dx.com/p/diy8-x-seven-segment-displays-module-for-arduino-595-driver-250813 using 3 arduino output pins. We run ECHO handicap races, so our race times are pretty important, I've added a toggle switch to display elapsed time as H:MM:SS or decimal minutes MMM.MMM which will save having to do the conversion later. I've used a couple of LEDs to display the state of our flags, red = class, blue = blue peter, with an LED flash & audible warning when it's time to raise/lower that flag. I've also hooked up a relay to connect to our 12v starting horns, and am thinking of expanding it to auto hoot every minute for up to 25 minutes for when we run pursuit races - all great stuff once you start to think about the expansion possibilities!
ReplyDelete