Introduction: Barista Championship Brewing Stopwatch • Introduction

Upon the needs of the (2014) World Barista Championship Rules and Regulations, an adequate and precise time measuring is needed during the competition. These measurements are usually done by regular stopwatches with all the benefits (ease of use, common availability) and drawbacks (two stopwatches needed for each judge, as well as some mathematics are necessary for supervision).

This Arduino-based stopwatch originally designed to meet all the requirements of this situation, finally to make judges' work seriously simpler, yet at least as acceptalbe as the good old way. My goal were to meet the requirements of Rules and Reglations 2.2.1.H (verbosely defined under 13.3.7.). In short: during the competiton, the extraction times within each beverage category should be within 3 second variance. For example, if the competitor extracts a set of espresso drinks (four servings in two two-servings brewing), de difference between each brewing time should be less than 3 second. If first brewing is 29.1 second, second brewing should be between 26.1 and 32.1 second (excluding both 26.1 and 32.1), otherwise the competitor will get a penalty.

Benefits:

  • one watch instead of two,
  • designed and built to meet WBC/WCE requirements to help judges,
  • can be upgraded, changed due to the changes of Rules and Regulations and/or different type of competitions,
  • make all the measurements and analysis,
  • it's hard to be hacked by the judge to make unwanted and unfair handicaps to certain competitors (see 15.0 in Rules and Regulations),
  • easy to use,
  • planned to be upgradable to on-line/off-line logging and other features,

Drawbacks:

  • not official,
  • still a prototype.

Step 1: Main Component Descriptions

This stopwatch has four main components:

  1. two independent 3 + 1 digit 7 segment displays,
  2. one keyboard with three keys to control,
  3. a 16×2 digit alphanumeric display,
  4. an Arduino board (now a Nano 328 v3, some other models are also accepted and may be working properly).

1. Main displays

Both have a very simple goal: to show measured time. To avoid unfair/dishonest behaviour of judges, actual (counted) time is not displayed, instead a mark shows that the measurement is running. So the judge has no any information about the elapsed time, so unable to stop the measurement within the 3 second barrier, whatever the competitor stopped the brewing or not. After the end of the first measurement, the measured time will shown on the display in 1/10 second precision, and this time will remain on the display until the next measurement has finished (on the same stopwatch). Most of the competitions are used to have a two grouphead espresso machine, where the competitors should use two groupheads to produce a set of drinks. Brewing time on each head should be measured independently, so two independent stopwatch and display needed.

2. Keyboard

This simple keyboard have two goals: to measure time (start/stop and reset the stopwatches) and control the menu of the stopwatch.

Each stopwatch has its own start/stop button. After initialization/reset, the first push of the start/stop button will start the measurement, second push will stop. After all six measurements (two measurements of three groups), the push of the third button will reset the values.

3. 16×2 digit alphanumeric display

There are a lot of information are on the display, as well as some future features will use it. Detailed information about the display will discussed later.

4. Arduino board

With some changes, almost any Arduino-compatilbe board can be used, except Esplora, Due and Leonardo. Some future software development may cause the exceeding of available memory, so "the bigger is better" rule may be true here.

Other components will be listed on the next page.

