/*
Seven segment countdown timer
from an idea by
Taxidermia — June 15, 2025 at 1:16 PM
Arduino | general
I only have the 3641BS, which is an anode, and it has 12 pins...
(the 4 digit display)
In a real circuit limit current to <5 mA per segment!
Modified for common anode
*/
const int INITIAL_TIME = 100;
const int DP_POS = 2; // decimal point at 3rd digit
const int NUM_BTNS = 2;
const int BTN_PINS[] = {A0, 12};
// Define digit control pins (D1 to D4)
const int DIGIT_PINS[4] = {A1, A2, A3, A4};
// Define segment pins (A-G, DP)
const int SEG_PINS[8] = {8, 10, 4, 6, 7, 9, 3, 5};
// Segment configuration for numbers 0-9 (A to G, DP)
const byte DIGITS[][8] = {
{1, 1, 1, 1, 1, 1, 0, 0}, // 0
{0, 1, 1, 0, 0, 0, 0, 0}, // 1
{1, 1, 0, 1, 1, 0, 1, 0}, // 2
{1, 1, 1, 1, 0, 0, 1, 0}, // 3
{0, 1, 1, 0, 0, 1, 1, 0}, // 4
{1, 0, 1, 1, 0, 1, 1, 0}, // 5
{1, 0, 1, 1, 1, 1, 1, 0}, // 6
{1, 1, 1, 0, 0, 0, 0, 0}, // 7
{1, 1, 1, 1, 1, 1, 1, 0}, // 8
{1, 1, 1, 1, 0, 1, 1, 0}, // 9
{0, 0, 0, 0, 0, 0, 0, 0} // blank
};
const int RED_LED_PIN = 13;
const int GRN_LED_PIN = 11;
const int BUZZ_PIN = 2;
const unsigned long DECI_SEC = 100;
// global variables
int btnState[NUM_BTNS];
int oldBtnState[NUM_BTNS];
bool isArmed = false;
int count = INITIAL_TIME;
unsigned long prevTime = 0;
// function returns which button was pressed, or 0 if none
int checkButtons() {
int btnPressed = 0;
for (int i = 0; i < NUM_BTNS; i++) {
// check each button
btnState[i] = digitalRead(BTN_PINS[i]);
if (btnState[i] != oldBtnState[i]) { // if it changed
oldBtnState[i] = btnState[i]; // remember state for next time
if (btnState[i] == LOW) { // was just pressed
btnPressed = i + 1;
//Serial.print("Button ");
//Serial.print(btnPressed);
//Serial.println(" pressed");
} else { // was just released
//Serial.print("Button ");
//Serial.print(btnPressed);
//Serial.println(" released");
}
delay(20); // debounce
}
}
return btnPressed;
}
// Show a number on a specific digit
void showDigit(int num, int digit) {
for (int i = 0; i < 8; i++) {
if (digit == DP_POS && i == 7) {
digitalWrite(SEG_PINS[i], 0);
} else {
digitalWrite(SEG_PINS[i], !DIGITS[num][i]);
}
}
// toggle digit pin
digitalWrite(DIGIT_PINS[digit], HIGH); // enable digit
digitalWrite(DIGIT_PINS[digit], LOW); // disable digit
}
// Display a 4-digit number on the 7-segment display
void showNumber(int number) {
int digitArray[4] = {
number / 1000,
(number / 100) % 10,
(number / 10) % 10,
number % 10
};
for (int i = 0; i < 4; i++) {
showDigit(digitArray[i], i);
}
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < NUM_BTNS; i++) {
pinMode(BTN_PINS[i], INPUT_PULLUP);
}
for (int i = 0; i < 8; i++) {
pinMode(SEG_PINS[i], OUTPUT);
}
for (int i = 0; i < 4; i++) {
pinMode(DIGIT_PINS[i], OUTPUT);
}
pinMode(RED_LED_PIN, OUTPUT);
pinMode(GRN_LED_PIN, OUTPUT);
digitalWrite(RED_LED_PIN, LOW);
digitalWrite(GRN_LED_PIN, HIGH);
Serial.println("Countdown timer ready...\n");
Serial.println("Safe mode active");
}
void loop() {
int btnNumber = checkButtons();
if (btnNumber) { // if not 0
if (btnNumber == 1) {
isArmed = true;
count = INITIAL_TIME;
Serial.println("System armed & running!");
} else {
isArmed = false;
//count = INITIAL_TIME;
Serial.println("Safe mode active");
noTone(BUZZ_PIN);
}
digitalWrite(RED_LED_PIN, isArmed);
digitalWrite(GRN_LED_PIN, !isArmed);
}
if (isArmed) {
if (millis() - prevTime >= DECI_SEC) {
prevTime = millis();
count--;
if (count <= 50) {
if (count % 2 == 0)
tone(BUZZ_PIN, 440, 100);
} else {
if (count % 10 == 0)
tone(BUZZ_PIN, 440, 100);
}
if (count <= 0) {
count = 0;
Serial.println("DETONATED!\n");
tone(BUZZ_PIN, 880, 1000);
isArmed = false;
}
}
}
// this must be called frequently
showNumber(count);
}
Arm
Safe