키친 타이머를 만들어 보자

by 하민정 | 2017-01-06 11:52
iot 건강한식사 쉽고간편하게 스마트홈 아두이노 음식탐지기 키친타이머

재료

  • 버저 1개
  • 표준 LCD 1개
  • 221 옴 저항기 1개
  • 브래드 보드 1개
  • 버튼 스위치 1개
  • 분압계 - 100k 옴 1개
  • 점퍼 와이어 1개
  • 아두이노 우노 1개
  • 10k 옴 저항기 1개

                                         


요리를 하다가 가끔 깜빡하고 냄비를 태워버리는 경우

혹은 보다 정성들인 음식을 하는 경우면 '혼자서 요리 하는 것은 벅차다' 라고 느낄 때가 있습니다. 

이럴 때면 '키친 타이머 하나만 두어도...' 라는 생각이 종종 들기도 합니다. 


굳이 만들어진 키친 타이머를 구입하지 않아도,

아두이노를 이용해 쉽고 간편하게 키친 타이머를 만들 수 있는 프로젝트

Hackster 에서 접하게 되어 이를 소개합니다. 


앞으로는 키친 타이머와 함께한 요리로 맛있고 건강한 식사를 할 수 있을 것입니다. 


Step 1. 준비물 


- 아두이노 우노 

- 표준 LCD 

- 버튼 스위치 

- 10k 옴 저항기 

- 221 옴 저항기 

- 분압계 - 100k 옴 

- 버저 

- 브래드 보드 

- 점퍼 와이어 


Step 2. 하드웨어 연결방법 




1) 아두이노와 LCD를

(D2, DB7), (D3,DB6), (D4,DB5), (D5, DB4), (D11, E), (D12, RS) 핀에 연결합니다. 


2) 분압계를 아두이노에 GND와 D10 핀에 연결합니다. 


3) 버튼 스위치를 아두이노 D6~D9 핀에 연결합니다. 


4) 버저를 아두이노 10번 핀에 연결합니다. 



Step 3. 코드 


1
2
3
4
5
const int buzzerPin = 10;
const int resetButtonPin = 6;
const int startStopButtonPin = 7;
const int downButtonPin = 8;
const int upButtonPin = 9;
cs


키친 타이머는 4개의 버튼을 이용합니다.  

6번 핀은 초기화 버튼,

7번 핀은 시작/정지 버튼,

8번 핀은 Down 버튼, 

9번 핀은 Up 버튼 으로 이용합니다. 


1
2
3
4
const int MODE_IDLE = 0;
const int MODE_SETUP = 1;
const int MODE_RUNNING = 2;
const int MODE_RINGING = 3;
cs


또한, 키친 타이머는 4가지 모드를 갖고 있습니다. 


⓵ IDLE 모드는  대기 모드

- 프로그램을 시작하거나 초기화 시키면 이 모드에 들어가게 됩니다. 


⓶ SETUP 모드는 설정 모드 

- 초기화 버튼을 길게 눌러 이 모드에 들어가게 됩니다. 

여기서 시작/정지 버튼을 통해 설정에서 바꿀 값을 선택할 수 있게 됩니다. 

Down 버튼과 Up 버튼을 이용해 선택한 값을 증가시키거나 감소시킬 수 있게 됩니다. 


⓷ RUNNING 모드는 타이머가 동작중인 모드

- 시작/정지 버튼을 눌러서 이 모드에 들어가게 됩니다.

이 모드에서 벗어나기 위해서는 시작/정지 버튼과 초기화 버튼을 동시에 눌러야 합니다.

그러면 대기 모드로 들어가게 됩니다.

 

⓸ RINGING 모드는 타이머 알람이 울리는 모드 

- 설정한 시간이 0 으로 되는 순간 이 모드에 들어가게 됩니다. 

어떠한 버튼을 눌러도 이 모드에서 벗어날 수 있습니다.

즉, 알람을 끌 수 있게 됩니다. 


1
2
3
4
5
6
7
8
9
10
11
void setup() {
  // put your setup code here, to run once:
  lcd.begin(16, 2);
  pinMode(resetButtonPin, INPUT);
  pinMode(startStopButtonPin, INPUT);
  pinMode(upButtonPin, INPUT);
  pinMode(downButtonPin, INPUT);
  pinMode(buzzerPin, OUTPUT);
  Serial.begin(9600);
}
 
cs


setup 에서 아두이노에 연결된 버튼 핀을 모두 입력으로 설정해 줍니다. 


