/*
  WOW! Works great and much easier to program using shift registers!
  Nine pins to control hours, minutes and seconds. Leaves three pins plus
  analog pins for AM/PM or day of week indicator lights.
*/



#include <Wire.h>
#define DS1307_ADDR 0x68

/* - - - - - - - - - -
  Two ways to enter time and date.
  1. Set a value for each item, then compile and upload at the 'set value' time.
  2. Use __TIME__ to obtain the values from the system clock at time of compile.
*/
// dayofWeek 1,Monday 2,Tuesday 3,Wednesday 4,Thursday 5,Friday 6,Saturday 7,Sunday
uint8_t day = 6, date = 28, month = 12;

char time[] = __TIME__;
int UTC = atoi(&time[0]); // does not obtain hours from computer clock
int hours = UTC - 6; // UTC (GMT: 0) -> CST (GMT: -6)
int minutes = atoi(&time[3]);
int seconds = atoi(&time[6]);

int year = 24;
uint8_t hour; // AM or PM hour
uint8_t hoursAMPM;

uint8_t sLSB;
uint8_t sMSB;
uint8_t sVal;
uint8_t mLSB;
uint8_t mMSB;
uint8_t mVal;
uint8_t hLSB;
uint8_t hMSB;
uint8_t hVal;

// in this sketch, AM-PM is not used
bool isPM = false; // 0x02 Hour regester, Bit 5 is set low(0) for AM and set high(1) for PM
bool is12HourMode;

const int sClockPin = 13;
const int sLatchPin = 12;
const int sDataPin = 11;
const int mClockPin = 10;
const int mLatchPin = 9;
const int mDataPin = 8;
const int hClockPin = 7;
const int hLatchPin = 6;
const int hDataPin = 5;


void setup() {
  Serial.begin(115200);
  Wire.begin();

  pinMode(sClockPin, OUTPUT);
  pinMode(sLatchPin, OUTPUT);
  pinMode(sDataPin, OUTPUT);
  pinMode(mClockPin, OUTPUT);
  pinMode(mLatchPin, OUTPUT);
  pinMode(mDataPin, OUTPUT);
  pinMode(hClockPin, OUTPUT);
  pinMode(hLatchPin, OUTPUT);
  pinMode(hDataPin, OUTPUT);

  // - - - Wokwi DS1307 doesn't work with 12 hour mode - - -
  hoursAMPM = dectoBcd(hours); // 1st: set the hour, in this example 11 or (0001 + 0001) in BCD
  Serial.print("24hr mode: "); Serial.println(hoursAMPM, BIN);
  // 2nd: Set the DS3231 12-hour format bit settings
  // 0x02 Hour address, Bit 6 is set high(1) for 12 hour format or set low(0) for 24 hour format
  // - - - In this sketch, 12 hour mode is not selected - - -
  hoursAMPM = hoursAMPM | 0b00000000; // OR's 00010001 | 00000000 = (0001 + 0001)
  // 3rd: Is PM set to 'true'?
  // - - - In this sketch, AM-PM is not used - - -
  if (isPM) { // Bit 5 set high in 12 hour mode = PM
    hoursAMPM = hoursAMPM | 0b00000000; // OR's 01010001 | 00000000 = (0101 + 0001)
    Serial.print(hoursAMPM, BIN);
  }

  // Set-Time by writing to DS3231
  Wire.beginTransmission(DS1307_ADDR); // start communication using DS3231 hex address
  Wire.write(0x00); // write to register address 0x00 the seconds register
  Wire.write(dectoBcd(seconds)); // write seconds in BCD format, 0100(MSB) and 0111(LSB)
  Wire.write(dectoBcd(minutes));
  Wire.write(hoursAMPM); // Has already been converted to dectoBcd
  Wire.write(dectoBcd(day));
  Wire.write(dectoBcd(date));
  Wire.write(dectoBcd(month));
  Wire.write(dectoBcd(year));
  Wire.endTransmission();

}

