Notifications
Clear all

Ardruino clock help 2

17 Posts
2 Users
2 Reactions
494 Views
(@lmitchel)
Member
Joined: 2 months ago
Posts: 13
Topic starter  

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

IMG 0092


   
Lee G reacted
ReplyQuote
(@lmitchel)
Member
Joined: 2 months ago
Posts: 13
Topic starter  

 

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
    }
  }


   
ReplyQuote
Page 2 / 2