Step 2: Component List

  • Arduino compatible board (except Leonardo [no SPI], Due [3.3 V], Esplora/Robot/Lillypads [all designed for different purposes], I suggest to use AtMega328 based boards like Nano rev 3.0 or Mini which has full SPI compatibility.
  • 2+1 pcs breadboards (2 pcs 820 hole, and one 170 hole mini breadboard)
  • tons of jumper wires, both rigid (34 pcs) and flexible (30 pcs),
  • 2 pcs 4 digit 7 segment LED displays or 8 pcs 1 digit 7 segment LED displays (common cathode type)
  • 1 pc Maxim MAX7221 or MAX 7219 LED display driver (shift register)
  • 1 pc 16×2 digit LCD display (Hitachi 44780 driven)
  • 1 pc 10k potentiometer (linear)
  • 3 pcs microswitches (pushbuttons)
  • 1 pc 0,1 mF 6.3V tantalum capacitor
  • 1 pc 10 mF 6.3V electrolytic capacitor
  • 1 pc 220 ohm resistor
  • 3 pcs 10k resistors
  • 1 pc 33k resistor

Alternative LCD connection:

  • 1 pc BC337 NPN transistor
  • 1 pc 270 ohm resistor
  • 1 pc1k resistor

Due to wide availability of components and geographical differences between prices, it’s almost impossible to guess the cost of this project. Mine is around or even under 30 USD with a lot of components found in cheap starter kits from SainSmart. Of course, original Arduino boards and higher quality displays may get the price higher, up to – a guess – 70 to 80 USD.

Step 3: Schematics, Wiring

Please see Fritzing file.

Note that almost all Arduino boards has a somewhat different pinouts for SPI connection (which is used by max72xx), as well as digital I/O and analogue input pinout. If you have any question or comment, please, feel free to write me.

In general, you’ll find some tips on pin numbering in Fritzing file (just go over a pin of an IC to show pin number).


Important notices

MAX7221 to Arduino

First of all, Maxim MAX7219 and MAX7221 has a different pinout. I've used 7221 for this project, but 7219 is still good. There is only a slight difference between these two shift registers which literally not affect the usage of the stopwatch. For wiring MAX72xx to Arduino, you have to use only three wires: SPI “MOSI”, “SCK” and “SS”. For Arduino Nano 328 and MAX 7221 the wiring should be:

  • Arduino Pin 10 to MAX7221 Pin 12 (SS / LOAD)
  • Arduino Pin 11 to MAX7221 Pin 1 (MOSI / DataIn)
  • Arduino Pin 13 to MAX7221 Pin 13 (SCK / CLK)

MAX7221 to LED displays

It is very important to use common cathode type displays. It's not mandatory to use 4 digit 7 segment displays (like 5641AS I’ve used). If you have common anode type display, a much complicated (yet seriously costy) wiring is needed, so I don’t recommend it.

Each display has at least 9 pins for each digit (common cathode pin is often doubled on 1 digit 7 segment displays). Each has a unique identifier from A to G, and one for decimal point (ofter marked as DP). As we use a shift register, the ‘A’ pins of each display should be connected to each other, as well as to pin ‘A’ of MAX7221 (it’s pin 14).

If you use single digit displays, a lot (literally 8×8 = 64) individual wire should be used, which is not the most space and money consuming solution. That's why I recommend to use 4 digit 7 segment displays (which requires only 2×8 = 16 wires for anodes). As a reference, here is the pinout for MAX7221.

  • A --> 14
  • B --> 16
  • C --> 20
  • D --> 23
  • E --> 21
  • F --> 15
  • G --> 17
  • DP --> 22

Finally, you should connect each cathode of each digit to the specified pin of MAX7221. At this point, first digit marked DIG0 on 7221. Here is the pinout for each digit (please note that all pinout diagrams starts with DIG0, which is the first digit):

  • 1. --> 2
  • 2. --> 11
  • 3. --> 6
  • 4. --> 7
  • 5. --> 3
  • 6. --> 10
  • 7. --> 5
  • 8. --> 8

Each LED cathode pin should be directly connected to MAX7221, and never should be connected to each other.

MAX7221 power and other connectors

Due to high frequency characteristics of almost every shift registers, it's recommended to put at least one capacitor to the power input of MAX7221. I’ve used a 10 mF and a 0.1 mF capacitor for this purpose and didn’t experienced any error in work. If you have, try other values. All capacitors should be as close to the input as possible.

It is possible the change the brightness of the LED displays within the arduino library of MAX72xx (LedControl.h), but it is necessary to properly drive each segment with a desired forward current. For further reference, please see MAX7221 datasheet here. For my displays, a <20 mA forward current and 1.5 forward voltage could be set with a 33k resistor connected to pin 18 of MAX7221 (R6 in Fritzing file). As almost anytime, some attention needed to protect your Arduino board and your MAX 7221 too. I do not recommend using any forward current over 20 mA.

LCD to Arduino

It's a very common connection. As I mentioned in Fritzing file note, this wiring should be changed due to different Arduino boards. At this case, you should change the LCD setup line in the Pre-Setup part of the sketch. This setting/wiring can be used for Nano328 compatible boards and common Hitachi 44780 driven 16×2 character alphanumeric displays.

It’s your decision to use a BC337 or similar NPN trasistor to drive LCD backlight. I’ve experienced that almost every common LCD can work properly with a simple direct drive through a 220 ohm resistor far under the 20 mA current limit of Arduino pin. For backlight setting, it's necessary to use a PWM pin for this purpose (see diagram in Fritzing file).

It’s rather common to get a blank LCD at first startup due to improper setting of contrast potentiometer (10k, marked as R1 in Fritzing). I suggest to rotate and try to find the best setting before change the wiring. Negative (white on black, white on blue – that I’ve used) type LCD displays are harder to setup and harder to read than positive types. By the way, usually seriously cheaper.

Step 4: Code

The following code should be copied and pasted into Arduino IDE, and then, if some necessary changes had been made, should be uploaded to the Arduino board.

For direct download as .ide file, please click this link.


// ==========
// World Barisa Championship brew time stopper
// coded and designed by Tamas Szekffy
// 2v0 as of 15. march 2014.
// For improvements, notes, hints and bug reports, please contact professzore@gmail.com
// Creative Commons Licence Attribution Non-Commercial Share Alike (CC-BY-NC-SA-4.0)
// ==========


// included Libraries

#include // Basic LCD library
#include // I2C Wiring library
#include // MAX7221 library

// ==========
// pre-Setup
// ==========

LiquidCrystal lcd(8, 9, 5, 4, 3, 2);      // LCD pinout
const byte dot = B10100101;
const byte full = B11111111;
const int LCDBK = 6;                      // LCD Backlight pin
const int ss1 = A3;                       // Start/Stop 1 pin
const int ss2 = A2;                       // Start/Stop 2 pin
const int rst = A1;                       // Reset pin
int bcklght = 200;                        // LCD Backlight value (0 = turn off backlight)

// Timer variables

boolean timer1 = false;                   // true, if timer1 is running
boolean timer2 = false;                   // true, if timer2 is running
boolean ssastate = false;                 // became true, if ss1 button pressed (used for debouncing)
boolean ssbstate = false;                 // became true, if ss2 button pressed (used for debouncing)
boolean rststate = false;                 // became true, if rst button pressed (used for debouncing)
boolean finish = false;                   // became true, as soon as all measurements finished
unsigned long atime;                      // temporary variable for first time, indicates the time spent since start
unsigned long btime;                      // temporary variable for second time, indicates the time spent since start
unsigned long astime;                     // variable for first time, indicates the time when stopper started
unsigned long bstime;                     // variable for second time, indicates the time when stopper started
unsigned long debounce;
int atimes[3];                            // arrays of variables to store first time measured, one after another
int btimes[3];                            // arrays of variables to store second time measured, one after another
int validtime = 3000;                     // maximum time allowed as difference between measured times to meet WBC rules (milliseconds, 3 sec = 3000)
unsigned long menudelay = 0;              // variable to measure the time how long "reset" pressed
boolean menustate = false;                // boolean variable to point out if the system is in MENU mode for validation time
boolean menustate2 = false;               // boolean variable to point out if the system is in MENU mode for backlight setting time
byte atimecount = 0;                      // variable to count which measurement (first, second or first) are 'in line' for first time
byte btimecount = 0;                      // variable to count which measurement (first, second or first) are 'in line' for second time

/*
 pin 11 is connected to the DataIn 
 pin 13 is connected to the CLK 
 pin 10 is connected to LOAD 
 We have only a single (1) MAX72XX.
 */
LedControl lc=LedControl(11,13,10,1);


// ===========
// Main Setup
// ===========

void setup() {
  Wire.begin();                   // Turn on I2C  
  analogWrite(LCDBK, bcklght);    // LCD backlight is ON
  lcd.begin(16, 2);               // set the size of the LCD (16 digits, 2 rows)
  lcd.clear();
  /*
   The MAX72XX is in power-saving mode on startup,
   we have to do a wakeup call
   */
  lc.shutdown(0,false);
  /* Set the brightness */
  lc.setIntensity(0,8);
  /* and clear the display */
  lc.clearDisplay(0);

  pinMode(ss1, INPUT);
  pinMode(ss2, INPUT);
  pinMode(rst, INPUT);
  
//  testrun();               // unmark '//' if you need the initial test of the system
//  delay(1);

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("WBC valid time");
  lcd.setCursor(0,1);
  lcd.print(validtime);
  lcd.print(" msec");
  delay(2000);
  lcd.clear();

  rstall();
}


// =============
// Main routine
// =============

void loop()
{
  if (digitalRead(ss1) == HIGH && ssastate == false && finish != true && debounce + 200 < millis()) {
    startstopa();
    debounce = millis();
  }
  if (digitalRead(ss1) == LOW && ssastate == true) {
    ssastate = false;
  }
  if (digitalRead(ss2) == HIGH && ssbstate == false && finish != true && debounce + 200 < millis()) {
    startstopb();
    debounce = millis();
  }
  if (digitalRead(ss2) == LOW && ssbstate == true) {
    ssbstate = false;
  }
  if (digitalRead(rst) == HIGH && rststate == false && finish == true && debounce + 200 < millis()) {
    rstall();
    debounce = millis();
    finish = false;
    menudelay = 0;
  }
  if (digitalRead(rst) == HIGH && rststate == false && finish == false && menudelay == 0)
  {
    menudelay = millis() + 3000;
    rststate = true;
  }
  if (digitalRead(rst) == HIGH && rststate == true && menudelay != 0 && menudelay < millis())
  {
    menustate = true;
    lcd.clear();
  }
  if (digitalRead(rst) == LOW && rststate == true) {
    rststate = false;
    menudelay = 0;
  }
  if (atimecount == btimecount && atimecount != 0) {
    validate();
  }
  if (atimecount == 3 && btimecount == 3) {
    finish = true;
  }
  
  // ============================
  // Time check value setup/menu
  // ============================
  
  while(menustate)
  {
    lc.clearDisplay(0);
    lcd.setCursor(0,0);
    lcd.print("WBC valid time");
    lcd.setCursor(0,1);
    lcd.print(validtime);
    lcd.print(" msec    ");
    if (digitalRead(ss1) == HIGH && ssastate == false && validtime > 0 && debounce + 200 < millis()) {
      ssastate = true;
      validtime -= 100;
      debounce = millis();
    }
    if (digitalRead(ss1) == LOW && ssastate == true) {
      ssastate = false;
    }
    if (digitalRead(ss2) == HIGH && ssbstate == false && validtime < 32700 && debounce + 200 < millis()) {
      ssbstate = true;
      validtime += 100;
      debounce = millis();
    }
    if (digitalRead(ss2) == LOW && ssbstate == true) {
      ssbstate = false;
    }

    if (digitalRead(rst) == LOW && rststate == true) {
      rststate = false;
      menudelay = 0;
    }
    if (digitalRead(rst) == HIGH && rststate == false && menudelay == 0)
    {
      menustate = false;
      menustate2 = true;
      rststate = true;
      menudelay = millis() + 200;
    }
  }
  // END of submenu1
  
  // =====================
  // Backlight setup menu
  // =====================

  while(menustate2)
  {
    lc.clearDisplay(0);
    lcd.setCursor(0,0);
    lcd.print("Backlight value");
    lcd.setCursor(0,1);
    lcd.print(bcklght);
    lcd.print("       ");
    if (digitalRead(ss1) == HIGH && ssastate == false && bcklght > 0 && debounce + 200 < millis()) {
      ssastate = true;
      bcklght -= 10;
      debounce = millis();
    }
    if (digitalRead(ss1) == LOW && ssastate == true) {
      ssastate = false;
    }
    if (digitalRead(ss2) == HIGH && ssbstate == false && bcklght < 250 && debounce + 200 < millis()) {
      ssbstate = true;
      bcklght += 10;
      debounce = millis();
    }
    if (digitalRead(ss2) == LOW && ssbstate == true) {
      ssbstate = false;
    }

    if (digitalRead(rst) == LOW && rststate == true) {
      rststate = false;
      menudelay = 0;
    }
    if (digitalRead(rst) == HIGH && rststate == false && menudelay > millis())
    {
      menustate2 = false;
      rststate = true;
      delay(1);
      rstall();
      delay(1);
    }
  analogWrite(LCDBK, bcklght);
  }
  // END of submenu2
  
  delay(1);
}

// ======================== // Start/stop of stopper 1 // ======================== void startstopa() { ssastate = true; // the button has pressed if (timer1 == false) // if the timer is NOT running... { astime = millis(); // record start time timer1 = true; // indicate the start of the timer lcd.setCursor(2,0); lcd.print("RUN"); lc.setChar(0,3,'-',false); } else // if the timer is running... { timer1 = false; // indicate the stop of the timer atime = millis() - astime; // calculates the time spent since start lcd.setCursor(2,0); lcd.print("..."); lc.setChar(0,3,' ',false); atime = atime/100; atime = atime * 100; atimes[atimecount] = atime; write7Segment(atimes[atimecount], 0); if (atimecount < 3) { lcd.setCursor(6+(atimecount*2),0); lcd.write(full); atimecount ++; } } } // ======================== // Start/stop of stopper 2 // ======================== void startstopb() { ssbstate = true; // the button has pressed if (timer2 == false) // if the timer is NOT running... { bstime = millis(); // record start time timer2 = true; // indicate the start of the timer lcd.setCursor(2,1); lcd.print("RUN"); lc.setChar(0,7,'-',false); } else // if the timer is running... { timer2 = false; // indicate the stop of the timer btime = millis() - bstime; // calculates the time spent since start lcd.setCursor(2,1); lcd.print("..."); lc.setChar(0,7,' ',false); btime = btime/100; btime = btime * 100; btimes[btimecount] = btime; write7Segment(btimes[btimecount], 4); if (btimecount < 3) { lcd.setCursor(6+(btimecount*2),1); lcd.write(full); btimecount ++; } } } // =================== // Write time on 7221 // =================== void write7Segment(unsigned long v, byte i) { byte ones; byte tens; byte fractions; v = v/100; fractions = v%10; v = v/10; ones = v%10; v = v/10; tens = v%10; lc.setDigit(0,i,tens,false); lc.setDigit(0,i+1,ones,true); lc.setDigit(0,i+2,fractions,false); } // ================================ // Validate each measurement-pairs // ================================ void validate() { unsigned int lowest; unsigned int highest; unsigned int difference; lowest = min(atimes[atimecount-1],btimes[btimecount-1]); highest = max(atimes[atimecount-1],btimes[btimecount-1]); difference = highest - lowest; if (difference > validtime) { lcd.setCursor(12,0); lcd.print("FAIL"); byte ones; byte tens; byte fractions; difference = difference/100; fractions = difference%10; difference = difference/10; ones = difference%10; difference = difference/10; tens = difference%10; lcd.setCursor(12,1); lcd.print(tens); lcd.setCursor(13,1); lcd.print(ones); lcd.setCursor(14,1); lcd.print('.'); lcd.setCursor(15,1); lcd.print(fractions); } else { lcd.setCursor(12,0); lcd.print(" OK "); byte ones; byte tens; byte fractions; difference = difference/100; fractions = difference%10; difference = difference/10; ones = difference%10; difference = difference/10; tens = difference%10; lcd.setCursor(12,1); lcd.print(tens); lcd.setCursor(13,1); lcd.print(ones); lcd.setCursor(14,1); lcd.print('.'); lcd.setCursor(15,1); lcd.print(fractions); } } // =========================================== // Reset all values, returns to initial state // =========================================== void rstall() { rststate = true; lc.clearDisplay(0); delay(100); write7Segment(0, 0); write7Segment(0, 4); //reset all variables timer1 = false; timer2 = false; atime = 0; btime = 0; astime = 0; bstime = 0; atimes[0] = 0; atimes[1] = 0; atimes[2] = 0; btimes[0] = 0; btimes[1] = 0; btimes[2] = 0; atimecount = 0; btimecount = 0; lcd.clear(); lcd.setCursor(0,0); lcd.print('1'); lcd.setCursor(0,1); lcd.print('2'); lcd.setCursor(2,0); lcd.print("..."); lcd.setCursor(2,1); lcd.print("..."); lcd.setCursor(6,0); lcd.write(dot); lcd.setCursor(8,0); lcd.write(dot); lcd.setCursor(10,0); lcd.write(dot); lcd.setCursor(6,1); lcd.write(dot); lcd.setCursor(8,1); lcd.write(dot); lcd.setCursor(10,1); lcd.write(dot); lcd.setCursor(12,0); lcd.print("...."); lcd.setCursor(12,1); lcd.print("...."); } // ======================================================================== // Test routine, may be started by un-// the call at the end of void.Setup // ======================================================================== void testrun() { lc.clearDisplay(0); lcd.clear(); lcd.setCursor(0,0); lcd.print("WBC Stopwatch"); lcd.setCursor(0,1); lcd.print("Made by Tamas Szekffy in 2014"); delay(2000); for (int scroll = 0; scroll < 18; scroll++) { lcd.scrollDisplayLeft(); delay(500); } lcd.clear(); lcd.setCursor(0,0); lcd.print("Test run will"); lcd.setCursor(0,1); lcd.print("undergo in 5 s"); delay(5000); lcd.clear(); lc.clearDisplay(0); for (int i = 0; i < 10; i++) { lc.setDigit(0,0,i,false); lc.setDigit(0,1,i,true); lc.setDigit(0,2,i,false); lc.setDigit(0,4,i,false); lc.setDigit(0,5,i,true); lc.setDigit(0,6,i,false); delay(500); } lc.clearDisplay(0); delay(500); lcd.clear(); delay(250); lcd.setCursor(0,0); lcd.print('1'); delay(250); lcd.setCursor(0,1); lcd.print('2'); delay(250); lcd.setCursor(2,0); lcd.print("..."); delay(250); lcd.setCursor(2,0); lcd.print("RUN"); delay(250); lcd.setCursor(2,1); lcd.print("..."); delay(250); lcd.setCursor(2,1); lcd.print("RUN"); delay(250); lcd.setCursor(6,0); lcd.write(dot); delay(250); lcd.setCursor(6,0); lcd.write(full); delay(250); lcd.setCursor(8,0); lcd.write(dot); delay(250); lcd.setCursor(8,0); lcd.write(full); delay(250); lcd.setCursor(10,0); lcd.write(dot); delay(250); lcd.setCursor(10,0); lcd.write(full); delay(250); lcd.setCursor(6,1); lcd.write(dot); delay(250); lcd.setCursor(6,1); lcd.write(full); delay(250); lcd.setCursor(8,1); lcd.write(dot); delay(250); lcd.setCursor(8,1); lcd.write(full); delay(250); lcd.setCursor(10,1); lcd.write(dot); delay(250); lcd.setCursor(10,1); lcd.write(full); delay(250); lcd.setCursor(12,0); lcd.print("...."); delay(250); lcd.setCursor(12,1); lcd.print("...."); delay(1000); lcd.clear(); lcd.setCursor(0,0); lcd.print("Ready to GO!"); delay(3000); lcd.clear();
}

