Hello!
I want to create a clock with 2 shift registers. I appended the wireing diagram and the code:

// **Arduino Nano 4-Digit Clock with 74HC595**
// Pin assignments
const int LATCH_PIN = 4; // ST_CP (latch) pin of 74HC595 chain
const int CLOCK_PIN = 7; // SH_CP (clock) pin
const int DATA_PIN = 8; // DS (data) pin
const int BUZZER_PIN = 3; // Buzzer control pin (for tone output)
const int MODE_BTN = 4; // Mode/Set button (cycles through setting modes)
const int HOUR_BTN = 5; // Hour increment button
const int MIN_BTN = 6; // Minute increment button
// (PM indicator LED is on shift register IC1 QE output in this design)
// Segment bit patterns for 0-9 on a 7-seg common anode (active LOW segments).
// Format: bit7 bit6 ... bit0 correspond to DP, G, F, E, D, C, B, A (we will define the mapping)
// Here we use bit0->A, bit1->B, ..., bit6->G, bit7->DP for convenience.
const byte SEGMENT_PATTERNS[10] = {
// For common anode: '0' means segment ON, '1' means OFF.
// 0 -> ABCDEF on, G off (to display 0)
B11000000, // 0: segments A,B,C,D,E,F = 0 (ON), G=1 (OFF), DP=1 (off)
B11111001, // 1: segments B,C = 0, others 1 (only B,C on)
B10100100, // 2: A, B, G, E, D on
B10110000, // 3: A, B, C, D, G on
B10011001, // 4: F, G, B, C on
B10010010, // 5: A, F, G, C, D on
B10000010, // 6: A, F, E, D, C, G on
B11111000, // 7: A, B, C on
B10000000, // 8: all segments on
B10010000 // 9: A, B, C, D, F, G on (segment E off)
};
// (If your segment wiring order is different, or if using DP differently, adjust patterns accordingly.)
// Variables for time and alarm
volatile unsigned long millisAtLastSecond = 0; // track when we last updated the seconds
int hours = 12;
int minutes = 0;
int seconds = 0;
bool isPM = false; // true = PM, false = AM
int alarmHour = 6;
int alarmMin = 30;
bool alarmEnabled = true;
bool alarmTriggered = false; // becomes true when alarm is sounding
// Variables for button handling
byte mode = 0; // 0 = normal run, 1 = set hours, 2 = set minutes, 3 = set alarm hours, 4 = set alarm minutes
bool lastModeBtnState = HIGH;
bool lastHourBtnState = HIGH;
bool lastMinBtnState = HIGH;
unsigned long modeBtnDebounceTime = 0;
unsigned long hourBtnDebounceTime = 0;
unsigned long minBtnDebounceTime = 0;
const unsigned long DEBOUNCE_DELAY = 50; // 50 ms debounce
// Forward declarations
void updateDisplay();
void setShiftRegisterOutput(byte digitMask, byte segmentsPattern);
void setup() {
// Pin modes
pinMode(LATCH_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(DATA_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(MODE_BTN, INPUT_PULLUP);
pinMode(HOUR_BTN, INPUT_PULLUP);
pinMode(MIN_BTN, INPUT_PULLUP);
// Initialize shift register outputs to 0 (all segments off, all digits off)
setShiftRegisterOutput(0xFF, 0xFF); // digit mask 0xFF (no digit active, since PNP active low), segments 0xFF (all off)
}
void loop() {
unsigned long currentMillis = millis();
// --- Timekeeping using millis() ---
if (currentMillis - millisAtLastSecond >= 1000) {
// One second has passed
millisAtLastSecond += 1000;
seconds++;
if (seconds >= 60) {
seconds = 0;
minutes++;
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) {
hours = 0;
}
}
}
// Manage AM/PM flag
if (hours == 12) {
// when hours rolls to 12, toggle AM/PM (0->12 means it's noon, 11->12 means PM flips; 23->0 means midnight flips)
isPM = !isPM;
} else if (hours == 0) {
// 24-hour 0 = 12 AM
isPM = false;
}
// Check alarm
if (alarmEnabled && hours == alarmHour && minutes == alarmMin && seconds == 0) {
// Trigger alarm at the exact match (on the minute)
alarmTriggered = true;
tone(BUZZER_PIN, 1000); // start alarm tone at 1 kHz
}
}
// --- Handle buttons (debounced) ---
// Read raw states
bool modeBtnState = digitalRead(MODE_BTN);
bool hourBtnState = digitalRead(HOUR_BTN);
bool minBtnState = digitalRead(MIN_BTN);
// Mode button (cycle through modes)
if (modeBtnState != lastModeBtnState) {
modeBtnDebounceTime = currentMillis;
lastModeBtnState = modeBtnState;
}
if ((currentMillis - modeBtnDebounceTime) > DEBOUNCE_DELAY) {
if (lastModeBtnState == LOW) { // button pressed
// cycle mode on press (falling edge)
mode = (mode + 1) % 5; // cycles 0->1->2->3->4->back to 0
// If returning to normal mode, we could save or do something if needed
}
}
// Hour button (increment hour in either time set or alarm set mode)
if (hourBtnState != lastHourBtnState) {
hourBtnDebounceTime = currentMillis;
lastHourBtnState = hourBtnState;
}
if ((currentMillis - hourBtnDebounceTime) > DEBOUNCE_DELAY) {
if (lastHourBtnState == LOW) { // pressed
if (mode == 1) { // setting hours (current time)
hours = (hours + 1) % 24;
} else if (mode == 3) { // setting alarm hours
alarmHour = (alarmHour + 1) % 24;
}
// If in 12h display mode, we will convert when displaying, but internal is 24h
}
}
// Minute button (increment minute for time or alarm)
if (minBtnState != lastMinBtnState) {
minBtnDebounceTime = currentMillis;
lastMinBtnState = minBtnState;
}
if ((currentMillis - minBtnDebounceTime) > DEBOUNCE_DELAY) {
if (lastMinBtnState == LOW) { // pressed
if (mode == 2) { // setting minutes (current time)
minutes = (minutes + 1) % 60;
} else if (mode == 4) { // setting alarm minutes
alarmMin = (alarmMin + 1) % 60;
}
}
}
// If alarm is ringing and any button is pressed, stop the alarm
if (alarmTriggered && (lastModeBtnState == LOW || lastHourBtnState == LOW || lastMinBtnState == LOW)) {
noTone(BUZZER_PIN);
alarmTriggered = false;
}
// --- Update display (multiplex) ---
updateDisplay(); // handles refreshing the 4-digit display via multiplexing
}
// This function handles multiplexing the 4-digit display
void updateDisplay() {
static byte currentDigit = 0; // which digit we are currently displaying (0-3 for D1-D4)
static unsigned long lastRefresh = 0;
const unsigned long REFRESH_INTERVAL = 2; // refresh each digit every 2ms (gives ~500 Hz multiplex rate / 125Hz per digit)
unsigned long now = millis();
if (now - lastRefresh < REFRESH_INTERVAL) {
return; // not time to update yet
}
lastRefresh = now;
// Determine the value to display on this digit
int displayHour = hours;
bool pmIndicator = isPM;
// For 12-hour format display:
int hour12 = displayHour % 12;
if (hour12 == 0) hour12 = 12; // display 12 instead of 0
// We will display hour12 on D1 and D2 (with leading 0 blanked out), and minutes on D3, D4.
byte segmentsByte;
byte digitMask = 0xFF; // all high (no digit on by default, PNP off)
// Select which digit to update this cycle (currentDigit cycles 0-3 corresponding to D1..D4)
switch (currentDigit) {
case 0: // D1 (tens of hour)
// tens of hour (if hour is 7, tens digit is blank, we can display as blank or 0 depending on preference)
if (hour12 >= 10) {
int tens = hour12 / 10;
segmentsByte = SEGMENT_PATTERNS[tens];
} else {
// If hour < 10, turn off all segments (blank) or optionally display 0 if you want leading zero
segmentsByte = B11111111; // all segments off (blank)
}
// If in a time-setting mode, we can blink this digit to indicate it's being set:
if (mode == 1) {
// Blink the hour digits when setting current time hours
static bool blinkOn = true;
if ((millis() % 500) < 250) { // 2Hz blink
segmentsByte = B11111111; // half the time blank it
}
} else if (mode == 3) {
// Blink the alarm hour if in alarm hour set mode
if ((millis() % 500) < 250) {
segmentsByte = B11111111;
}
}
digitMask = ~(1 << 0); // activate digit 0 (D1) -> ~(00000001) = 0xFE for PNP logic active low
break;
case 1: // D2 (ones of hour)
segmentsByte = SEGMENT_PATTERNS[hour12 % 10];
if (mode == 1) {
// Blink hour ones too during setting
if ((millis() % 500) < 250) segmentsByte = B11111111;
} else if (mode == 3) {
// Blink alarm hour ones
if ((millis() % 500) < 250) segmentsByte = B11111111;
}
digitMask = ~(1 << 1); // activate D2 only
break;
case 2: // D3 (tens of minute)
segmentsByte = SEGMENT_PATTERNS[minutes / 10];
if (mode == 2) {
// Blink minute tens if setting current minutes
if ((millis() % 500) < 250) segmentsByte = B11111111;
} else if (mode == 4) {
// Blink alarm minute tens
if ((millis() % 500) < 250) segmentsByte = B11111111;
}
digitMask = ~(1 << 2); // activate D3
break;
case 3: // D4 (ones of minute)
segmentsByte = SEGMENT_PATTERNS[minutes % 10];
if (mode == 2) {
// Blink minute ones during setting
if ((millis() % 500) < 250) segmentsByte = B11111111;
} else if (mode == 4) {
// Blink alarm minute ones
if ((millis() % 500) < 250) segmentsByte = B11111111;
}
digitMask = ~(1 << 3); // activate D4
break;
}
// If alarm is currently ringing, we can also flash the display or the AM/PM LED as a visual indicator (optional)
// e.g., if (alarmTriggered) { segmentsByte = (millis() % 200 < 100) ? segmentsByte : B11111111; }
// Set the shift register outputs for this digit:
setShiftRegisterOutput(digitMask, segmentsByte);
// Move to next digit for next refresh cycle
currentDigit = (currentDigit + 1) % 4;
}
// Helper: Update the two shift registers (digit control and segments) and latch the output
void setShiftRegisterOutput(byte digitMask, byte segmentsPattern) {
// digitMask: bit mask for digit drivers (active low for PNP transistors)
// segmentsPattern: pattern for segments (active low for segments that should light)
digitalWrite(LATCH_PIN, LOW);
// Send the digit control byte (plus PM LED bit). Here we also want to include the PM indicator state on one of the upper bits.
// Assume we wired PM LED to IC1 QE (which corresponds to bit4 of the digit register byte).
byte digitByte = digitMask;
// Set the PM indicator bit (bit4) in digitByte:
if (isPM) {
// PM LED on -> for common anode LED connected to shift reg output, assume it lights when output is LOW (like segments)
// If we wired LED anode to 5V and cathode to shift reg, then LOW output lights it.
digitByte &= ~(1 << 4); // set bit4 to 0 (active low to turn LED on)
} else {
digitByte |= (1 << 4); // set bit4 to 1 (LED off)
}
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, digitByte);
// Send the segments byte second
shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, segmentsPattern);
digitalWrite(LATCH_PIN, HIGH);
}
