#include <IRremote.h>
#define LATCH 9
#define CLOCK 10
#define DATA 8
#define BUZZER_PIN 12
#define RECEIVER_PIN 11
// 4 digit select pins (active LOW for common anode, HIGH for common cathode)
int digitPins[4] = {7, 6, 5, 4};
// Common-anode version of 0–9
byte table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66,
0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x00};
const int IR_1 = 48;
const int IR_2 = 24;
const int IR_3 = 122;
const int IR_4 = 16;
const int IR_5 = 56;
const int IR_6 = 90;
const int IR_7 = 66;
const int IR_8 = 74;
const int IR_9 = 82;
const int IR_0 = 104;
const int IR_POWER = 162; // Start / Pause
const int IR_RESET = 194; // Reset
enum State { IDLE, RUNNING, FINISHED };
State state = IDLE;
unsigned int timerValue = 0; // supports up to 9999
unsigned long lastSecond = 0;
unsigned long lastDisplay = 0;
int digits[4] = {0, 0, 0, 0}; // individual digits
// ---------------- DISPLAY FUNCTIONS ----------------
void showDigit(byte digit, byte pos) {
// Turn all digits OFF
for (int i = 0; i < 4; i++) digitalWrite(digitPins[i], HIGH);
// Send segment data
digitalWrite(LATCH, LOW);
shiftOut(DATA, CLOCK, MSBFIRST, table[digit]);
digitalWrite(LATCH, HIGH);
// Turn this digit ON
digitalWrite(digitPins[pos], LOW); // active LOW
delayMicroseconds(1000); // short hold time
digitalWrite(digitPins[pos], HIGH);
}
void updateDigits(unsigned int value) {
digits[0] = value / 1000;
digits[1] = (value / 100) % 10;
digits[2] = (value / 10) % 10;
digits[3] = value % 10;
}
void refreshDisplay() {
for (int i = 0; i < 4; i++) {
showDigit(digits[i], i);
}
}
// ---------------- IR HANDLER ----------------
void handleIR() {
int code = IrReceiver.decodedIRData.command;
Serial.print("Received Button Code: ");
Serial.println(code);
if (code == IR_0) timerValue = timerValue * 10 + 0;
else if (code == IR_1) timerValue = timerValue * 10 + 1;
else if (code == IR_2) timerValue = timerValue * 10 + 2;
else if (code == IR_3) timerValue = timerValue * 10 + 3;
else if (code == IR_4) timerValue = timerValue * 10 + 4;
else if (code == IR_5) timerValue = timerValue * 10 + 5;
else if (code == IR_6) timerValue = timerValue * 10 + 6;
else if (code == IR_7) timerValue = timerValue * 10 + 7;
else if (code == IR_8) timerValue = timerValue * 10 + 8;
else if (code == IR_9) timerValue = timerValue * 10 + 9;
else if (code == IR_POWER) {
if (state == IDLE && timerValue > 0) state = RUNNING;
else if (state == RUNNING) state = IDLE;
}
else if (code == IR_RESET) {
timerValue = 0;
state = IDLE;
digitalWrite(BUZZER_PIN, LOW);
}
if (timerValue > 9999) timerValue = 9999; // limit to 4 digits
updateDigits(timerValue);
}
// ---------------- SETUP / LOOP ----------------
void setup() {
Serial.begin(9600);
Serial.println("IR Receiver + 4-digit Counter");
pinMode(LATCH, OUTPUT);
pinMode(CLOCK, OUTPUT);
pinMode(DATA, OUTPUT);
for (int i = 0; i < 4; i++) {
pinMode(digitPins[i], OUTPUT);
digitalWrite(digitPins[i], HIGH);
}
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
IrReceiver.begin(RECEIVER_PIN, ENABLE_LED_FEEDBACK);
updateDigits(timerValue);
}
void loop() {
// Handle IR input
if (IrReceiver.decode()) {
handleIR();
IrReceiver.resume();
}
// Countdown
if (state == RUNNING && millis() - lastSecond >= 1000) {
lastSecond = millis();
if (timerValue > 0) {
timerValue--;
updateDigits(timerValue);
}
if (timerValue == 0) {
state = FINISHED;
digitalWrite(BUZZER_PIN, HIGH);
delay(500);
digitalWrite(BUZZER_PIN, LOW);
timerValue = 0;
state = IDLE;
}
}
// Refresh display quickly
refreshDisplay();
}