My new rev 3 of the clock pcb works fine. So now I have a separate pcb do do my testing on.
waiting for parts to upgrade the amp that feeds the speaker.
the arduino uno rev 3 pcb compiled and uploaded the softeware downloaded from the site.The other smd arduino pcb didn’t run at all because it couldn’t find a com port.
The software works like exactly like my original arduino uno rev 3 software.
now comes the fun. There is a guy named Rick in the states that built a clock similar to mine, it it has a remote. I have a copy of his clock software on my pc. I just have to find it and try it on my test unit.
below is my new test clock unit.
I will let you know about my progress, and if let you know how I make out. And if I need help.
The old man education continues.
Lee
if we can get this code to compile, I can test it on my test clock. If it works, setting clock & the remote works, I would like to add my chime code to it or add the emote code from this one to my code..
/*
Lee's Clock: Hopefully the final revision.
In order to use the buttons:
Hold 'set' to adjust the 'minutes' and 'brightness'.
Press 'set' to change between twelve and twenty four hour time.
As of right now the 'hours' button doesn't work so i forced the minutes to cycle to the next hour.
If you hold any of the first three buttons; it will begin to add faster.
Set the brightness first and then minutes because holding 'set' pauses the clock.
Make the minutes one higher than the current time and let go of the 'set' button when the time on the standard you are using hits zero seconds.
I advise against putting any battery on the RTC module as it will consistently overcharge
*/
#include <Wire.h>
#include <RTClib.h>
RTC_DS3231 rtc;
// Shift Register Hardware Pin Definitions
const int reg_clock = 13; // SRCLK
const int reg_latch = 10; // RCLK
const int reg_data = 11; // SER (Data In)
const int reg_OE = 9; // OE (Output Enable - Hardware PWM Control)
// Exact physical pinout matching your custom PCB panel traces
const int btn_bright = 2; // J6 Pin 5 -> Arduino Pin 2
const int btn_min = 3; // J6 Pin 4 -> Arduino Pin 3 (Minutes Button with Hour Rollover)
const int btn_set = 5; // J6 Pin 2 -> Arduino Pin 5 (Set Button)
// Common Anode segment patterns (1 = Segment ON / Sink to GND)
const byte digitPatterns[] = {
0b01111110, // 0
0b00110000, // 1
0b01101101, // 2
0b01111001, // 3
0b00110011, // 4
0b01011011, // 5
0b01011111, // 6
0b01110000, // 7
0b01111111, // 8
0b01111011 // 9
};
// Global Time variables
int hours = 12, minutes = 14, seconds = 0; // Pre-set to 12:14:00 for easy testing
bool is24Hour = false;
// Chime Flags
bool chimeDoneThisMinute = false;
int lastCheckedMinute = -1;
// Asynchronous Chime Engine State Variables (Zero Delays)
bool introMelodyActive = false;
unsigned long chimeStateTimer = 0;
int strikeCount = 0;
int totalStrikesNeeded = 0;
// Your exact custom active-low brightness PWM levels
const int brightnessPWM[] = {255, 192, 128, 64, 0};
int currentBrightStage = 2;
// Non-blocking button and chime tracking state counters
int setHoldCounter = 0;
int minHoldCounter = 0;
int rtcRefreshTimer = 0;
bool setModeActive = false;
// Tracking variables to prevent display lag and electrical noise
int lastDispSec = -1, lastDispMin = -1, lastDispHr = -1;
void updateDisplayRouting() {
int displayHours = hours;
bool isPM = (displayHours >= 12);
if (!is24Hour) {
displayHours = displayHours % 12;
if (displayHours == 0) displayHours = 12;
}
int onesSec = seconds % 10;
int tensSec = seconds / 10;
int onesMin = minutes % 10;
int tensMin = minutes / 10;
int onesHr = displayHours % 10;
int tensHr = displayHours / 10;
byte seg0 = digitPatterns[onesSec];
byte seg1 = digitPatterns[tensSec];
byte seg2 = (tensHr == 0) ? 0b00000000 : digitPatterns[tensHr];
byte seg3 = digitPatterns[onesHr];
byte seg4 = digitPatterns[tensMin];
byte seg5 = digitPatterns[onesMin];
if (setModeActive) {
seg2 |= 0b10000000;
seg3 |= 0b10000000;
}
if (!is24Hour && isPM) {
seg5 |= 0b10000000;
}
digitalWrite(reg_latch, LOW);
shiftOut(reg_data, reg_clock, MSBFIRST, seg5);
shiftOut(reg_data, reg_clock, MSBFIRST, seg4);
shiftOut(reg_data, reg_clock, MSBFIRST, seg3);
shiftOut(reg_data, reg_clock, MSBFIRST, seg2);
shiftOut(reg_data, reg_clock, MSBFIRST, seg1);
shiftOut(reg_data, reg_clock, MSBFIRST, seg0);
digitalWrite(reg_latch, HIGH);
}
void readRTC() {
DateTime now = rtc.now();
hours = now.hour();
minutes = now.minute();
seconds = now.second();
}
// RESTORED: Uses Native Hardware Serial for your pins 0 and 1 connection
void triggerTrack(byte trackNumber) {
Serial.write((uint8_t)'t');
delayMicroseconds(50);
Serial.write((uint8_t)trackNumber);
}
void checkChimesAsynchronous() {
if (minutes != lastCheckedMinute) {
chimeDoneThisMinute = false;
lastCheckedMinute = minutes;
}
if (!chimeDoneThisMinute) {
if (minutes == 15) {
triggerTrack(1);
chimeDoneThisMinute = true;
}
else if (minutes == 30) {
triggerTrack(2);
chimeDoneThisMinute = true;
}
else if (minutes == 45) {
triggerTrack(3);
chimeDoneThisMinute = true;
}
else if (minutes == 0) {
triggerTrack(4); // Start hourly intro melody immediately
introMelodyActive = true;
chimeStateTimer = millis();
totalStrikesNeeded = hours % 12;
if (totalStrikesNeeded == 0) totalStrikesNeeded = 12;
strikeCount = 0;
chimeDoneThisMinute = true;
}
}
if (introMelodyActive) {
unsigned long elapsed = millis() - chimeStateTimer;
// Step 1: Wait out the 26-second intro song
if (strikeCount == 0 && elapsed >= 26000) {
triggerTrack(5); // First hour strike
strikeCount = 1;
chimeStateTimer = millis();
}
// Step 2: Cycle through individual hourly strikes every 3 seconds
else if (strikeCount > 0 && strikeCount < totalStrikesNeeded && elapsed >= 3000) {
triggerTrack(5); // Consecutive hour strike
strikeCount++;
chimeStateTimer = millis();
}
// Step 3: Turn off tracking engine when total count completes
else if (strikeCount >= totalStrikesNeeded) {
introMelodyActive = false;
}
}
}
void handleInputsNoMillis() {
bool setPressed = (digitalRead(btn_set) == LOW);
bool minPressed = (digitalRead(btn_min) == LOW);
bool brightPressed = (digitalRead(btn_bright) == LOW);
// --- SET BUTTON INITIAL PRESS AND MODE TRANSITION ---
if (setPressed) {
if (!setModeActive) {
setHoldCounter++;
if (setHoldCounter > 30) { // ~600ms hold requirement reached
setModeActive = true;
}
}
if (setModeActive) {
// 1. Brightness Toggle
if (brightPressed) {
currentBrightStage = (currentBrightStage + 1) % 5;
analogWrite(reg_OE, brightnessPWM[currentBrightStage]);
delay(250); // Small mechanical debounce
}
// 2. Minutes Step with Automatic Hour Rollover
if (minPressed) {
minHoldCounter++;
// Advance instantly on first press. If held past 25 frames (~500ms), scroll every 6 frames (~120ms)
if (minHoldCounter == 1 || (minHoldCounter > 25 && minHoldCounter % 6 == 0)) {
seconds = 0;
minutes++;
// Rollover minutes into hours smoothly
if (minutes >= 60) {
minutes = 0;
hours++;
if (hours >= 24) hours = 0;
}
updateDisplayRouting();
}
} else {
minHoldCounter = 0; // Reset scroll acceleration instantly on button release
}
}
}
else {
// --- SET BUTTON RELEASE DETECTED ---
if (setHoldCounter > 0) {
if (setModeActive) {
setModeActive = false;
rtc.adjust(DateTime(2026, 8, 13, hours, minutes, 0)); // Save strictly on release
}
else if (setHoldCounter > 1 && setHoldCounter <= 30) {
is24Hour = !is24Hour; // Short tap changes 12/24 mode formatting
}
setHoldCounter = 0;
minHoldCounter = 0;
updateDisplayRouting();
}
}
}
void setup() {
pinMode(reg_clock, OUTPUT); pinMode(reg_latch, OUTPUT); pinMode(reg_data, OUTPUT); pinMode(reg_OE, OUTPUT);
analogWrite(reg_OE, brightnessPWM[currentBrightStage]);
pinMode(btn_bright, INPUT_PULLUP);
pinMode(btn_min, INPUT_PULLUP);
pinMode(btn_set, INPUT_PULLUP);
Wire.begin();
Serial.begin(38400); // Start native Hardware Serial on pins 0/1 at 38400 baud
if (!rtc.begin()) {
while (1);
}
// TESTING FEATURE: Forces the RTC memory directly to 12:14:00 on every boot/reset
// so you can instantly verify the 12:15:00 chime sound!
rtc.adjust(DateTime(2026, 8, 13, 12, 14, 0));
readRTC();
updateDisplayRouting();
}
void loop() {
handleInputsNoMillis();
if (!setModeActive) {
checkChimesAsynchronous();
// Query the RTC hardware via I2C once every 10 loops (~200ms)
rtcRefreshTimer++;
if (rtcRefreshTimer >= 10) {
readRTC();
rtcRefreshTimer = 0;
}
}
// Conditional refresh gate blocks high-frequency trace noise from locking up buttons
if (seconds != lastDispSec || minutes != lastDispMin || hours != lastDispHr || setModeActive) {
updateDisplayRouting();
lastDispSec = seconds;
lastDispMin = minutes;
lastDispHr = hours;
}
delay(20);
M
Digit Clock using TPIC6B595 Shift Registers with Menu System and Temperature/Date Display
Modified to include Menu System for setting date and time via IR Remote, temperature, and date display
Original by Adrian Smith with adjustments by Rick Gast, further modified for Menu System and Temp/Date Display
Fixed button release detection to work with Button Library
Leftmost digit is digit 0
7-segment digits 0-9 {Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7} --> {A, B, C, D, E, F, G, DP}
Data fed to rightmost register first, so digit 0 shifts out first
Uses IRremote Library 3.8.0 or later for Remote Control
Button 1 (IR: 0xF807FF00): Enter/Exit Set Mode or move to next menu item [Rewind]
Button 2 (IR: 0xBF40FF00): Increment value (in Set Mode) [+]
Button 3 (IR: 0xE619FF00): Decrement value (in Set Mode) [-]
Button 4 (IR: 0xF609FF00): Toggle 12/24 Hour Mode (Short press, outside Set/Temp Modes) or toggle °C/°F (Short press, in Temp Mode) [Forward]
IR: 0xBB44FF00: Toggle Temperature Display (Short press, outside Set Mode)[Test]
IR: 0xBC43FF00: Toggle Date Display (Short press, outside Set Mode)[Return]
IR: 0xB847FF00: Set Display Brightness (Does not overide Night Dim Setting) [Menu]
IR: 0xBA45FF00: Display Test [Power]
Version 1.9.2 - Added separate IR keys for Date and Temperature Displays. Added 8 second timeout. Added Brightness Control via IR
Version 1.9.3 - Added Display Test thru IR
To add IR hardware to the clock, choose the IR Receiver module or component of your liking,
then connect 3 pins to the Arduino as follows: Vcc and Gnd are self explanatory. Connect the signal pin to digital pin 6.
*/
#define DS3231_I2C_ADDR 0x68
#define DS3231_TEMPERATURE_ADDR 0x11
#include <SPI.h>
#include <Wire.h>
#include <RTCIRremote.hpp>lib.h>
#include <
#include <Button.h> // Button library by Alexander Brevig
const int reg_clock = 13; // SRCK
const int reg_latch = 10; // RCLK
const int reg_data = 11; // SER_IN
const int reg_OE = 9; // Output enable; Active LOW for brightness PWM
const int colon_pin = 8; // Colon LEDs dimming control
const int RECV_PIN = 6; // IR receiver pin
unsigned long key_value = 0;
uint32_t lastKey = 0; // Stores last non-repeat IR key
Button button1 = Button(2, BUTTON_PULLUP_INTERNAL); // Set mode
Button button2 = Button(3, BUTTON_PULLUP_INTERNAL); // Toggle date display or increment/navigate up
Button button3 = Button(4, BUTTON_PULLUP_INTERNAL); // Toggle temp display or decrement/navigate down
Button button4 = Button(5, BUTTON_PULLUP_INTERNAL); // 12/24 hour or °C/°F toggle
int brightness = 2; // Display brightness (1-5, 4 was too bright)
int brt = 0; // Reversed PWM percentage
int set_mode = 0; // 0: normal, 1: set mode
int twelveH_mode = 1; // 1: 12-hour mode (default), 0: 24-hour mode
int PM = 0; // PM indicator for 12-hour mode
bool temp_mode = false; // Flag for temperature display mode
bool date_mode = false; // Flag for date display mode
bool dispTest_mode = false; // Flag for display test
int temp_unit = 0; // 0: Celsius (default), 1: Fahrenheit
static unsigned long lastIRTime = 0; // Moved to global for consistent IR debouncing
const unsigned long IR_DEBOUNCE = 750; // Increased to 750ms to reduce intermittent triggers
static int previous_mode = 1; // 0: clock, 1: 12-hour mode
static unsigned long temp_display_start = 0; // Time when temp mode starts
static unsigned long date_display_start = 0; // Time when date mode starts
static unsigned long dispTest_mode_start = 0; // time when display test starts
const unsigned long DISPLAY_TIMEOUT = 8000; // 8 seconds
int menu_state = 0; // 0: Year, 1: Month, 2: Day, 3: Hour, 4: Minute
int set_year = 0;
int set_month = 0;
int set_day = 0;
int set_hour = 0;
int set_min = 0;
RTC_DS3231 rtc;
byte ssddigits[10] = // 7-segment digits, DP off
{
B11111100, // 0
B01100000, // 1
B11011010, // 2
B11110010, // 3
B01100110, // 4
B10110110, // 5
B10111110, // 6
B11100000, // 7
B11111110, // 8
B11110110 // 9
};
byte ssddigitsDP[10] = // 7-segment digits, DP on
{
B11111101, // 0
B01100001, // 1
B11011011, // 2
B11110011, // 3
B01100111, // 4
B10110111, // 5
B10111111, // 6
B11100001, // 7
B11111111, // 8
B11110111 // 9
};
byte tempdigits[2] = // C and F with degrees symbol
{
B10011101, // °C
B10001111 // °F
};
byte menuDisplay[5][3] = // Menu indicators: "Yr", "Mo", "dA", "Hr", "Mn"
{
{B01110110, B00001010, B00000000}, // Yr
{B11001100, B11100100, B00111010}, // Mo
{B01111010, B11101110, B00000000}, // dA
{B01101110, B00001010, B00000000}, // Hr
{B11001100, B11100100, B00101010} // Mn
};
void setup()
{
IrReceiver.begin(RECV_PIN, DISABLE_LED_FEEDBACK);
pinMode(reg_clock, OUTPUT);
pinMode(reg_latch, OUTPUT);
pinMode(reg_data, OUTPUT);
pinMode(reg_OE, OUTPUT);
pinMode(colon_pin, OUTPUT);
digitalWrite(reg_OE, LOW);
digitalWrite(colon_pin, HIGH);
setBrt();
uint8_t duty = map(brt, 0, 100, 0, 255);
analogWrite(reg_OE, duty);
SPI.begin();
displayTest();
delay(3000);
Wire.begin();
rtc.begin();
set_mode = 0;
twelveH_mode = 1;
temp_unit = 1; // Default to Fahrenheit
previous_mode = 1; // Default to 12-hour mode
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Uncomment to set initial time
}
void loop()
{
if (set_mode == 1)
{
setTime();
}
else if (temp_mode)
{
tempDisplay();
}
else if (dispTest_mode)
{
displayTest();
}
else if (date_mode)
{
dateDisplay();
}
else if (twelveH_mode)
{
twelve_hourMode();
previous_mode = 1;
}
else
{
clockDisplay();
previous_mode = 0;
}
checkButtons();
checkIRinput();
setBrt();
uint8_t duty = map(brt, 0, 100, 0, 255);
analogWrite(reg_OE, duty);
}
// *** Functions *** //
void clockDisplay()
{
DateTime now = rtc.now();
int hours = now.hour();
int minutes = now.minute();
int seconds = now.second();
int h1 = hours / 10;
int h2 = hours % 10;
int m1 = minutes / 10;
int m2 = minutes % 10;
int s1 = seconds / 10;
int s2 = seconds % 10;
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
if (hours <= 9)
{
SPI.transfer(ssddigits[h1]);
SPI.transfer(ssddigitsDP[h2]);
SPI.transfer(ssddigitsDP[m1]);
SPI.transfer(ssddigitsDP[m2]);
SPI.transfer(ssddigitsDP[s1]);
SPI.transfer(ssddigits[s2]);
}
else
{
SPI.transfer(ssddigits[h1]);
SPI.transfer(ssddigitsDP[h2]);
SPI.transfer(ssddigitsDP[m1]);
SPI.transfer(ssddigitsDP[m2]);
SPI.transfer(ssddigitsDP[s1]);
SPI.transfer(ssddigits[s2]);
}
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
if (now.hour() == 23 && now.minute() == 0 && now.second() == 0)
{
brightness = 1;
digitalWrite(colon_pin, LOW);
}
if (now.hour() == 8 && now.minute() == 0 && now.second() == 0)
{
brightness = 2;
digitalWrite(colon_pin, HIGH);
}
}
void twelve_hourMode()
{
DateTime now = rtc.now();
PM = (now.hour() >= 12) ? 1 : 0;
int hours = now.hour() % 12;
if (hours == 0) hours = 12;
int minutes = now.minute();
int seconds = now.second();
int h1 = hours / 10;
int h2 = hours % 10;
int m1 = minutes / 10;
int m2 = minutes % 10;
int s1 = seconds / 10;
int s2 = seconds % 10;
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
if (hours <= 9 && PM == 0)
{
SPI.transfer(B00000000);
SPI.transfer(ssddigitsDP[h2]);
SPI.transfer(ssddigitsDP[m1]);
SPI.transfer(ssddigitsDP[m2]);
SPI.transfer(ssddigitsDP[s1]);
SPI.transfer(ssddigits[s2]);
}
else if (hours <= 9 && PM == 1)
{
SPI.transfer(B00000001);
SPI.transfer(ssddigitsDP[h2]);
SPI.transfer(ssddigitsDP[m1]);
SPI.transfer(ssddigitsDP[m2]);
SPI.transfer(ssddigitsDP[s1]);
SPI.transfer(ssddigits[s2]);
}
else if (hours >= 10 && PM == 0)
{
SPI.transfer(ssddigits[h1]);
SPI.transfer(ssddigitsDP[h2]);
SPI.transfer(ssddigitsDP[m1]);
SPI.transfer(ssddigitsDP[m2]);
SPI.transfer(ssddigitsDP[s1]);
SPI.transfer(ssddigits[s2]);
}
else
{
SPI.transfer(ssddigitsDP[h1]);
SPI.transfer(ssddigitsDP[h2]);
SPI.transfer(ssddigitsDP[m1]);
SPI.transfer(ssddigitsDP[m2]);
SPI.transfer(ssddigitsDP[s1]);
SPI.transfer(ssddigits[s2]);
}
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
if (now.hour() == 23 && now.minute() == 0 && now.second() == 0)
{
brightness = 1;
digitalWrite(colon_pin, LOW);
}
if (now.hour() == 8 && now.minute() == 0 && now.second() == 0)
{
brightness = 2;
digitalWrite(colon_pin, HIGH);
}
}
void setTime()
{
//checkIRinput(); // Commented out, not needed with shared variables
DateTime now = rtc.now();
if (menu_state == 0 && set_mode == 1)
{
set_year = now.year();
set_month = now.month();
set_day = now.day();
set_hour = now.hour();
set_min = now.minute();
}
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
switch (menu_state)
{
case 0: // Year
{
int y1 = (set_year / 1000) % 10;
int y2 = (set_year / 100) % 10;
int y3 = (set_year / 10) % 10;
int y4 = set_year % 10;
SPI.transfer(ssddigits[y1]);
SPI.transfer(ssddigits[y2]);
SPI.transfer(ssddigits[y3]);
SPI.transfer(ssddigits[y4]);
SPI.transfer(menuDisplay[0][0]); // Y
SPI.transfer(menuDisplay[0][1]); // r
break;
}
case 1: // Month
{
int m1 = set_month / 10;
int m2 = set_month % 10;
SPI.transfer(ssddigits[m1]);
SPI.transfer(ssddigits[m2]);
SPI.transfer(B00000000);
SPI.transfer(menuDisplay[1][0]); // M
SPI.transfer(menuDisplay[1][1]); // M
SPI.transfer(menuDisplay[1][2]); // o
//Serial.print(m1);
//Serial.print(m2);
break;
}
case 2: // Day
{
int d1 = set_day / 10;
int d2 = set_day % 10;
SPI.transfer(ssddigits[d1]);
SPI.transfer(ssddigits[d2]);
SPI.transfer(B00000000);
SPI.transfer(B00000000);
SPI.transfer(menuDisplay[2][0]); // d
SPI.transfer(menuDisplay[2][1]); // A
break;
}
case 3: // Hour
{
int h1 = set_hour / 10;
int h2 = set_hour % 10;
SPI.transfer(h1 == 0 ? B00000000 : ssddigits[h1]);
SPI.transfer(ssddigits[h2]);
SPI.transfer(B00000000);
SPI.transfer(B00000000);
SPI.transfer(menuDisplay[3][0]); // H
SPI.transfer(menuDisplay[3][1]); // r
break;
}
case 4: // Minute
{
int m1 = set_min / 10;
int m2 = set_min % 10;
SPI.transfer(ssddigits[m1]);
SPI.transfer(ssddigits[m2]);
SPI.transfer(B00000000);
SPI.transfer(menuDisplay[4][0]); // M
SPI.transfer(menuDisplay[4][1]); // M
SPI.transfer(menuDisplay[4][2]); // n
break;
}
}
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
if (button2.uniquePress())
{
switch (menu_state)
{
case 0: set_year = (set_year < 2099) ? set_year + 1 : 2000; break;
case 1: set_month = (set_month < 12) ? set_month + 1 : 1; break;
case 2: set_day = (set_day < 31) ? set_day + 1 : 1; break;
case 3: set_hour = (set_hour < 23) ? set_hour + 1 : 0; break;
case 4: set_min = (set_min < 59) ? set_min + 1 : 0; break;
}
delay(10);
rtc.adjust(DateTime(set_year, set_month, set_day, set_hour, set_min, 0));
}
if (button3.uniquePress())
{
switch (menu_state)
{
case 0: set_year = (set_year > 2000) ? set_year - 1 : 2099; break;
case 1: set_month = (set_month > 1) ? set_month - 1 : 12; break;
case 2: set_day = (set_day > 1) ? set_day - 1 : 31; break;
case 3: set_hour = (set_hour > 0) ? set_hour - 1 : 23; break;
case 4: set_min = (set_min > 0) ? set_min - 1 : 59; break;
}
delay(10);
rtc.adjust(DateTime(set_year, set_month, set_day, set_hour, set_min, 0));
}
if (button1.uniquePress())
{
if (menu_state < 4)
{
menu_state++;
}
else
{
// Use user-adjusted values, reset seconds to 0
rtc.adjust(DateTime(set_year, set_month, set_day, set_hour, set_min, 0));
set_mode = 0;
menu_state = 0;
key_value = 0;
temp_mode = false; // Ensure temp mode off
date_mode = false; // Ensure date mode off
temp_display_start = 0; // Reset timeout
date_display_start = 0; // Reset timeout
if (twelveH_mode)
twelve_hourMode();
else
clockDisplay();
return;
}
}
}
void setBrt()
{
if (brightness == 1) brt = 92;
else if (brightness == 2) brt = 80;
else if (brightness == 3) brt = 50;
else if (brightness == 4) brt = 30;
else if (brightness == 5) brt = 0;
}
void dateDisplay()
{
DateTime now = rtc.now();
int month = now.month();
int day = now.day();
int year = now.year() % 100; // Last two digits of year
int m1 = month / 10;
int m2 = month % 10;
int d1 = day / 10;
int d2 = day % 10;
int y1 = year / 10;
int y2 = year % 10;
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
// SPI.transfer(m1 == 0 ? B00000000 : ssddigits[m1]);
SPI.transfer(ssddigits[m1]);
SPI.transfer(ssddigitsDP[m2]); // Decimal point after month
// SPI.transfer(d1 == 0 ? B00000000 : ssddigits[d1]);
SPI.transfer(ssddigits[d1]);
SPI.transfer(ssddigitsDP[d2]); // Decimal point after day
// SPI.transfer(y1 == 0 ? B00000000 : ssddigits[y1]);
SPI.transfer(ssddigits[y1]);
SPI.transfer(ssddigits[y2]);
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
// Check for 8-second timeout
if (millis() - date_display_start >= DISPLAY_TIMEOUT)
{
date_mode = false;
date_display_start = 0;
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
}
void tempDisplay()
{
// Read temperature from DS3231
Wire.beginTransmission(DS3231_I2C_ADDR);
Wire.write(DS3231_TEMPERATURE_ADDR);
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDR, 2);
uint8_t temp_msb = Wire.read();
uint8_t temp_lsb = Wire.read(); // Read but ignore fractional part
int temp_display;
// int offset = -6; // replace the 6 with the number of degrees C it is out (allow 1 hour warm up time)
if (temp_unit == 1) // Fahrenheit
{
temp_display = (temp_msb * 9) / 5 + 32; // Convert to Fahrenheit
}
else // Celsius
{
// temp_display = temp_msb + offset; // Use integer part only
temp_display = temp_msb; // Use integer part only
}
int t1 = temp_display / 10;
int t2 = temp_display % 10;
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
SPI.transfer(B00000000); // blank digit
SPI.transfer(B00000000); // blank digit
SPI.transfer(t1 == 0 ? B00000000 : ssddigits[t1]);
SPI.transfer(ssddigits[t2]);
// SPI.transfer(B11000110); // display °
SPI.transfer(tempdigits[temp_unit]); // °C or °F
SPI.transfer(B00000000); // blank digit
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
// Check for 8-second timeout
if (millis() - temp_display_start >= DISPLAY_TIMEOUT)
{
temp_mode = false;
temp_display_start = 0;
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
}
void displayTest()
{
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
SPI.transfer(B11111111);
SPI.transfer(B11111111);
SPI.transfer(B11111111);
SPI.transfer(B11111111);
SPI.transfer(B11111111);
SPI.transfer(B11111111);
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
// Check for 8 second timeout
if (millis() - dispTest_mode_start >= DISPLAY_TIMEOUT)
{
dispTest_mode = false;
dispTest_mode_start = 0;
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
}
void checkButtons()
{
// Handle button 1: Enter set mode only when not in set mode
if (button1.uniquePress() && !set_mode)
{
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
SPI.transfer(B00000000); // Blank hour 10s
SPI.transfer(B00000000); // Blank hour 1s
SPI.transfer(B00000010); // -
SPI.transfer(B00000010); // -
SPI.transfer(B00000000); // Blank seconds 10s
SPI.transfer(B00000000); // Blank seconds 1s
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
delay(2000);
set_mode = 1;
temp_mode = false; // Ensure temp mode is off
date_mode = false; // Ensure date mode is off
temp_display_start = 0; // Reset timeout
date_display_start = 0; // Reset timeout
}
// Handle button 2: Toggle date display (outside set mode)
if (button2.uniquePress() && !set_mode)
{
date_mode = !date_mode;
if (date_mode)
{
temp_mode = false; // Ensure temp mode is off
temp_display_start = 0; // Reset temp timeout
date_display_start = millis(); // Start date timeout
dateDisplay();
}
else
{
date_display_start = 0; // Reset date timeout
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
}
// Handle button 3: Toggle temperature display (outside set mode)
if (button3.uniquePress() && !set_mode)
{
temp_mode = !temp_mode;
if (temp_mode)
{
date_mode = false; // Ensure date mode is off
date_display_start = 0; // Reset date timeout
temp_display_start = millis(); // Start temp timeout
tempDisplay();
}
else
{
temp_display_start = 0; // Reset temp timeout
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
}
// Handle button 4: Toggle 12/24 hour mode (outside set/temp mode) or °C/°F (in temp mode)
if (button4.uniquePress() && !set_mode)
{
if (temp_mode)
{
temp_unit = !temp_unit; // Toggle °C/°F in temp mode
}
else
{
twelveH_mode = !twelveH_mode; // Toggle 12/24 hour mode
}
delay(400);
}
}
void checkIRinput()
{
if (set_mode == 1 && menu_state == 0)
{
DateTime now = rtc.now();
set_year = now.year();
set_month = now.month();
set_day = now.day();
set_hour = now.hour();
set_min = now.minute();
}
if (IrReceiver.decode())
{
unsigned long currentTime = millis();
unsigned long value = IrReceiver.decodedIRData.decodedRawData;
if (value == 0)
{
IrReceiver.resume();
return;
}
if (!(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) && (currentTime - lastIRTime > IR_DEBOUNCE))
{
if (set_mode == 1)
{
// Handle IR commands in set mode
switch (value)
{
case 0xF807FF00: // Advance menu state or exit
if (menu_state < 4)
{
menu_state++;
setTime(); // Update display for new menu state
}
else
{
rtc.adjust(DateTime(set_year, set_month, set_day, set_hour, set_min, 0));
set_mode = 0;
menu_state = 0;
key_value = 0;
temp_mode = false;
date_mode = false;
temp_display_start = 0;
date_display_start = 0;
if (twelveH_mode)
twelve_hourMode();
else
clockDisplay();
delay(100); // Allow display refresh
}
lastIRTime = currentTime;
break;
case 0xBF40FF00: // Increment value
switch (menu_state)
{
case 0: set_year = (set_year < 2099) ? set_year + 1 : 2000; break;
case 1: set_month = (set_month < 12) ? set_month + 1 : 1; break;
case 2: set_day = (set_day < 31) ? set_day + 1 : 1; break;
case 3: set_hour = (set_hour < 23) ? set_hour + 1 : 0; break;
case 4: set_min = (set_min < 59) ? set_min + 1 : 0; break;
}
delay(10);
rtc.adjust(DateTime(set_year, set_month, set_day, set_hour, set_min, 0));
setTime(); // Update display with new value
lastIRTime = currentTime;
break;
case 0xE619FF00: // Decrement value
switch (menu_state)
{
case 0: set_year = (set_year > 2000) ? set_year - 1 : 2099; break;
case 1: set_month = (set_month > 1) ? set_month - 1 : 12; break;
case 2: set_day = (set_day > 1) ? set_day - 1 : 31; break;
case 3: set_hour = (set_hour > 0) ? set_hour - 1 : 23; break;
case 4: set_min = (set_min > 0) ? set_min - 1 : 59; break;
}
delay(10);
rtc.adjust(DateTime(set_year, set_month, set_day, set_hour, set_min, 0));
setTime(); // Update display with new value
lastIRTime = currentTime;
break;
case 0xF609FF00: // Ignored in set mode
lastIRTime = currentTime;
break;
default:
lastIRTime = currentTime;
break;
}
}
else
{
// Handle IR commands outside set mode
switch (value)
{
case 0xF807FF00: // Enter set mode
SPI.beginTransaction(SPISettings(8000000, LSBFIRST, SPI_MODE0));
digitalWrite(reg_latch, LOW);
SPI.transfer(B00000000); // Blank hour 10s
SPI.transfer(B00000000); // Blank hour 1s
SPI.transfer(B00000010); // -
SPI.transfer(B00000010); // -
SPI.transfer(B00000000); // Blank seconds 10s
SPI.transfer(B00000000); // Blank seconds 1s
digitalWrite(reg_latch, HIGH);
SPI.endTransaction();
delay(2000);
set_mode = 1;
temp_mode = false;
date_mode = false;
temp_display_start = 0;
date_display_start = 0;
dispTest_mode_start = 0;
lastIRTime = currentTime;
break;
case 0xB847FF00: // set brightness
{
brightness++;
if (brightness == 6)
brightness = 1;
break;
}
case 0xF609FF00: // Toggle 12/24 hour or °C/°F
if (temp_mode)
{
temp_unit = !temp_unit;
}
else
{
twelveH_mode = !twelveH_mode;
}
lastIRTime = currentTime;
delay(400);
break;
case 0xBB44FF00: // Toggle temperature
temp_mode = !temp_mode;
if (temp_mode)
{
date_mode = false;
date_display_start = 0;
date_display_start = 0;
temp_display_start = millis();
tempDisplay();
}
else
{
temp_display_start = 0;
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
lastIRTime = currentTime;
break;
case 0xBC43FF00: // Toggle date
date_mode = !date_mode;
if (date_mode)
{
temp_mode = false;
temp_display_start = 0;
dispTest_mode_start = 0;
date_display_start = millis();
dateDisplay();
}
else
{
date_display_start = 0;
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
lastIRTime = currentTime;
break;
case 0xBA45FF00: // Toggle Display Test
dispTest_mode = !dispTest_mode;
if (dispTest_mode)
{
temp_mode = false;
date_mode = false;
temp_display_start = 0;
date_display_start = 0;
dispTest_mode_start = millis();
displayTest();
}
else
{
dispTest_mode_start = 0;
if (previous_mode == 1)
twelve_hourMode();
else
clockDisplay();
}
default:
lastIRTime = currentTime;
break;
}
}
// Clear IR data and resume receiver
IrReceiver.decodedIRData.decodedRawData = 0; // Clear IR data
key_value = 0; // Clear key_value
IrReceiver.resume(); // Prepare for next IR signal
}
else
{
IrReceiver.resume(); // Clear repeat or debounced signals
}
}
