// Include libraries:
#include "EEPROM.h"
#include <TM1637Display.h>
// Connections:
#define CLK_PIN 2
#define DIO_PIN 3
#define pot1_clk A0
#define pot1_dt A1
#define pot2_clk A2
#define pot2_dt A3
#define startButton A4
#define relay A5
// Necessary variables for calculating the time and saving the state of the rotary encoders
unsigned long seconds, minutes;
int mins;
unsigned long leftTime;
unsigned long refreshTime;
int currentStateCLK, lastStateCLK;
int currentStateCLK2, lastStateCLK2;
TM1637Display display(CLK_PIN, DIO_PIN); // Instantiate a TM1637Display object
void setup() {
// Configure pins (necessary)
pinMode(pot1_clk, INPUT);
pinMode(pot1_dt, INPUT);
pinMode(pot2_clk, INPUT);
pinMode(pot2_dt, INPUT);
pinMode(startButton, INPUT_PULLUP);
pinMode(relay, OUTPUT);
// Read the minutes and seconds from EEPROM (saved), in WOKWI this feature does not work
minutes = EEPROM.read(0);
seconds = EEPROM.read(1);
if (minutes >= 60 || seconds >= 60) { // Invalid EEPROM?, default option:
minutes = 0;
seconds = 0;
}
// Read the initial state of CLK (starting position of the rotary encoder)
lastStateCLK = digitalRead(pot1_clk);
// Read the initial state of CLK2
lastStateCLK2 = digitalRead(pot2_clk);
// Start the TM1637 display module and set the 0:0 time
display.setBrightness(7);
setTime();
}
void loop() {
// Read the current state of CLK
currentStateCLK = digitalRead(pot1_clk);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1) {
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(pot1_dt) != currentStateCLK) {
minutes--;
if (minutes == -1) minutes = 99; // limit max minutes
} else {
// Encoder is rotating CW so increment
minutes++;
if (minutes == 100) minutes = 0; // limit max minutes
}
EEPROM.update(0, minutes); // save the value to EEPROM
setTime(); // display the current set time
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
currentStateCLK2 = digitalRead(pot2_clk);
if (currentStateCLK2 != lastStateCLK2 && currentStateCLK2 == 1) {
if (digitalRead(pot2_dt) != currentStateCLK2) {
seconds--;
if (seconds == -1) seconds = 59;
} else {
seconds++;
if (seconds == 60) seconds = 0;
}
EEPROM.update(1, seconds);
setTime();
}
lastStateCLK2 = currentStateCLK2;
// If start button was pressed
if (!digitalRead(startButton)) {
unsigned long timeMillis = (minutes * 60000) + (seconds * 1000); // Calculate total milliseconds to wait
unsigned long startMillis = millis(); // Load milliseconds from start
delay(500); // Wait 500ms to prevent double-clicking the button
digitalWrite(relay, HIGH); // Turn ON relay
while (digitalRead(startButton) && startMillis + timeMillis > millis()) { // Check if the time is up or the button was pressed (timer cancelled)
if (refreshTime + 1000 < millis()) { // Refresh the time display each second
refreshTime = millis();
leftTime = (startMillis + timeMillis - millis()) / 1000; // Calculate left time in millis, divide to get seconds
mins = 0;
while (leftTime >= 60) { // Divide the minutes
mins++;
leftTime = leftTime - 60;
}
int time = mins * 100 + leftTime; // Change format to MMSS
display.showNumberDecEx(time, 0b01000000, true); // Display with decimal point on second position
}
}
digitalWrite(relay, LOW);
delay(550); // Wait 550ms to prevent double-clicking the button
setTime();
}
// Put in a slight delay to help debounce the reading (rotary encoder, necessary!)
delay(1);
}
void setTime() {
int time = minutes * 100 + seconds; // Change format to MMSS
display.showNumberDecEx(time, 0b01000000, true); // Display with decimal point on second position
}