/*
Simple shift register / 7 segment countdown timer
AnonEngineering 9/15/26
*/
const int COUNT_VALUE = 15;
const int MAX_DIGITS = 2;
const int DP_POS = 2; // decimal point position, digit 1 - MAX_DIGITS
const unsigned long CNT_INTERVAL = 1000; // 1 second count
// pin constants
const int CLOCK_PIN = 10;
const int LATCH_PIN = 9;
const int DATA_PIN = 8;
const int DIGIT_PINS[MAX_DIGITS] = {6, 7}; // most significant digit first
// segment lookup table - DP, G - A
const byte NUM_SEGS[] = {
0b00111111, /* 0 */
0b00000110, /* 1 */
0b01011011, /* 2 */
0b01001111, /* 3 */
0b01100110, /* 4 */
0b01101101, /* 5 */
0b01111101, /* 6 */
0b00000111, /* 7 */
0b01111111, /* 8 */
0b01101111, /* 9 */
0b00000000 /* blank */
};
unsigned long prev_cnt_time = 0;
long countVal = COUNT_VALUE;
void updateDisplay(long value, int dpPos) {
int maxPowOfTen = MAX_DIGITS - 1;
int minBlankDigit = dpPos - 1;
int digitVal = 0;
long operand1 = 1, operand2 = 1;
// separate the digits
//thousands = (value % 10000) / 1000 (value % operand1 / operand2)
//hundreds = (value % 1000) / 100
//tens = (value % 100) / 10
//ones = (value % 10) / 1
for (int digit = 0; digit <= maxPowOfTen; digit++) {
for (int p = maxPowOfTen - digit; p >= 1; p--) {
operand2 *= 10;
}
operand1 = operand2 * 10;
// don't blank digit where the dp and lower are
if (digit < minBlankDigit) {
// lead zero blanking
digitVal = value < operand2 ? 10 : (value % operand1) / operand2;
} else {
// no lead zero blanking
digitVal = (value % operand1) / operand2;
}
writeShiftDigit (digit, digitVal, ((dpPos - 1) == digit));
operand1 = 1; operand2 = 1;
}
}
void writeShiftDigit(int digit, int number, bool dp) {
int value = NUM_SEGS[number]; // invert (~) bits for common anode
digitalWrite(LATCH_PIN, LOW);
//shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, dp ? (value | 0x80) : value);
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, value); // no DP here...
digitalWrite(LATCH_PIN, HIGH);
// drive digit, reverse for common anode
digitalWrite(DIGIT_PINS[digit], LOW);
digitalWrite(DIGIT_PINS[digit], HIGH);
}
// for hardware test only
void testDisplay() {
writeShiftDigit(0, 1, false);
writeShiftDigit(1, 2, false);
//writeShiftDigit(2, 3, true);
//writeShiftDigit(3, 4, false);
}
void setup() {
Serial.begin(115200);
// set pin modes
pinMode(DATA_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
for (int digit = 0; digit < MAX_DIGITS; digit++) {
pinMode(DIGIT_PINS[digit], OUTPUT);
digitalWrite(DIGIT_PINS[digit], HIGH); // start both digits off
}
}
void loop() {
if (millis() - prev_cnt_time >= CNT_INTERVAL) {
prev_cnt_time = millis();
countVal--;
if (countVal < 0) countVal = COUNT_VALUE;
}
updateDisplay(countVal, DP_POS);
//testDisplay();
}