loop 의 시작에서는 항상 모든 버튼을 false 상태로 초기화 시켜서 

아무 입력도 없는 상태로 만들어 줍니다. 

이후 특정 버튼이 눌렸을 때, 기능에 따라 모드를 바꾸어 수행하도록 만들어 줍니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
   * Reset button management
   */
  resetButtonPressed = false;
  resetButtonLongPressed = false;
  resetButtonState = digitalRead(resetButtonPin);
  if(resetButtonState != resetButtonPrevState)
  {
    resetButtonPressed = resetButtonState == HIGH;
    resetButtonPrevState = resetButtonState;
  }
  else  // Long press management...
  {
    if(resetButtonState == HIGH)
    {
      resetButtonLongPressCounter++;
      if(resetButtonLongPressCounter == 100)
      {
        resetButtonPressed = false;
        resetButtonLongPressed = true;
        resetButtonLongPressCounter = 0;
      }
    }
    else
    {
      resetButtonLongPressCounter = 0;
      resetButtonPressed = false;
      resetButtonLongPressed = false;
    }
  }
 
cs


⓵  초기화 버튼(=6번 핀)이 눌려진 경우 

초기화 버튼은 짧게 누르는 경우와 길게 눌려진 경우로 두가지 기능을 두었습니다. 

이를 resetButtonPressed 와 resetButtonLongPressed 로 구분해 두었습니다. 

초기화 버튼이 눌려지게 되면 resetButtonState에 값이 읽히게 됩니다. 


값을 계속 읽어서, resetButtonState 가 이전과 달라진 상태라면

짧게 누른 resetBottonPressed 에 해당하는 것이고, 

이전과 같은 상태라면 길게 누른 resetBottonLongPressed 상태에 해당하도록 구분하였습니다. 


각각에 대한 상태를 HIGH 로 변경해 버튼에 입력을 주었습니다. 


1
2
3
4
5
6
7
8
9
10
  /*
   * Start/Stop button management
   */
  startStopButtonPressed = false;
  startStopButtonState = digitalRead(startStopButtonPin);
  if(startStopButtonState != startStopButtonPrevState)
  {
    startStopButtonPressed = startStopButtonState == HIGH;
    startStopButtonPrevState = startStopButtonState;
  }
cs


⓶ 시작/정지 버튼 (=7번 핀)이 눌려진 경우

시작/정지 버튼이 눌려진 경우 이를 startStopButtonState 가 이전의 상태와 달라진 상태일 경우이고 

이때, startStopButtonPressed 와 startStopButtonState 를 HIGH 로 변경해 입력을 주었습니다. 


1
2
3
4
5
6
7
8
9
10
  /*
   * Down button management
   */
  downButtonPressed = false;
  downButtonState = digitalRead(downButtonPin);
  if(downButtonState != downButtonPrevState)
  {
    downButtonPressed = downButtonState == HIGH;
    downButtonPrevState = downButtonState;
  }
cs

 

⓷ Down 버튼 (=8번 핀) 이 눌려진 경우 

시작/정지 버튼에서와 마찬가지로 Down 버튼이 눌려진 경우 이를 인지해

downButtonPressed와 downButtonState 를 HIGH 로 변경해 입력을 주었습니다. 


1
2
3
4
5
6
7
8
9
10
  /*
   * Up button management
   */
  upButtonPressed = false;
  upButtonState = digitalRead(upButtonPin);
  if(upButtonState != upButtonPrevState)
  {
    upButtonPressed = upButtonState == HIGH;
    upButtonPrevState = upButtonState;
  }
cs


⓸ Up 버튼 (=9번 핀) 이 눌려진 경우 

Down 버튼과 같이 Up 버튼이 눌려진 경우 이를 인지해

