#include <Adafruit_NeoPixel.h>
#include <TM1637Display.h>
#define NUM_PIXELS 12
#define PIXEL_PIN 6
#define CLK 2
#define DIO 3
#define BUTTON_NEXT 7
#define BUTTON_PLUS 8
#define BUTTON_START 9
Adafruit_NeoPixel pixels(NUM_PIXELS, PIXEL_PIN, NEO_GRB + NEO_KHZ800);
TM1637Display display(CLK, DIO);
int timeDigits[4] = {0, 0, 0, 0}; // MM:SS
int selectedDigit = 0; // 0-3 = active, 4 = none
bool settingTime = true;
bool countdownRunning = false;
unsigned long lastInputTime = 0;
unsigned long countdownStart = 0;
unsigned long lastSecondTick = 0;
int totalSeconds = 0;
// Button states
bool lastNextState = HIGH;
bool lastPlusState = HIGH;
bool lastStartState = HIGH;
unsigned long lastBlinkTime = 0;
bool blinkOn = true;
void setup() {
pixels.begin();
display.setBrightness(0x0f);
pinMode(BUTTON_NEXT, INPUT_PULLUP);
pinMode(BUTTON_PLUS, INPUT_PULLUP);
pinMode(BUTTON_START, INPUT_PULLUP);
showTimeSetting();
}
void loop() {
handleButtons();
if (settingTime) {
if (millis() - lastBlinkTime > 500) {
blinkOn = !blinkOn;
lastBlinkTime = millis();
}
showTimeSetting();
if ((millis() - lastInputTime > 5000 && totalSecondsFromDigits() > 0)) {
startCountdown();
}
} else if (countdownRunning) {
unsigned long elapsed = (millis() - countdownStart) / 1000;
int remaining = totalSeconds - elapsed;
if (remaining >= 0 && elapsed != (millis() - lastSecondTick) / 1000) {
updateCountdown(remaining);
lastSecondTick = millis();
}
}
}
void handleButtons() {
bool nextState = digitalRead(BUTTON_NEXT);
bool plusState = digitalRead(BUTTON_PLUS);
bool startState = digitalRead(BUTTON_START);
if (settingTime) {
if (nextState == LOW && lastNextState == HIGH) {
selectedDigit = (selectedDigit + 1) % 5; // 0-3 = digits, 4 = none
lastInputTime = millis();
}
if (plusState == LOW && lastPlusState == HIGH && selectedDigit < 4) {
timeDigits[selectedDigit]++;
if (selectedDigit == 0 && timeDigits[0] > 5) timeDigits[0] = 0; // MM tens
if (selectedDigit == 1 && timeDigits[1] > 9) timeDigits[1] = 0;
if (selectedDigit == 2 && timeDigits[2] > 5) timeDigits[2] = 0; // SS tens
if (selectedDigit == 3 && timeDigits[3] > 9) timeDigits[3] = 0;
lastInputTime = millis();
}
if (startState == LOW && lastStartState == HIGH && totalSecondsFromDigits() > 0) {
startCountdown();
}
}
lastNextState = nextState;
lastPlusState = plusState;
lastStartState = startState;
}
void startCountdown() {
totalSeconds = totalSecondsFromDigits();
settingTime = false;
countdownRunning = true;
countdownStart = millis();
lastSecondTick = millis();
}
void showTimeSetting() {
uint8_t segData[4];
int digits[4] = {
timeDigits[0], timeDigits[1],
timeDigits[2], timeDigits[3]
};
for (int i = 0; i < 4; i++) {
if (selectedDigit == i && !blinkOn) {
segData[i] = 0x00; // blank
} else {
segData[i] = display.encodeDigit(digits[i]);
}
}
display.setSegments(segData);
}
int totalSecondsFromDigits() {
int minutes = timeDigits[0] * 10 + timeDigits[1];
int seconds = timeDigits[2] * 10 + timeDigits[3];
return minutes * 60 + seconds;
}
void updateCountdown(int remainingSeconds) {
int minutes = remainingSeconds / 60;
int seconds = remainingSeconds % 60;
display.showNumberDecEx(minutes * 100 + seconds, 0b11100000, true);
pixels.clear();
if (remainingSeconds > 60) {
for (int i = 0; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(0, 150, 0));
}
} else if (remainingSeconds > 10) {
int yellowPixels = map(remainingSeconds, 10, 60, 1, NUM_PIXELS);
for (int i = 0; i < yellowPixels; i++) {
pixels.setPixelColor(i, pixels.Color(200, 150, 0));
}
} else if (remainingSeconds > 0) {
for (int i = 0; i < remainingSeconds && i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, pixels.Color(255, 0, 0));
}
} else {
static bool flash = false;
flash = !flash;
uint32_t color = flash ? pixels.Color(255, 0, 0) : pixels.Color(0, 0, 0);
for (int i = 0; i < NUM_PIXELS; i++) {
pixels.setPixelColor(i, color);
}
}
pixels.show();
}