Simple & Versatile Arduino Kitchen Timer with TM1637 Display
This Arduino project delivers a user-friendly, highly customizable kitchen timer with all essential features.
Components and supplies
1
Grove - Buzzer - Piezo
1
tm1637 4 digit dis play
1
Arduino Nano
3
Grove - Button (P)
1
10kOhm potentiometer
Tools and machines
1
Soldering kit
Apps and platforms
1
Arduino IDE
Project description
Code
Code
cpp
..
1//----------------------------------------------------- 2// Kitchen Timer with TM1637 Display 3// by: mircemk 4// License: GNU GPl 3.0 5// Created: April 2025 6//----------------------------------------------------- 7 8 9#include <TM1637Display.h> 10 11// Pin definitions 12#define CLK 2 13#define DIO 3 14#define POT_PIN A0 15#define START_BUTTON 4 16#define RESET_BUTTON 5 17#define BUZZER_PIN 6 18#define RANGE_BUTTON 7 19 20// Constants 21#define QUANTIZE_INTERVAL 30 22#define POT_SMOOTHING 30 23#define POT_READ_DELAY 50 24#define ALARM_FREQ 500 25#define ALARM_ON_TIME 200 26#define ALARM_OFF_TIME 200 27#define COLON_ON_TIME 500 28#define COLON_OFF_TIME 500 29#define RANGE_DISPLAY_TIME 1000 30#define BUTTON_DEBOUNCE_TIME 300 31 32// Timer ranges in seconds 33const int TIMER_RANGES[] = {600, 1800, 3600}; // 10min, 30min, 60min 34const int NUM_RANGES = 3; 35 36// Display instance 37TM1637Display display(CLK, DIO); 38 39// Variables 40unsigned long previousMillis = 0; 41const long interval = 1000; 42bool timerRunning = false; 43int remainingTime = 0; 44bool colonState = true; 45unsigned long lastBuzzTime = 0; 46unsigned long lastColonToggle = 0; 47bool alarmOn = false; 48bool displayOn = true; 49int lastDisplayedTime = -1; 50unsigned long lastPotRead = 0; 51unsigned long lastDisplayFlash = 0; 52int currentRangeIndex = 2; // Start with 60min range (index 2) 53unsigned long rangeDisplayStartTime = 0; 54bool showingRange = false; 55unsigned long lastRangeButtonPress = 0; 56 57// State enumeration 58enum TimerState { 59 IDLE, 60 SHOWING_RANGE, 61 RUNNING, 62 ALARMING 63}; 64 65TimerState currentState = IDLE; 66 67void setup() { 68 pinMode(START_BUTTON, INPUT_PULLUP); 69 pinMode(RESET_BUTTON, INPUT_PULLUP); 70 pinMode(RANGE_BUTTON, INPUT_PULLUP); 71 pinMode(BUZZER_PIN, OUTPUT); 72 73 display.setBrightness(0x0a); 74 updateDisplay(quantizeTime(readSmoothedPot())); 75} 76 77void loop() { 78 unsigned long currentMillis = millis(); 79 80 switch(currentState) { 81 case SHOWING_RANGE: 82 // Stay in range display mode until time elapsed 83 if (currentMillis - rangeDisplayStartTime >= RANGE_DISPLAY_TIME) { 84 currentState = IDLE; 85 lastDisplayedTime = -1; // Force pot reading update 86 } else { 87 // Keep showing range 88 displayRange(TIMER_RANGES[currentRangeIndex]); 89 return; // Skip all other processing while showing range 90 } 91 break; 92 93 case IDLE: 94 // Handle range button 95 if (digitalRead(RANGE_BUTTON) == LOW) { 96 if (currentMillis - lastRangeButtonPress >= BUTTON_DEBOUNCE_TIME) { 97 currentRangeIndex = (currentRangeIndex + 1) % NUM_RANGES; 98 rangeDisplayStartTime = currentMillis; 99 lastRangeButtonPress = currentMillis; 100 currentState = SHOWING_RANGE; 101 displayRange(TIMER_RANGES[currentRangeIndex]); 102 return; // Exit loop immediately after changing to range display 103 } 104 } 105 106 // Handle potentiometer input 107 if (currentMillis - lastPotRead > POT_READ_DELAY) { 108 lastPotRead = currentMillis; 109 int potTime = quantizeTime(readSmoothedPot()); 110 if (potTime != lastDisplayedTime) { 111 colonState = true; 112 updateDisplay(potTime); 113 lastDisplayedTime = potTime; 114 } 115 } 116 117 // Handle start button 118 if (digitalRead(START_BUTTON) == LOW) { 119 delay(50); 120 if (digitalRead(START_BUTTON) == LOW) { 121 remainingTime = quantizeTime(readSmoothedPot()); 122 currentState = RUNNING; 123 previousMillis = currentMillis; 124 lastDisplayedTime = -1; 125 colonState = true; 126 lastColonToggle = currentMillis; 127 } 128 } 129 break; 130 131 case RUNNING: 132 // Handle colon blinking 133 if (colonState && (currentMillis - lastColonToggle >= COLON_ON_TIME)) { 134 colonState = false; 135 lastColonToggle = currentMillis; 136 updateDisplay(remainingTime); 137 } 138 else if (!colonState && (currentMillis - lastColonToggle >= COLON_OFF_TIME)) { 139 colonState = true; 140 lastColonToggle = currentMillis; 141 updateDisplay(remainingTime); 142 } 143 144 // Update timer 145 if (currentMillis - previousMillis >= interval) { 146 previousMillis = currentMillis; 147 if (remainingTime > 0) { 148 remainingTime--; 149 updateDisplay(remainingTime); 150 } 151 if (remainingTime == 0) { 152 currentState = ALARMING; 153 } 154 } 155 156 // Check reset button 157 if (digitalRead(RESET_BUTTON) == LOW) { 158 delay(50); 159 if (digitalRead(RESET_BUTTON) == LOW) { 160 resetTimer(); 161 } 162 } 163 break; 164 165 case ALARMING: 166 handleAlarm(); 167 if (digitalRead(RESET_BUTTON) == LOW) { 168 delay(50); 169 if (digitalRead(RESET_BUTTON) == LOW) { 170 resetTimer(); 171 } 172 } 173 break; 174 } 175} 176 177void displayRange(int rangeInSeconds) { 178 int minutes = rangeInSeconds / 60; 179 uint8_t segments[4]; 180 181 segments[0] = display.encodeDigit(minutes / 10); 182 segments[1] = display.encodeDigit(minutes % 10) | 0x80; // Force colon on 183 segments[2] = display.encodeDigit(0); 184 segments[3] = display.encodeDigit(0); 185 186 display.setSegments(segments); 187} 188 189int readSmoothedPot() { 190 long total = 0; 191 for (int i = 0; i < POT_SMOOTHING; i++) { 192 total += analogRead(POT_PIN); 193 delay(1); 194 } 195 int average = total / POT_SMOOTHING; 196 return map(average, 0, 1023, 0, TIMER_RANGES[currentRangeIndex]); 197} 198 199int quantizeTime(int seconds) { 200 int quantized = (seconds / QUANTIZE_INTERVAL) * QUANTIZE_INTERVAL; 201 return constrain(quantized, 0, TIMER_RANGES[currentRangeIndex]); 202} 203 204void updateDisplay(int timeInSeconds) { 205 if (currentState == ALARMING && !displayOn) { 206 display.clear(); 207 return; 208 } 209 210 int minutes = timeInSeconds / 60; 211 int seconds = timeInSeconds % 60; 212 213 uint8_t segments[4]; 214 215 segments[0] = display.encodeDigit(minutes / 10); 216 segments[1] = display.encodeDigit(minutes % 10); 217 segments[2] = display.encodeDigit(seconds / 10); 218 segments[3] = display.encodeDigit(seconds % 10); 219 220 if (colonState) { 221 segments[1] |= 0x80; 222 } 223 224 display.setSegments(segments); 225 lastDisplayedTime = timeInSeconds; 226} 227 228void handleAlarm() { 229 unsigned long currentMillis = millis(); 230 231 // Handle display flashing 232 if (currentMillis - lastDisplayFlash >= 500) { 233 lastDisplayFlash = currentMillis; 234 displayOn = !displayOn; 235 updateDisplay(0); 236 } 237 238 // Handle intermittent alarm sound 239 if (currentMillis - lastBuzzTime >= (alarmOn ? ALARM_ON_TIME : ALARM_OFF_TIME)) { 240 lastBuzzTime = currentMillis; 241 alarmOn = !alarmOn; 242 243 if (alarmOn) { 244 tone(BUZZER_PIN, ALARM_FREQ); 245 } else { 246 noTone(BUZZER_PIN); 247 } 248 } 249} 250 251void resetTimer() { 252 currentState = IDLE; 253 timerRunning = false; 254 noTone(BUZZER_PIN); 255 alarmOn = false; 256 displayOn = true; 257 colonState = true; 258 lastDisplayedTime = -1; 259 updateDisplay(quantizeTime(readSmoothedPot())); 260}
Documentation
Schematic
...
Schematic.png

Comments
Only logged in users can leave comments