Hi,
I have utilised a programme using an Uno, for a countdown timer. I have tried to contact the original designer but without success. The coding is open source so I believe its ok to list this below. If someone could advise what additional code is required to enable this timer to auto reset 1-2 seconds from reaching 00:00 I would be very grateful. Thanking you in advance.
Original code
//-----------------------------------------------------
// Kitchen Timer with TM1637 Display
// by: mircemk
// License: GNU GPl 3.0
// Created: April 2025
//-----------------------------------------------------
#include <TM1637Display.h>
// Pin definitions
#define CLK 2
#define DIO 3
#define POT_PIN A0
#define START_BUTTON 4
#define RESET_BUTTON 5
#define BUZZER_PIN 6
#define RANGE_BUTTON 7
// Constants
#define QUANTIZE_INTERVAL 10
#define POT_SMOOTHING 30
#define POT_READ_DELAY 50
#define ALARM_FREQ 500
#define ALARM_ON_TIME 200
#define ALARM_OFF_TIME 200
#define COLON_ON_TIME 500
#define COLON_OFF_TIME 500
#define RANGE_DISPLAY_TIME 1000
#define BUTTON_DEBOUNCE_TIME 300
// Timer ranges in seconds
const int TIMER_RANGES[] = {300}; // 30min
const int NUM_RANGES = 1;
// Display instance
TM1637Display display(CLK, DIO);
// Variables
unsigned long previousMillis = 0;
const long interval = 1000;
bool timerRunning = false;
int remainingTime = 0;
bool colonState = true;
unsigned long lastBuzzTime = 0;
unsigned long lastColonToggle = 0;
bool alarmOn = false;
bool displayOn = true;
int lastDisplayedTime = -1;
unsigned long lastPotRead = 0;
unsigned long lastDisplayFlash = 0;
int currentRangeIndex = 2; // Start with 60min range (index 2)
unsigned long rangeDisplayStartTime = 0;
bool showingRange = false;
unsigned long lastRangeButtonPress = 0;
// State enumeration
enum TimerState {
IDLE,
SHOWING_RANGE,
RUNNING,
ALARMING
};
TimerState currentState = IDLE;
void setup() {
pinMode(START_BUTTON, INPUT_PULLUP);
pinMode(RESET_BUTTON, INPUT_PULLUP);
pinMode(RANGE_BUTTON, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
display.setBrightness(0x0a);
updateDisplay(quantizeTime(readSmoothedPot()));
}
void loop() {
unsigned long currentMillis = millis();
switch(currentState) {
case SHOWING_RANGE:
// Stay in range display mode until time elapsed
if (currentMillis - rangeDisplayStartTime >= RANGE_DISPLAY_TIME) {
currentState = IDLE;
lastDisplayedTime = -1; // Force pot reading update
} else {
// Keep showing range
displayRange(TIMER_RANGES\[currentRangeIndex\]);
return; // Skip all other processing while showing range
}
break;
case IDLE:
// Handle range button
if (digitalRead(RANGE_BUTTON) == LOW) {
if (currentMillis - lastRangeButtonPress >= BUTTON_DEBOUNCE_TIME) {
currentRangeIndex = (currentRangeIndex + 1) % NUM_RANGES;
rangeDisplayStartTime = currentMillis;
lastRangeButtonPress = currentMillis;
currentState = SHOWING_RANGE;
displayRange(TIMER_RANGES\[currentRangeIndex\]);
return; // Exit loop immediately after changing to range display
}
}
// Handle potentiometer input
if (currentMillis - lastPotRead > POT_READ_DELAY) {
lastPotRead = currentMillis;
int potTime = quantizeTime(readSmoothedPot());
if (potTime != lastDisplayedTime) {
colonState = true;
updateDisplay(potTime);
lastDisplayedTime = potTime;
}
}
// Handle start button
if (digitalRead(START_BUTTON) == LOW) {
delay(50);
if (digitalRead(START_BUTTON) == LOW) {
remainingTime = quantizeTime(readSmoothedPot());
currentState = RUNNING;
previousMillis = currentMillis;
lastDisplayedTime = -1;
colonState = true;
lastColonToggle = currentMillis;
}
}
break;
case RUNNING:
// Handle colon blinking
if (colonState && (currentMillis - lastColonToggle >= COLON_ON_TIME)) {
colonState = false;
lastColonToggle = currentMillis;
updateDisplay(remainingTime);
}
else if (!colonState && (currentMillis - lastColonToggle >= COLON_OFF_TIME)) {
colonState = true;
lastColonToggle = currentMillis;
updateDisplay(remainingTime);
}
// Update timer
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (remainingTime > 0) {
remainingTime--;
updateDisplay(remainingTime);
}
if (remainingTime == 0) {
currentState = ALARMING;
}
}
// Check reset button
if (digitalRead(RESET_BUTTON) == LOW) {
delay(50);
if (digitalRead(RESET_BUTTON) == LOW) {
resetTimer();
}
}
break;
case ALARMING:
handleAlarm();
if (digitalRead(RESET_BUTTON) == LOW) {
delay(50);
if (digitalRead(RESET_BUTTON) == LOW) {
resetTimer();
}
}
break;
}
}
void displayRange(int rangeInSeconds) {
int minutes = rangeInSeconds / 60;
uint8_t segments[4];
segments[0] = display.encodeDigit(minutes / 10);
segments[1] = display.encodeDigit(minutes % 10) | 0x80; // Force colon on
segments[2] = display.encodeDigit(0);
segments[3] = display.encodeDigit(0);
display.setSegments(segments);
}
int readSmoothedPot() {
long total = 0;
for (int i = 0; i < POT_SMOOTHING; i++) {
total += analogRead(POT_PIN);
delay(1);
}
int average = total / POT_SMOOTHING;
return map(average, 0, 1023, 0, TIMER_RANGES[currentRangeIndex]);
}
int quantizeTime(int seconds) {
int quantized = (seconds / QUANTIZE_INTERVAL) * QUANTIZE_INTERVAL;
return constrain(quantized, 0, TIMER_RANGES[currentRangeIndex]);
}
void updateDisplay(int timeInSeconds) {
if (currentState == ALARMING && !displayOn) {
display.clear();
return;
}
int minutes = timeInSeconds / 60;
int seconds = timeInSeconds % 60;
uint8_t segments[4];
segments[0] = display.encodeDigit(minutes / 10);
segments[1] = display.encodeDigit(minutes % 10);
segments[2] = display.encodeDigit(seconds / 10);
segments[3] = display.encodeDigit(seconds % 10);
if (colonState) {
segments\[1\] |= 0x80;
}
display.setSegments(segments);
lastDisplayedTime = timeInSeconds;
}
void handleAlarm() {
unsigned long currentMillis = millis();
// Handle display flashing
if (currentMillis - lastDisplayFlash >= 500) {
lastDisplayFlash = currentMillis;
displayOn = !displayOn;
updateDisplay(0);
}
// Handle intermittent alarm sound
if (currentMillis - lastBuzzTime >= (alarmOn ? ALARM_ON_TIME : ALARM_OFF_TIME)) {
lastBuzzTime = currentMillis;
alarmOn = !alarmOn;
if (alarmOn) {
tone(BUZZER_PIN, ALARM_FREQ);
} else {
noTone(BUZZER_PIN);
}
}
}
void resetTimer() {
currentState = IDLE;
timerRunning = false;
noTone(BUZZER_PIN);
alarmOn = false;
displayOn = true;
colonState = true;
lastDisplayedTime = -1;
updateDisplay(quantizeTime(readSmoothedPot()));
}