Coffee machine cleaning timer gadget

I have a coffee-making machine that makes nice coffee:

Unfortunately it is supposed to be cleaned each week (each day?) using these rather complex instructions:

Every time I go to do that, it almost drives me insane as I try to get it right (run 5 seconds, off 2 seconds, repeat 10 times, etc.). So I thought, "why not make a gadget that does that?". So here it is:

Everything is "off the shelf" hardware-wise, except the addition of a soldered-on piezo buzzer (circled).

Buttons ($US 8.90):

http://iteadstudio.com/store/index.php?main_page=product_info&cPath=18&products_id=351

LCD ($US 6.80):

http://iteadstudio.com/store/index.php?main_page=product_info&cPath=57_60&products_id=155

Plus an Arduino Uno ($US 30).

You basically hit the "clean" or "rinse" button and it counts down the correct number of cycles, with a beep to alert you to change modes on the coffee machine. It doesn't operate the machine, but it does the timing and counting for you.

Code:

// Program for timing Cappuccino machine cleaning cycle

// Author: Nick Gammon
// Date: 15 June 2011

#include <avr/pgmspace.h>
#include <iBridge_Keypad.h>
#include <iBridge_LCD_display.h>

#define CLEANCYCLES 5   // number of cleaning cycles
#define CLEANSECS 5     // seconds to clean
#define CLEANSITSECS 15 // seconds to sit idle

#define RINSECYCLES 10  // number of rinse cycles
#define RINSESECS 5     // seconds to rinse
#define RINSESITSECS 2  // seconds to sit idle

// beep sound
#define FREQUENCY 880L  // Hz
#define PERIOD 500000L / FREQUENCY  // (1 / frequency) * 1e6 / 2

// pins buzzer is connected to
#define BUZZER1 A2
#define BUZZER2 A4

// keypad
const byte ROWS = 3; 
const byte COLS = 3; 
const char keys[ROWS][COLS] = {
  { '1', '2', '3' },
  { '4', '5', '6' },
  { '7', '8', '9' },
};

byte rowPins[ROWS] = { 2, 3, 4 }; //connect to the row pinouts of the keypad
byte colPins[COLS] = { 5, 6, 7 }; //connect to the column pinouts of the keypad

// Create the Keypad
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

// Create the LCD
iBridge_LCD_display lcd;

// Beep for duration times x number
void beep (const int duration, const int number = 1)
{

  for (int j = 0; j < number; j++)
    {
    for(int i = 0 ; i < duration ; i++)
    {
      digitalWrite (BUZZER1, HIGH);
      digitalWrite (BUZZER2, LOW);
      delayMicroseconds(PERIOD);
  
      digitalWrite (BUZZER1, LOW);
      digitalWrite (BUZZER2, HIGH);
      delayMicroseconds(PERIOD);
    }  // end of for duration
  
    digitalWrite (BUZZER1, LOW);
    digitalWrite (BUZZER2, LOW);
    
    delay (100);
    }  // end of for each number

}  // end of beep


void menu ()
{
  lcd.clear ();
  lcd.gotoxy (0, 0);
  lcd.string ("Coffee cleaner");
  lcd.gotoxy (0, 2);
  lcd.string ("1: Clean");
  lcd.gotoxy (0, 3);
  lcd.string ("3: Rinse");
  
}  // end of menu

void showcounter (char * s, byte line, byte c1, byte c2)
{
  lcd.gotoxy (0, line - 1);
  lcd.string (s);
  lcd.writeDecimal (c1);
  lcd.string ("/");
  lcd.writeDecimal (c2);
  lcd.string ("    ");
}  // end of showcounter

void clean ()
{
  for (byte cycle = 1; cycle <= CLEANCYCLES; cycle++)
    {
    beep (50, 2);
    lcd.gotoxy (0, 0);
    lcd.clear ();
    showcounter ("Clean #", 1, cycle, CLEANCYCLES);
   
    for (byte s = 1; s <= CLEANSECS; s++)
      {
      showcounter ("Run: ", 3, s, CLEANSECS);
      delay (1000);
      }

    beep (50, 5);
    
    for (byte s = 1; s <= CLEANSITSECS; s++)
      {
      showcounter ("Sit: ", 3, s, CLEANSITSECS);
      delay (1000);
      }
  
    } // end of for

  beep (50, 10);
  lcd.gotoxy (0, 0);
  lcd.clear ();
  lcd.string ("Rinse and");
  lcd.gotoxy (0, 1);
  lcd.string (" reinsert");
  lcd.gotoxy (0, 2);
  lcd.string (" handle.");
  delay (5000);

}  // end of clean

void rinse ()
{
  for (byte cycle = 1; cycle <= RINSECYCLES; cycle++)
    {
    beep (50, 2);
    lcd.gotoxy (0, 0);
    lcd.clear ();
    showcounter ("Rinse #", 1, cycle, RINSECYCLES);
    
    for (byte s = 1; s <= RINSESECS; s++)
      {
      showcounter ("Run: ", 3, s, RINSESECS);
      delay (1000);
      }
    
    beep (50, 5);

    for (byte s = 1; s <= RINSESITSECS; s++)
      {
      showcounter ("Sit: ", 3, s, RINSESITSECS);
      delay (1000);
      }
  
    } // end of for

  beep (50, 10);

  lcd.gotoxy (0, 0);
  lcd.clear ();
  lcd.string ("Brew and");
  lcd.gotoxy (0, 1);
  lcd.string (" discard");
  lcd.gotoxy (0, 2);
  lcd.string (" coffee.");
  delay (5000);

}  // end of rinse


void setup()
{
  lcd.begin();

  pinMode (BUZZER1, OUTPUT);   // piezo buzzer pins
  pinMode (BUZZER2, OUTPUT);

  beep (10);  // beep to show we started up

  menu ();
}  // end of setup

void loop()
{
  byte key = kpd.getKey();
  if (key)
    {
    delay (500);  // debounce
    switch (key)
      {
      case  '1': clean (); break;
      case  '3': rinse (); break;
      }  // end of switch
    menu ();
    }  // end if key pressed
}  // end of loop

Fun application, well done. Now the robotarm that does the cleaning :wink:

Nice looking coffee maker -- was that gauge always crooked, or is it just from the last time you took it all apart?

I didn't take it apart, it just became crooked. :frowning:

Maybe it's time to :slight_smile:

Would give you the opportunity to figure out how to connect your nifty new toy so it could actually control the cleaning cycle as well! I remember how nervous I was taking my iPod apart for the first time...