void loop() {
  Wire.beginTransmission(DS1307_ADDR);
  Wire.write(0);  // Start reading from address 0 (seconds)
  Wire.endTransmission();
  Wire.requestFrom(DS1307_ADDR, 7);  // Request seconds, minutes, hours, day, date, month, year

  if (Wire.available()) { // Also works when 'if' statement removed
    seconds = bcdToDec(Wire.read() & 0x7F);  // Mask the high bit of seconds
    minutes = bcdToDec(Wire.read() & 0x7F);
    hoursAMPM = Wire.read();

    is12HourMode = hoursAMPM & 0x40;  // Check if DS3231 is in 12-hour mode
    if (is12HourMode) {
      Serial.println("12-hour");
      isPM = hoursAMPM & 0x20;  // Check if it's PM
      hour = bcdToDec(hoursAMPM & 0x1F);  // Mask the high three bits for 12-hour mode
    }
    else {
      Serial.println("24-hour");
      hour = bcdToDec(hoursAMPM & 0x3F);  // 24-hour mode, mask the high two bits
    }

    day = bcdToDec(Wire.read());
    date = bcdToDec(Wire.read());
    month = bcdToDec(Wire.read());
    year = 2000 + bcdToDec(Wire.read());

    sLSB = seconds % 10; // returns last digit of seconds
    sMSB = seconds / 10 << 4; // returns first digit of seconds and shifts left 4 places
    sVal = sMSB | sLSB; // using 'OR' returns value, 47 = 01000000 | 00000111 = 0100 0111
    mLSB = minutes % 10;
    mMSB = minutes / 10 << 4;
    mVal = mLSB | mMSB;
    hLSB = hour % 10;
    hMSB = hour / 10 << 4;
    hVal = hLSB | hMSB;

    // Seconds shift register output
    digitalWrite(sLatchPin, LOW); 
    shiftOut(sDataPin, sClockPin, MSBFIRST, sVal); // this commands dataPin to sync with clockPin, send most significant bit first.
    digitalWrite(sLatchPin, HIGH);

    // Minutes shift register output
    digitalWrite(mLatchPin, LOW); 
    shiftOut(mDataPin, mClockPin, MSBFIRST, mVal); // this commands dataPin to sync with clockPin, send most significant bit first.
    digitalWrite(mLatchPin, HIGH);

    // Hours shift register output
    digitalWrite(hLatchPin, LOW); 
    shiftOut(hDataPin, hClockPin, MSBFIRST, hVal); // this commands dataPin to sync with clockPin, send most significant bit first.
    digitalWrite(hLatchPin, HIGH);


    // Print to Serial Monitor
    Serial.print("Time: ");
    Serial.print(hour);
    Serial.print("-");
    Serial.print(hVal, BIN);
    Serial.print(": ");
    if (minutes < 10) Serial.print("0");
    Serial.print(minutes);
    Serial.print("-");
    Serial.print(mVal, BIN);
    Serial.print(": ");
    if (seconds < 10) Serial.print("0"); // Add a '0' to seconds
    Serial.print(seconds);
    Serial.print("-");
    Serial.print(sVal, BIN);
    Serial.print(is12HourMode ? (isPM ? " PM" : " AM") : ""); // Empty string if in 24-hour mode
    Serial.println();

  }

  delay(500);

}


// - - - - FUNCTIONS - - - -

byte bcdToDec(byte val) {
  return ((val / 16 * 10) + (val % 16));
}

byte dectoBcd(byte val) {
  return ((val / 10 * 16) + (val % 10)); // This works the same as below
  //return ((val / 10 << 4) + (val % 10)); // This works the same as above
}

String dayOfWeekToStr(byte dayOfWeek) {
  switch (dayOfWeek) {
    case 1: return "Monday   ";
    case 2: return "Tuesday  ";
    case 3: return "Wednesday";
    case 4: return "Thursday ";
    case 5: return "Friday   ";
    case 6: return "Saturday ";
    case 7: return "Sunday   ";
    default: return "Err     ";
  }
}
74HC595
GND5VSDASCLSQWRTCDS1307+
74HC595
74HC595