upButtonPressed 와 upButtonState를 모두 HIGH 로 변경해 입력을 줍니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  /*
   * Mode management
   */
  switch(currentMode)
  {
    case MODE_IDLE:
      if(resetButtonPressed)
      {
        Reset();
      }
      if(resetButtonLongPressed)
      {
        currentMode = MODE_SETUP;
      }
      if(startStopButtonPressed)
      {
        currentMode = currentMode == MODE_IDLE ? MODE_RUNNING : MODE_IDLE;
        if(currentMode == MODE_RUNNING)
        {
          // STARTING TIMER!
          startTime = now();
        }
      }
      break;
 
    case MODE_SETUP:
      if(resetButtonPressed)
      {
        // Exit setup mode
        setupTime = setupSeconds + (60 * setupMinutes) + (3600 * setupHours);
        currentHours = setupHours;
        currentMinutes = setupMinutes;
        currentSeconds = setupSeconds;
        dataSelection = 0;
        currentMode = MODE_IDLE;
      }
      if(startStopButtonPressed)
      {
        // Select next data to adjust
        dataSelection++;
        if(dataSelection == 3)
        {
          dataSelection = 0;
        }
      }
      if(downButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // hours
            setupHours--;
            if(setupHours == -1)
            {
              setupHours = 99;
            }
            break;
          case 1: // minutes
            setupMinutes--;
            if(setupMinutes == -1)
            {
              setupMinutes = 59;
            }
            break;
          case 2: // seconds
            setupSeconds--;
            if(setupSeconds == -1)
            {
              setupSeconds = 59;
            }
            break;
        }
      }
      if(upButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // hours
            setupHours++;
            if(setupHours == 100)
            {
              setupHours = 0;
            }
            break;
          case 1: // minutes
            setupMinutes++;
            if(setupMinutes == 60)
            {
              setupMinutes = 0;
            }
            break;
          case 2: // seconds
            setupSeconds++;
            if(setupSeconds == 60)
            {
              setupSeconds = 0;
            }
            break;
        }
      }
      break;
    
    case MODE_RUNNING:
      if(startStopButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      if(resetButtonPressed)
      {
        Reset();
        currentMode = MODE_IDLE;
      }
      break;
 
    case MODE_RINGING:
      if(resetButtonPressed || startStopButtonPressed || downButtonPressed || upButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      break;
  }
 
cs


⓹ 타이머에서 모드 변경을 원하는 경우 

Ⅰ. 현재의 모드가 대기 상태 모드인 경우 

- 짧은 초기화 버튼이 눌려진 경우 : Reset ( )를 통해 초기화 시킵니다. 

- 긴 초기화 버튼이 눌려진 경우 : 현재 모드에서 설정 모드로 변경 합니다. 

- 시작/정지 버튼이 눌려진 경우 : 대기모드 에서는 동작 중 모드로 변경 합니다.

  현재 모드가 대기 모드가 아닌 경우 대기 모드로 변경 합니다.

  동작 중 모드로 변경할 때, now( )라는 함수를 이용해 현재 시간을 sec 단위로 입력 받습니다.  


Ⅱ. 설정 모드에서 모드를 변경하는 경우 

- 초기화 버튼이 눌려진 경우 : 설정한 시간을 현재 시간으로 변경해 줍니다. 

 ( 시간 관리를 위해 아두이노의 <Time.h> 라이브러리를 이용합니다. )

- 시작/정지 버튼을 눌려진 경우 : <시각/분/초> 중 변경하고 싶은 데이터를 선택합니다. 

- Down 버튼이 눌려진 경우 : 선택한 데이터 값을 1씩 감소시킵니다. 

- Up 버튼이 눌려진 경우 : 선택한 데이터 값을 1씩 증가시킵니다. 


Ⅲ. 동작 중 모드에서 모드를 변경하는 경우 

- 시작/정지 버튼이 눌려진 경우 : 현재 모드를 대기 모드로 변경해 줍니다. 

- 초기화 버튼이 눌려진 경우 : 초기화 시키고, 현재 모드를 대기 모드로 변경해 줍니다. 


Ⅳ. 알람이 울리는 모드에서 모드를 변경하는 경우 

- 초기화, 시작/정지, Down, Up 버튼 모두 현재 모드를 대기 모드로 변경해 줍니다. 


⓺ 시간 관리 

- 알람이 울리도록 설정한 시간인 ( setupTime )에 도달하면, 

즉, currentTime 값이 0 이하의 경우 부터 알람이 울리도록 만들었습니다. 


⓻ LCD 관리 

- LiquidCrystal.h 라이브러리를 사용합니다. 

각 모드에서의 LCD 출력을 switch 문으로 구분지어 라이브러리의 내부 함수를 이용합니다. 


⓼ 초기화 

- Reset( ) 함수가 호출이 되면, 

현재 모드를 대기 모드로 변경해 주고, 

현재 시간을 설정한 시간으로 변경해 줍니다. 


⓽ 전체 코드 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/**************************************************
 * Arduino Kitchen Timer v1.0 - 2016/01/27
 * By Angelo Fiorillo (Rome, IT)
 * This work is distributed under the GNU General 
 * Public License version 3 or later (GPL3+)
 * Please include this credit note if you want to 
 * re-use any part of this sketch. Respect my work 
 * as I'll do with yours.
 * Feel free to contact me: afiorillo@gmail.com
 * ************************************************/
#include <LiquidCrystal.h>
#include <Time.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
 
const int buzzerPin = 10;
const int resetButtonPin = 6;
const int startStopButtonPin = 7;
const int downButtonPin = 8;
const int upButtonPin = 9;
 
int setupHours = 0;     // How many hours will count down when started
int setupMinutes = 0;   // How many minutes will count down when started
int setupSeconds = 0;   // How many seconds will count down when started
time_t setupTime = 0;
 
int currentHours = 0;
int currentMinutes = 0;
int currentSeconds = 0;
time_t currentTime = 0;
 
time_t startTime = 0;
time_t elapsedTime = 0;
 
int resetButtonState = LOW;
long resetButtonLongPressCounter = 0;
int startStopButtonState = LOW;
int upButtonState = LOW;
int downButtonState = LOW;
int resetButtonPrevState = LOW;
int startStopButtonPrevState = LOW;
int upButtonPrevState = LOW;
int downButtonPrevState = LOW;
bool resetButtonPressed = false;
bool resetButtonLongPressed = false;
bool startStopButtonPressed = false;
bool upButtonPressed = false;
bool downButtonPressed = false;
 
const int MODE_IDLE = 0;
const int MODE_SETUP = 1;
const int MODE_RUNNING = 2;
const int MODE_RINGING = 3;
 
int currentMode = MODE_IDLE;    // 0=idle 1=setup 2=running 3=ringing
                                // Power up --> idle
                                // Reset --> idle
                                //  Start/Stop --> start or stop counter
                                //  Up / Down --> NOP
                                // Reset (long press) --> enter setup
                                //   Start/Stop --> data select
                                //   Up --> increase current data value
                                //   Down --> decrease current data value
                                //   Reset --> exit setup (idle)
 
int dataSelection = 0;  // Currently selected data for edit (setup mode, changes with Start/Stop)
                        // 0=hours (00-99) 1=minutes (00-59) 2=seconds (00-59)
 
void setup() {
  // put your setup code here, to run once:
  lcd.begin(16, 2);
  pinMode(resetButtonPin, INPUT);
  pinMode(startStopButtonPin, INPUT);
  pinMode(upButtonPin, INPUT);
  pinMode(downButtonPin, INPUT);
  pinMode(buzzerPin, OUTPUT);
  Serial.begin(9600);
}
 
void loop() {
  // put your main code here, to run repeatedly:
  startStopButtonPressed = false;
  upButtonPressed = false;
  downButtonPressed = false;
 
  /*
   * Reset button management
   */
  resetButtonPressed = false;
  resetButtonLongPressed = false;
  resetButtonState = digitalRead(resetButtonPin);
  if(resetButtonState != resetButtonPrevState)
  {
    resetButtonPressed = resetButtonState == HIGH;
    resetButtonPrevState = resetButtonState;
  }
  else  // Long press management...
  {
    if(resetButtonState == HIGH)
    {
      resetButtonLongPressCounter++;
      if(resetButtonLongPressCounter == 100)
      {
        resetButtonPressed = false;
        resetButtonLongPressed = true;
        resetButtonLongPressCounter = 0;
      }
    }
    else
    {
      resetButtonLongPressCounter = 0;
      resetButtonPressed = false;
      resetButtonLongPressed = false;
    }
  }
 
  /*
   * Start/Stop button management
   */
  startStopButtonPressed = false;
  startStopButtonState = digitalRead(startStopButtonPin);
  if(startStopButtonState != startStopButtonPrevState)
  {
    startStopButtonPressed = startStopButtonState == HIGH;
    startStopButtonPrevState = startStopButtonState;
  }
 
  /*
   * Down button management
   */
  downButtonPressed = false;
  downButtonState = digitalRead(downButtonPin);
  if(downButtonState != downButtonPrevState)
  {
    downButtonPressed = downButtonState == HIGH;
    downButtonPrevState = downButtonState;
  }
 
  /*
   * Up button management
   */
  upButtonPressed = false;
  upButtonState = digitalRead(upButtonPin);
  if(upButtonState != upButtonPrevState)
  {
    upButtonPressed = upButtonState == HIGH;
    upButtonPrevState = upButtonState;
  }
 
  /*
   * Mode management
   */
  switch(currentMode)
  {
    case MODE_IDLE:
      if(resetButtonPressed)
      {
        Reset();
      }
      if(resetButtonLongPressed)
      {
        currentMode = MODE_SETUP;
      }
      if(startStopButtonPressed)
      {
        currentMode = currentMode == MODE_IDLE ? MODE_RUNNING : MODE_IDLE;
        if(currentMode == MODE_RUNNING)
        {
          // STARTING TIMER!
          startTime = now();
        }
      }
      break;
 
    case MODE_SETUP:
      if(resetButtonPressed)
      {
        // Exit setup mode
        setupTime = setupSeconds + (60 * setupMinutes) + (3600 * setupHours);
        currentHours = setupHours;
        currentMinutes = setupMinutes;
        currentSeconds = setupSeconds;
        dataSelection = 0;
        currentMode = MODE_IDLE;
      }
      if(startStopButtonPressed)
      {
        // Select next data to adjust
        dataSelection++;
        if(dataSelection == 3)
        {
          dataSelection = 0;
        }
      }
      if(downButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // hours
            setupHours--;
            if(setupHours == -1)
            {
              setupHours = 99;
            }
            break;
          case 1: // minutes
            setupMinutes--;
            if(setupMinutes == -1)
            {
              setupMinutes = 59;
            }
            break;
          case 2: // seconds
            setupSeconds--;
            if(setupSeconds == -1)
            {
              setupSeconds = 59;
            }
            break;
        }
      }
      if(upButtonPressed)
      {
        switch(dataSelection)
        {
          case 0: // hours
            setupHours++;
            if(setupHours == 100)
            {
              setupHours = 0;
            }
            break;
          case 1: // minutes
            setupMinutes++;
            if(setupMinutes == 60)
            {
              setupMinutes = 0;
            }
            break;
          case 2: // seconds
            setupSeconds++;
            if(setupSeconds == 60)
            {
              setupSeconds = 0;
            }
            break;
        }
      }
      break;
    
    case MODE_RUNNING:
      if(startStopButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      if(resetButtonPressed)
      {
        Reset();
        currentMode = MODE_IDLE;
      }
      break;
 
    case MODE_RINGING:
      if(resetButtonPressed || startStopButtonPressed || downButtonPressed || upButtonPressed)
      {
        currentMode = MODE_IDLE;
      }
      break;
  }
 
  /*
   * Time management
   */
  switch(currentMode)
  {
    case MODE_IDLE:
    case MODE_SETUP:
      // NOP
      break;
    case MODE_RUNNING:
      currentTime = setupTime - (now() - startTime);
      if(currentTime <= 0)
      {
        currentMode = MODE_RINGING;
      }
      break;
    case MODE_RINGING:
      analogWrite(buzzerPin, 20);
      delay(20);
      analogWrite(buzzerPin, 0);
      delay(40);
      break;
  }
 
  /*
   * LCD management
   */
  //lcd.clear();
  lcd.setCursor(0, 0);
  switch(currentMode)
  {
    case MODE_IDLE:
      lcd.print("Timer ready     ");
      lcd.setCursor(0, 1);
      lcd.print(currentHours);
      lcd.print(" ");
      lcd.print(currentMinutes);
      lcd.print(" ");
      lcd.print(currentSeconds);
      lcd.print("    ");
      break;
    case MODE_SETUP:
      lcd.print("Setup mode: ");
      switch(dataSelection)
      {
        case 0:
          lcd.print("HRS ");
          break;
        case 1:
          lcd.print("MINS");
          break;
        case 2:
          lcd.print("SECS");
          break;
      }
      lcd.setCursor(0, 1);
      lcd.print(setupHours);
      lcd.print(" ");
      lcd.print(setupMinutes);
      lcd.print(" ");
      lcd.print(setupSeconds);
      lcd.print("    ");
      break;
    case MODE_RUNNING:
      lcd.print("Counting down...");
      lcd.setCursor(0, 1);
      if(hour(currentTime) < 10) lcd.print("0");
      lcd.print(hour(currentTime));
      lcd.print(":");
      if(minute(currentTime) < 10) lcd.print("0");
      lcd.print(minute(currentTime));
      lcd.print(":");
      if(second(currentTime) < 10) lcd.print("0");
      lcd.print(second(currentTime));
      break;
    case MODE_RINGING:
      lcd.print("   RING-RING!   ");
      lcd.setCursor(0, 1);
      lcd.print("                ");
      break;
  }
  delay(10);
}
 
void Reset()
{
  currentMode = MODE_IDLE;
  currentHours = setupHours;
  currentMinutes = setupMinutes;
  currentSeconds = setupSeconds;
}
 
cs



Step 4. 수행 결과 




댓글 1

MADE BY