Step 5: How to Use

Before first try, I suggest to upload the sketch with the test routine allowed (remove '//' in void Setup() from the line

//  testrun();
Use

Power up. Wait test to finish and main display show on (see image 2 on top). You will see two independent lines with the following informations (from left to right):

1. Number of stopwatch (1 and 2)

2. State of the stopwatch (... if in idel, RUN if counting)

3. Markings to show which measurement are finished (small dot for unfinished, big box for finished measurements for each of three independent rounds).

4. Four dots at the end of each line, until a pair of measurements are finished. As soon as a pair of measurement finished, the result of time verification will appear: OK if the measurements is between the 3 second boundary, FAIL, if the measurements are exceeded the boundary. On the second line, you'll see the difference of two measurements.

(See picture 1. above.)

To start a stopwatch, simply press the start/stop button of the desired stopwatch (1 or 2). Due to some debouncing delay, I do not recommend to press both button at the very same time. Based on my measurements, the possibility to push one of the buttons within the “blind” time of debouncing is rather high within about 50 microseconds (1/20th of a second) period after a button has pressed. Please check the state of each stopwatch after you push ANY of the buttons!

It is theoretically unwanted, so both measurement within a serving (1st, 2nd and 3rd serving) should be measured (started and stopped) before the preparation of next serving take place. There is no rule to serve all four beverages at the same time, so the competitor is free to serve two drinks at a time, and start the preparation of the next two drinks of the same kind of beverage just after that. But it’s not allowed to start the brewing of the next kind of drinks before all four beverage had been served. The sketch doesn’t count with this strange (yet completly unwanted) scenario. For example, if a competitor use a two group machine, and start the preparation of an espresso drink as the first beverage, start timer 1 as soon as the first brewing starts, then stop it as the brewing has turned off. Whatever the first brewing is in progress or not, start timer 2 as soon as the competitor starts the brewing on the second grouphead, and stop it as the barista shuts of the machine. Then start measure 2. and 3. pair of brewings. Plase note the above warning about debouncing (do not start or stop the timers at the very same time). Also do not start the measurement of the next pair of drinks before both of the previous pair of measurements finished, otherwise the validation will not be made.

After all the six measurements are made, simply press the reset button once to clear all the data, and put the stopwatch back to its normal state.

Picture 2 shows the LCD as its natural state (idle state), ready to measure times.

Picture 3 shows the LCD as first round of measurementsd made (first set of drinks), first measurement of second round finished (big box on top center), second measurement of second round running (RUN on left), and the final validation of first round: valid with a difference of 0.8 seconds.

Picture 4 shows the LCD as all six measurements (two measurements in each of 3 set of drinks) made, last (third) measurement validated as failed due to 3.4 second difference between two brewing times.

For a brief tutorial, see this film.

LED displays

Each LED displays is used to display measured time for two separate stopwatches. The time measured on first stopwatch can be seen on the left, the time measured on the second stopwatch can be seen on the right display. It’s important to notice that there is no running time displayed during measurement. The actual state of the stopwatch (running or idle) is indicated on LED displays with a bar seen on the rightmost characer of each display.

After power on/reset, each displays shows 00.0. After push any of start/stop button, a bar will turn on on the middle of the rightmost character of the display to show that the measurement is going (as well as “RUN” will display on the LCD screen). As soon as the timer stopped (with a push of the desired start/stop button), the bar will disappear and the measured time will displayed immediately with a 1/10th second accuracy. This value will be unchanged until the next measurement of the stopwatch will be finished.

If you have any troubles reading the LED displays (too bright or too dim), change the value of

lc.setIntensity(0,8);

in void Setup(). Minimum is 0 (dimmer), maximum is 8 (brighter). Default setting is 8 (brighter).

Menu setting

In Menu, you can setup the validation time from the pre-set 3 second (3000 milliseconds) to any desired value, as well as the backlight setting for LCD display.

To enter Menu, reset the stopwatch (finish all the six measurements and push the reset button), or press the reset button of the board. Push Reset for at least 3 seconds until the “WBC valid time” “3000 msec” will appear on the LCD display. Decrease or increase the value by 100 msec (0,1 sec) increments with start/stop buttons. The button of stopwatch 1 will decrease, the button of stopwatch 2 will increase the value. Minimum is 0, maximum is 32700, which means 32,7 second.

To step forward for backlight adjustment, press Reset once.

Adjust the backlight as desired with start/stop buttons. Minimum is 0 (no backlight), maximum is 250 (brightest). If there is no significant change of readibility of the display, adjust the contrast with the potentiometer connected to LCD pin 3 (V0).

To exit menu, press Reset again.

Please note, that these settings will erased each time the Arduino board reset (restart, serial communication or due to power failure).

Step 6: Future Plans

This watch is functional and trustable. There are a few plans about it for future development. Some of them are only coding, others need (somewhat serious) hardware changes.

1. „Brew not served to judges” setting, cancel of one measurement

It's absolutely not usual, by the way, WBC Rules and Regulations let the competitor to cancel the serving of a set of beverages. In this situation, the last measurement should be cancelled. Now it's not possible, some fake measurements should be made, and a new set of measurements should started. It's just a bit of coding, of course. I haven’t even heard any existence of a related situation, so it’s not a serious feature.

2. Logging

A real time clock and an SD-card are needed, among some coding, of course. This new feature will record the start time and date of each measurements, measured times for each brewing, as well as the valid/no-valid sign.

3. On-line logging

Possibly can be made with a Yún, or an arduino and an Ethernet/Wifi board. Same functionality with the previous function, except all the information will be transferred to a website/web logger service.

4. WBC/WCE/SCAA/SCAE approval

It's a waaaaaay long journey. I think, and hope, that this project will be useful enough to put it in “mass” production, as well as approved by the mentioned organizations. I think, logging and some reduncancy, as well as more accurate measurements are necessary to meet all the requirements, meanwhile all current procedures/protocols are almost the same by the meaning of reduncancy, accuracy and trustability, by the way, have some disadvantages (see in description).

5. Kit/assembled availability

Of course, it's my dream to put this into a start-up project. Plan a useful display, a case, final code, and a bunch of component to selled as a kit or a pre-assembled final product to all national chapters of SCAA/SCAE. A somewhat different code and some minor changes in hardware may let judges and volunteers to measure preparation/competition/clean-up times for example.

Step 7: Updates

as of 21. March, 2014.
Since the original post I've made some tests with the accuracy of the internal clock (millis() and micros() ) of different arduino boards (Mega2560, Leonardo, Nano 328). I've experienced that in most cases, an average arduino board is off-time for about 4 seconds within 10 minutes. It's almost 1% inaccuracy. For the usual 30 seconds long brewing time, it means 0,2 seconds variance between real time and measured time. In usual circumstances, this difference is less than the average deviation between measurements made by an average person with a usual stopwatch.
I'll make some improvements with a DS3231 RTC (32 kHz precision oscillator) in the close future and update this instructables with my experiences.