/* Grinder Timer V2
Replacement of my first grinder timer.
Uses a scroll wheel to select time, a 2-digit display, and buttons for triggering timed countdown
or free count up.
by Jeff Saltzman
Interface with a scroll controller from a Panasonic VCR remote and a 2-digit 7-segment LED display from a Sony DigiBeta deck.
This will become the next version of the coffee grinder timer with the addition of two buttons and an output to a relay or triac
v.1: 11/12/2008: Just starting: first task: count
second task: mess w/LED... that'll eat a lot of pins...:o
11/15/2008: Counting 0-99 on the display! Will keep code verbose for now
Got the scroller working-- simple! Adding different modes...
11/22/2008: Get anlog pin to switch multiple inputs
Build complete countDown program
Program enhancements:
Chase and time remaining display during countDown
No scroll during override
Max override time
Flash max time when max time reached
Flash ct when countDown complete
Do not show leading 0 when count time is single digits
Chase and time elapsed display during override
Lit buttons for countDown grinding versus override
Store delay in EEPROM so it will have the same count as last time
11/22/2008: Test with triac output: shuffle pins go use 13 for the trigger
11/30/2008: Fixing code thanks to suggestions in the forum:
Set multiPin's pullup resistor so the "extra" resistor is not needed per http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1226896251/3#3
12/13/2008: Major revision: use shift registers for displaying the digits
12/14/2008: Add proper third override mode: dial, count down, override (count up)
Check button lights w/logic of registers: add 128 to byte for button light
Check multi-button input: cancel mode when hitting other button
12/30/2008: Build display+button board
Refine display+button board EAGLE layout
Etch PCB, clean PCB, drill PCB
Build board
12/31/2008: Troubleshoot board and program
Infinite looping when counting down from >32s
HW Testing:
Segment 1D not lit
Chase animation incorrect
Button 2 lights?
Button 1 accidentally triggers
No button 2 trigger
1/1/2009: Build relay circuit onto outlet
Capacitor squeal-- replace
Replace M with F headers on display board
Build power circuit-- swich & reg >> BB
Print cutting guides
Cut box
Make standoffs for control board
Mount everything
- FINAL *
1/2/2009: GRIND ME SOME COFFEE!
All code released under
Creative Commons Attribution-Noncommercial-Share Alike 3.0
*/
#include <EEPROM.h>
// Shift register control pins
int latchPin = 8;
int clockPin = 12;
int dataPin = 11;
// Input button pins
int btn1Pin = 6;
int btn2Pin = 5;
int btnUpPin = 4;
int btnDnPin = 3;
// Button states
int btn1Val = 0; // Value of button 1
int btn2Val = 0; // Value of button 2
int upVal = 0; // Value of up scroller
int dnVal = 0; // Value of down scroller
int btn1Last = HIGH; // Last value for button 1
int btn2Last = HIGH; // Last value for button 2
int upLast = 0; // Last value for scroll up
int dnLast = 0; // Last value for scroll down
// Trigger pin
int triggerPin = 2; // Pin to trigger output relay
int triggerVal = 0; // Value of the trigger pin
// Vars for debouncing
long time1 = 0; // The last time button 1 was toggled
long time2 = 0; // The last time button 2 was toggled
long timeUp = 0; // The last time up button was toggled
long timeDn = 0; // The last time down button was toggled
long debounce = 50; // The debounce time for the buttons
long chaseTime = 0; // Time of last chase step
// Timing the grind...
long grindStartTime = 0; // The time the countDown or override started
long grindTimeMax = 60; // Longest the grinder can run
int grindTimeMin = 3; // Shortest duration for a timed grind
// Program vars
int mode = 0; // 0: dialing, 1: counting down, 2: counting up, 3: setup
long ct = 28; // Count value: the desired time
int ctRead = 0; // Count value read from the EEPROM
long longClick = 3000; // ms duration to click and hold button 1 for setup mode
int chaseDelay = 42; // ms duration of each chase segment
// Program status vars
int countDown = 0; // Countdown value
int countUp = 0; // Count up value
boolean setupMax = false; // Whether setting max time or not (not == setting min)
int chaseVal = 0; // 0-7: chase position
int i; // For keeping track of things
int j; // For keeping track of...
long freqCounter = 0; // Timing test counter
long freqTime = 0; // Last time frequency was reported
// Data for digits 0-8 to shift gfedcba: 7 BITS ONLY
//byte digs[] = {
// B0111111, B0000110, B1011011, B1001111, B1100110, B1101101, B1111101, B0000111, B1111111, B1101111 };
// old: gfedcba
// new1: cedgafb
// new1: bfagdec
byte digs1[] = {
B1110111, B1000001, B0111101, B1011101, B1001011, B1011110, B1111110, B1000101, B1111111, B1011111 };
byte digs2[] = {
B1110111, B1000001, B1011110, B1011101, B1101001, B0111101, B0111111, B1010001, B1111111, B1111101 };
// Chase pattern, 2-segments clockwise
byte ch1[] = {
B1000001, B1010000, B0010000, B0000000, B0000000, B0000000, B0000100, B0000101 };
byte ch2[] = {
B0000000, B0000000, B0000100, B0000110, B0100010, B0110000, B0100000, B0000000 };
// Data to display
byte D1; // First digit to display
byte D2; // Second digit to display
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void setup() {
Serial.begin(9600);
// Set trigger output pin
pinMode(triggerPin, OUTPUT);
digitalWrite(triggerPin, LOW);
// Set shift register output pins
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
// Set input buttons
pinMode(btn1Pin, INPUT);
pinMode(btn2Pin, INPUT);
pinMode(btnUpPin, INPUT);
pinMode(btnDnPin, INPUT);
// Turn on pullup resistors for the pins
digitalWrite(btn1Pin, HIGH);
digitalWrite(btn2Pin, HIGH);
digitalWrite(btnUpPin, HIGH);
digitalWrite(btnDnPin, HIGH);
// Get ct from the EEPROM
ctRead = EEPROM.read(0);
delay(100);
ct = ctRead;
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
void loop() {
// Frequency counter: how many times/sec does the loop run?
freqCounter += 1;
if ((millis()-freqTime) > 5000) {
Serial.print(freqCounter/5);
Serial.println(" Hz");
freqCounter = 0;
freqTime = millis();
}
// Read control button inputs
btn1Val = digitalRead(btn1Pin);
btn2Val = digitalRead(btn2Pin);
upVal = digitalRead(btnUpPin);
dnVal = digitalRead(btnDnPin);
// Debounce and process button 1: countdown OR setup
if (btn1Val == 1 && btn1Last == 0 && millis() - time1 > debounce) {
if (mode == 0) { // Button 1 switching from mode 0 (dialing) to mode 1 (timed grind)
Serial.println("B1: Mode 1: countdown");
mode = 1;
runGrinder(true);
grindStartTime = millis();
EEPROM.write(0, int(ct)); // Write ct to the EEPROM
delay(20);
}
else if (mode == 1 || mode == 2) { // Button 1 switching from modes 1 or 2 to mode 0 (dialing)
Serial.println("B1: Mode 0: wait");
mode = 0;
runGrinder(false);
}
time1 = millis();
}
btn1Last = btn1Val;
// Debounce and process button 2: override
if (btn2Val == 1 && btn2Last == 0 && millis() - time2 > debounce) {
if (mode == 0) { // Button 2 switching from mode 0 (dialing) to mode 2 (count up/override)
Serial.println("B2: Mode 2: count up/override");
mode = 2;
runGrinder(true);
grindStartTime = millis();
}
else if (mode == 2 || mode == 1) { // Button 2 switching off the relay and not chasing
Serial.println("B2: Mode 0");
mode = 0;
runGrinder(false);
}
time2 = millis();
}
btn2Last = btn2Val;
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//-=-= MODES =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
if (mode == 0) {
// Debounce and process up and down buttons: scroll
if (upVal == 1 && upLast == 0 && millis() - timeUp > debounce) {
if ((millis()-timeUp) < 120) ct+=2;
else ct+=1;
Serial.print("UP: ");Serial.print(ct);Serial.print(" (");Serial.print(millis()-timeUp);Serial.println(")");
timeUp = millis();
}
upLast = upVal;
if (dnVal == 1 && dnLast == 0 && millis() - timeDn > debounce) {
if ((millis()-timeDn) < 120) ct-=2;
else ct-=1;
Serial.print("DN: ");Serial.print(ct);Serial.print(" (");Serial.print(millis()-timeDn);Serial.println(")");
timeDn = millis();
}
dnLast = dnVal;