DS1307 simple time drift compensation

DS3231 currently seems to be an ultimate solution for my RTC needs, but I have plenty of DS1307 in my coffers (some of them are pretty inaccurate, like 90 ppm ahead of time), so I'd like to find a way to use them effectlively.

There are similar topics on the web, but I'd like to ask to check my code so that I could understand if I'd been able to find a proper solution by myself. The sketch works, I've tried that out, but nevertheless I feel the need to draw some feedback. Can I do something better? Didn't I overlook something?, etc.

Time drift compensation statements are at the very end of the sketch. Basically sketch counts up every hour and each 12 hours it subtracts 4 seconds from current RTC time. The value (4 sec) was found experimentally. This sketch is written in mind with following:

– it's made for room clock which don't get turned off too often;

– using another DS1307 module will require measuring its item-specific drift experimentally;

– overall compensation is not very accurate, it can suffer from temperature etc. But if time drift will be more or less consistent, constant 4 sec offset still makes things somewhat better.

So yes, in this case it's an attempt to turn terrible case (90+ ppm) into a mediocre one (around 20 ppm).

One more thing. I'm just beginning to learn how to write readable and consistent code, therefore any general input on my sketch is appreciated. At the very least I have a feeling I'm overusing global variables, but I'm not sure if it's really bad.

// This RTC sketch drives multiplexed 7-segment 4-digit LED display with 4 common cathodes.
// Between GND and each common cathode a small-signal NPN BJT is required.

#include <Adafruit_BusIO_Register.h>
#include <Adafruit_I2CDevice.h>
#include <Adafruit_I2CRegister.h>
#include <Adafruit_SPIDevice.h>
#include <RTClib.h>

// This sketch works for both DS3231 and DS1307 RTCs. Applicable variant must be uncommented to create an object.
// RTC_DS3231 rtc;
RTC_DS1307 rtc;

// Pins which drive transistors connecting common cathodes to GND
// and thus switch the active display section ("D" is for "digit").
int D1_PIN = 9;
int D2_PIN = 8;
int D3_PIN = 7;
int D4_PIN = 6;

// Pins for interaction with 74HC595 latched shift register IC.
// The 595 is used to create an output parallel set of signals ("byte mask")
// that lights up only necessary segments at any given time.
int DATA_PIN = 4;
int LATCH_PIN = 2;
int CLOCK_PIN = 3;

// For storing numbers from RTC output.
int RTC_hours;
int RTC_minutes;
int RTC_seconds;

// For storing digits to be displayed.
int hours_first_digit;
int hours_second_digit;
int minutes_first_digit;
int minutes_second_digit;
int seconds_first_digit;
int seconds_second_digit;

// Optional. Used to start a counter which sends RTC output through UART once per second.
int previous_RTC_seconds;
bool initial_RTC_seconds_was_stored = 0;

// GLOBAL VARIABLES USED IN SETTIME MODE
// Settime mode allows setting time manually without neither using IDE nor resetting the MCU.

// Used to begin and end "while" loops during which time may be set.
bool settime_mode = 0;

// Pins for active-low manual input buttons.
int SETTIME_TOGGLE_PIN = 10;
int SETTIME_HOURS_PIN = 11;
int SETTIME_MINUTES_PIN = 12;

// For button debounce.
bool settime_toggle_button_is_pressed;
bool settime_toggle_button_wasnt_pressed;
bool settime_minutes_button_is_pressed;
bool settime_minutes_button_wasnt_pressed;
bool settime_hours_button_is_pressed;
bool settime_hours_button_wasnt_pressed;

// For storing temporary numbers to be loaded into RTC when settime mode is turned off.
int settime_hours;
int settime_minutes;
int settime_seconds;


// GLOBAL VARIABLES USED FOR TIME DRIFT COMPENSATION
int previous_RTC_hours;
bool initial_RTC_hours_was_stored = 0;
int compensation_counter = 0;
int compensation_threshold = 12;
bool compensation_cocked = 0;
int compensation_value = 4;


void setup() {
  rtc.begin();

  // If necessary, uncomment following line to set RTC time, upload the sketch, comment the following line once again and then re-upload the sketch.
  // rtc.adjust(DateTime(2022, 2, 20, 3, 39, 0)); // year, month, date, hours, minutes, seconds

  pinMode(D1_PIN, OUTPUT);
  pinMode(D2_PIN, OUTPUT);
  pinMode(D3_PIN, OUTPUT);
  pinMode(D4_PIN, OUTPUT);
  
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
  
  pinMode(SETTIME_TOGGLE_PIN, INPUT_PULLUP);
  pinMode(SETTIME_HOURS_PIN, INPUT_PULLUP);
  pinMode(SETTIME_MINUTES_PIN, INPUT_PULLUP);

  // Prevents artifacts from being displayed during boot.
  digitalWrite(D1_PIN, 0);
  digitalWrite(D2_PIN, 0);
  digitalWrite(D3_PIN, 0);
  digitalWrite(D4_PIN, 0);

  // May be handy for debugging purposes etc.
  Serial.begin(9600);
}


// User-defined function dedicated to actually displaying digits.
void display_digit(int current_cathode, int digit_to_display, bool whether_dot_is_used) {

  // Defines which display section (digit) is to be turned on right now.
  bool cathode_1;
  if (current_cathode == D1_PIN) {
    cathode_1 = 1;
  } else cathode_1 = 0;
  digitalWrite(D1_PIN, cathode_1);

  bool cathode_2;
  if (current_cathode == D2_PIN) {
    cathode_2 = 1;
  } else cathode_2 = 0;
  digitalWrite(D2_PIN, cathode_2);

  bool cathode_3;
  if (current_cathode == D3_PIN) {
    cathode_3 = 1;
  } else cathode_3 = 0;
  digitalWrite(D3_PIN, cathode_3);

  bool cathode_4;
  if (current_cathode == D4_PIN) {
    cathode_4 = 1;
  } else cathode_4 = 0;
  digitalWrite(D4_PIN, cathode_4);

  // Byte maska for digits from 0 to 9. May vary depending on the order
  // in which 595 output pins are wired to the display input pins.
  uint8_t output_matrix[] = {
    0b11011101,
    0b00001100,
    0b11000111,
    0b11001110,
    0b00011110,
    0b11011010,
    0b11011011,
    0b01001100,
    0b11011111,
    0b11011110
};

  // If third argument of function is 1, decimal point ("dot") will blink once per second.
  bool dot_state; 
  uint8_t dot_byte = 0;                      // An addition to an appropriate matrix byte which makes (or doesn't make) decimal point bit become 1.
  if (whether_dot_is_used) {
    dot_state = seconds_second_digit % 2;    // Every other second the dot blinks.
      if (dot_state) {                       
        dot_byte = 0b00100000;
      } else dot_byte = 0;
  }

  // Displaying.
  digitalWrite(LATCH_PIN, 0);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, output_matrix[digit_to_display] + dot_byte);
  digitalWrite(LATCH_PIN, 1);

  // Anti-ghosting sequence.
  delay(4); // Anti-ghosting delay
  digitalWrite(LATCH_PIN, 0);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, 0b00000000); // Turns all display sections off for one clock period.
  digitalWrite(LATCH_PIN, 1);
}


void loop() {
  DateTime now = rtc.now();

  RTC_hours = now.hour(), DEC;
  RTC_minutes = now.minute(), DEC;
  RTC_seconds = now.second(), DEC;

  // Division and modulo operators are used
  // to divide numbers into first and second digit.
  hours_first_digit = RTC_hours / 10;
  hours_second_digit = RTC_hours % 10;
  minutes_first_digit = RTC_minutes / 10;
  minutes_second_digit = RTC_minutes % 10;
  seconds_first_digit = RTC_seconds / 10;
  seconds_second_digit = RTC_seconds % 10;

  // Display function callers.
  display_digit(D1_PIN, hours_first_digit, 0);
  display_digit(D2_PIN, hours_second_digit, 1);         // Decimal point is used to visually separate hours from minutes and indicate that the clock is running.
  display_digit(D3_PIN, minutes_first_digit, 0);
  display_digit(D4_PIN, minutes_second_digit, 0);

  // Optional. Begin counting seconds as a clock for sending data via UART.
  if (!initial_RTC_seconds_was_stored) {
    previous_RTC_seconds = RTC_seconds;
    initial_RTC_seconds_was_stored = 1;
  }

  // Optional. Used for sending RTC output via UART.
  if (RTC_seconds != previous_RTC_seconds) {
    Serial.print(RTC_hours);
    Serial.print(":");
    Serial.print(RTC_minutes);
    Serial.print(":");
    Serial.println(RTC_seconds);
    previous_RTC_seconds = RTC_seconds;
  }

  
  // SETTIME MODE
  // Turn on settime mode
  settime_toggle_button_is_pressed = !digitalRead(SETTIME_TOGGLE_PIN);
  if (settime_toggle_button_is_pressed && settime_toggle_button_wasnt_pressed) {
      delay(10);
      if (settime_toggle_button_is_pressed) {
        settime_seconds = 0;
        settime_minutes = 0;
        settime_hours = 0;
        Serial.print(settime_hours);
        Serial.print(":");
        Serial.print(settime_minutes);
        Serial.print(":");
        Serial.println(settime_seconds);
        settime_mode = !settime_mode;
      }
  }
  settime_toggle_button_wasnt_pressed = !settime_toggle_button_is_pressed;

  // While settime mode is on.
  while (settime_mode) {
    settime_hours_button_is_pressed = !digitalRead(SETTIME_HOURS_PIN);
    if (settime_hours_button_is_pressed && settime_hours_button_wasnt_pressed) {
      delay(10);
      if (settime_hours_button_is_pressed) {
        ++settime_hours;
        if (settime_hours > 23) {
          settime_hours = 0;
        }
        Serial.print(settime_hours);
        Serial.print(":");
        Serial.print(settime_minutes);
        Serial.print(":");
        Serial.println(settime_seconds);
      }
    }
    settime_hours_button_wasnt_pressed = !settime_hours_button_is_pressed;
    
    settime_minutes_button_is_pressed = !digitalRead(SETTIME_MINUTES_PIN);
    if (settime_minutes_button_is_pressed && settime_minutes_button_wasnt_pressed) {
      delay(10);
      if (settime_minutes_button_is_pressed) {
        ++settime_minutes;
        if (settime_minutes > 59) {
          settime_minutes = 0;
        }
        Serial.print(settime_hours);
        Serial.print(":");
        Serial.print(settime_minutes);
        Serial.print(":");
        Serial.println(settime_seconds);
      }
    }
    settime_minutes_button_wasnt_pressed = !settime_minutes_button_is_pressed;

    // Same as outside settime "while" loop, but calculated from temporary numbers, not actual RTC output.
    hours_first_digit = settime_hours / 10;
    hours_second_digit = settime_hours % 10;
    minutes_first_digit = settime_minutes / 10;
    minutes_second_digit = settime_minutes % 10;
    seconds_first_digit = settime_seconds / 10;
    seconds_second_digit = settime_seconds % 10;

    // Same as outside settime "while" loop.
    display_digit(D1_PIN, hours_first_digit, 0);
    display_digit(D2_PIN, hours_second_digit, 0);           // Decimal point is off to indicate that settime mode is on.
    display_digit(D3_PIN, minutes_first_digit, 0);
    display_digit(D4_PIN, minutes_second_digit, 0);
   
    // Turn off settime mode.
    settime_toggle_button_is_pressed = !digitalRead(SETTIME_TOGGLE_PIN);
    if (settime_toggle_button_is_pressed && settime_toggle_button_wasnt_pressed) {
      delay(10);
      if (settime_toggle_button_is_pressed) {
        rtc.adjust(DateTime(2022, 2, 20, settime_hours, settime_minutes, settime_seconds)); // year, month, date, hours, minutes, seconds
        settime_mode = !settime_mode;
      }
    }
    settime_toggle_button_wasnt_pressed = !settime_toggle_button_is_pressed;
  }


  // TIME DRIFT COMPENSATION
  if (!initial_RTC_hours_was_stored) {                              // Begin counting hours.
    previous_RTC_hours = RTC_hours;
    initial_RTC_hours_was_stored = 1;
  }
  
  if (RTC_hours != previous_RTC_hours) {
    ++compensation_counter;
    previous_RTC_hours = RTC_hours;
  }

  if (compensation_counter >= compensation_threshold) {
    compensation_cocked = 1;                                        // When set number of hours has passed, a compensation is ready to take place.
    compensation_counter = 0;
  }

  if (RTC_seconds >= compensation_value && compensation_cocked) {   // Wait until current RTC_seconds equals compensation value to avoid setting negative RTC time.
    rtc.adjust(DateTime(2022, 2, 20, RTC_hours, RTC_minutes, RTC_seconds - compensation_value)); // year, month, date, hours, minutes, seconds with compensation value subtracted.
    compensation_cocked = 0;
  }
}

Can be simplified (and corrected)

  bool cathode_1 = current_cathode == D1_PIN;

(But digitalWrite doesn't take a bool)

Minor point but ppl seem to care, things like that don't need to be int types, and they don't need to be variable.

const byte D1_PIN = 9;

And maybe the next thing to learn/know about is the switch/case statement.

But the idea of just bumping the time occasionally to keep it on track is fine.

I suppose a fanatic might add a potentiometer that would afford, say, +/- 10 seconds and labeled it F <-> S like an old fashioned clock, where it's always unclear what to do if the clock is running fast or slow, which direction should the lever be moved. :expressionless:

a7

Hello,

  • I would tidy up the main loop and call separate functions for each task (e.g. rtc compensation) and certainly use shorter variable names (e.g. settime_minutes_button_is_pressed). :slight_smile:

  • As TheMemberFormerlyKnownAsAWOL mentioned you tend to mix bool variables with HIGH/LOW and 0/1 logic. As you say, this could work but bool supposed to have true/false states.

  • You should make constants constant and choose the appropriate datatype.

const uint8_t D1_PIN = 9;     //instead of int
const uint8_t D2_PIN = 8;
const uint8_t D3_PIN = 7;
const uint8_t D4_PIN = 6;
  • Other variables which are not changed at runtime, should also be constants and I think it is a good habit to name them with upper case.
//int compensation_threshold = 12;
const uint8_t COMPENSATION_THRESHOLD = 12;
//int compensation_value = 4;
const int COMPENSATION_VALUE = 4;      //this could have negative value
  • I didn't follow the whole logic, but I think you could spare a button. You could have a SetTimeToggle and a Set button. Toggle enters setup mode, Set button sets hour, Toggle again to jump to set minute mode, after it Toggle to exit setup mode.

  • This logic could leave to incorrect compensation, if the comp value was negative:
    (also I would have -4 compensation if the RTC is ahead of time, but it's just a matter of thinking)

  if (RTC_seconds >= compensation_value && compensation_cocked) {   // Wait until current RTC_seconds equals compensation value to avoid setting negative RTC time.
    rtc.adjust(DateTime(2022, 2, 20, RTC_hours, RTC_minutes, RTC_seconds - compensation_value)); // year, month, date, hours, minutes, seconds with compensation value subtracted.
    compensation_cocked = 0;
  }

Maybe cocked is a bad variable name, or part of any, in a clock sketch.

At my first reading I wa looking at it as a lot typo.

Just sayin'.

a7

That's a whole lot of useful input. Thank you all for advice! Reading your comments made me considerably rearrange my code. Therefore I'm posting an update.

Are there any caveats about doing so? I find logic "if YES, output HIGH (equal to 1), else output LOW (equal to 0)" extremely straightforward. I thought it's a natural way of using bool and it works like a charm.

Good point, I accounted for that.

You're right, although "cocked" sounds cool to me :slight_smile: Reminds of mechanisms.

Beside the point, I also tried tinkering with DS1307 module itself. It's a cheap stuff which ran 110 ppm ahead of time. I tried replacing a crystal (I have a batch of 12.5 pF crystals from AliExpress) and it worked like a charm! I couldn't believe my eyes, now this thing goes 5 ppm slow, which is considerably better. Perhaps original crystal was crap. I understand it's a bit off topic, but I'm writing this in case some poor soul will be searching information about ways for improving their old DS1307's. I think my sketch is still useful, but the less you have to compensate for in first place, the better.

// This sketch reads RTC output and drives multiplexed 7-segment 4-digit LED display with 4 common cathodes.
// Between GND and each common cathode a small-signal NPN BJT is required.

#include <Adafruit_BusIO_Register.h>
#include <Adafruit_I2CDevice.h>
#include <Adafruit_I2CRegister.h>
#include <RTClib.h>


// GENERIC GLOBAL VARIABLES

// This sketch works with both DS3231 and DS1307 RTCs. Applicable variant must be uncommented to create an object.
// RTC_DS3231 rtc;
RTC_DS1307 rtc;

// For storing numbers from RTC output.
uint8_t RTC_hours;
uint8_t RTC_minutes;
uint8_t RTC_seconds;

// For storing digits to be displayed.
uint8_t hours_first_digit;
uint8_t hours_second_digit;
uint8_t minutes_first_digit;
uint8_t minutes_second_digit;
uint8_t seconds_first_digit;
uint8_t seconds_second_digit;


// GLOBAL VARIABLES RELATED TO USER-DEFINED FUNCTIONS

//dispay_digit()
// Pins which drive transistors connecting common cathodes to GND
// and thus switch the active display section ("D" is for "digit").
const uint8_t D1_PIN = 9;
const uint8_t D2_PIN = 8;
const uint8_t D3_PIN = 7;
const uint8_t D4_PIN = 6;

// Pins which interact with 74HC595 (known as simply "595") latched shift register IC.
// The 595 is used to create a set of parallel output signals ("byte mask") that lights up only necessary segments at any given time.
const uint8_t DATA_PIN = 4;
const uint8_t LATCH_PIN = 2;
const uint8_t CLOCK_PIN = 3;

uint8_t dot_byte;                                 // A byte which, being added to a byte mask, turns decimal point ("dot") bit into 1.
bool dot_state;                                   // Makes decimal point blink.


// serial_output()
uint8_t previous_RTC_seconds;                     // Reference point for finding out if seconds output has been changed, which entails sending data via UART.
bool initial_RTC_seconds_was_stored = 0;          // Makes the code which finds out initial previous_RTC_seconds run only once.


// compensate()
const int COMPENSATION_VALUE = -4;                // Number of seconds added to RTC_seconds in order to keep up with the actual time. 
                                                  // Value should be negative if RTC runs ahead of time, positive otherwise. Valid value ranges from -59 to 59.
const uint8_t COMPENSATION_THRESHOLD = 12;        // How many hours will pass until compensation occurs.
uint8_t compensation_counter = 0;                 // Hours counter.
uint8_t previous_RTC_hours;                       // Reference point for finding out if hours output has been changed, which entails raising hours counter.
bool initial_RTC_hours_was_stored = 0;            // Makes the code which finds out initial previous_RTC_hours run only once.
bool compensation_loaded = 0;                     // Enables compensation and puts it "on hold" until RTC_seconds takes appropriate value.
bool compensation_all_clear;                      // Confirms that RTC_seconds holds appropriate value. It prevents driving RTC_seconds below 0 or above 59.


// manual_settime()
bool settime_mode = 0;                            // Used to begin and end while() loops during which time may be manually set.

// Pins for active-low manual input buttons.
const uint8_t SETTIME_TOGGLE_PIN = 10;
const uint8_t SETTIME_HOURS_PIN = 11;
const uint8_t SETTIME_MINUTES_PIN = 12;

// For button debounce.
bool settime_toggle_button_is_pressed;
bool settime_toggle_button_wasnt_pressed;
bool settime_hours_button_is_pressed;
bool settime_hours_button_wasnt_pressed;
bool settime_minutes_button_is_pressed;
bool settime_minutes_button_wasnt_pressed;

// For storing temporary numbers to be loaded into RTC when settime mode is turned off.
uint8_t settime_hours;
uint8_t settime_minutes;
uint8_t settime_seconds;


void setup() {
  rtc.begin();

  // If necessary, uncomment following line to set RTC time, upload the sketch, comment the following line once again and then re-upload the sketch.
  // rtc.adjust(DateTime(2022, 2, 22, 12, 59, 50));  // year, month, date, hours, minutes, seconds

  pinMode(D1_PIN, OUTPUT);
  pinMode(D2_PIN, OUTPUT);
  pinMode(D3_PIN, OUTPUT);
  pinMode(D4_PIN, OUTPUT);
  
  pinMode(CLOCK_PIN, OUTPUT);
  pinMode(LATCH_PIN, OUTPUT);
  pinMode(DATA_PIN, OUTPUT);
  
  pinMode(SETTIME_TOGGLE_PIN, INPUT_PULLUP);
  pinMode(SETTIME_HOURS_PIN, INPUT_PULLUP);
  pinMode(SETTIME_MINUTES_PIN, INPUT_PULLUP);

  // Prevents artifacts from being displayed during boot.
  digitalWrite(D1_PIN, 0);
  digitalWrite(D2_PIN, 0);
  digitalWrite(D3_PIN, 0);
  digitalWrite(D4_PIN, 0);

  // May be handy for debugging purposes etc.
  Serial.begin(9600);
}


void loop() {
  DateTime now = rtc.now();

  RTC_hours = now.hour(), DEC;
  RTC_minutes = now.minute(), DEC;
  RTC_seconds = now.second(), DEC;

  // Division and modulo operators are used
  // to separate numbers into first and second digit.
  hours_first_digit = RTC_hours / 10;
  hours_second_digit = RTC_hours % 10;
  minutes_first_digit = RTC_minutes / 10;
  minutes_second_digit = RTC_minutes % 10;
  seconds_first_digit = RTC_seconds / 10;
  seconds_second_digit = RTC_seconds % 10;

  // Display function callers.
  display_digit(D1_PIN, hours_first_digit, 0);
  display_digit(D2_PIN, hours_second_digit, 1);    // Decimal point is used to visually separate hours from minutes and indicate that the clock is running.
  display_digit(D3_PIN, minutes_first_digit, 0);
  display_digit(D4_PIN, minutes_second_digit, 0);

  // Serial output function caller.
  serial_output();

  // Time drift compensation function caller.
  compensate();

  // Turn on settime mode.
  settime_toggle_button_is_pressed = !digitalRead(SETTIME_TOGGLE_PIN);
  if (settime_toggle_button_is_pressed && settime_toggle_button_wasnt_pressed) {
      delay(10);
      if (settime_toggle_button_is_pressed) {
        manual_settime();                          // Settime function caller.
      }
  }
  settime_toggle_button_wasnt_pressed = !settime_toggle_button_is_pressed;
}


void display_digit(int current_cathode, int digit_to_display, bool whether_dot_is_used) {  // User-defined function dedicated to actually displaying digits.

  // Defines which display section (digit) is to be turned on right now.
  bool cathode_1 = current_cathode == D1_PIN;
  digitalWrite(D1_PIN, cathode_1);
  bool cathode_2 = current_cathode == D2_PIN;
  digitalWrite(D2_PIN, cathode_2);
  bool cathode_3 = current_cathode == D3_PIN;
  digitalWrite(D3_PIN, cathode_3);
  bool cathode_4 = current_cathode == D4_PIN;
  digitalWrite(D4_PIN, cathode_4);

  // Byte masks for digits from 0 to 9. May vary depending on the order
  // in which 595 output pins are wired to the display input pins.
  uint8_t output_matrix[] = {
    0b11011101,
    0b00001100,
    0b11000111,
    0b11001110,
    0b00011110,
    0b11011010,
    0b11011011,
    0b01001100,
    0b11011111,
    0b11011110
};

  // If third argument of function is 1, decimal point will blink once per second.
  dot_byte = 0;
  if (whether_dot_is_used) {
    dot_state = seconds_second_digit % 2;  // Every other second the dot blinks.
      if (dot_state) {                       
        dot_byte = 0b00100000;
      } else dot_byte = 0;
  }

  // Displaying.
  digitalWrite(LATCH_PIN, 0);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, output_matrix[digit_to_display] + dot_byte);
  digitalWrite(LATCH_PIN, 1);

  // Anti-ghosting sequence.
  delay(4); // Anti-ghosting delay
  digitalWrite(LATCH_PIN, 0);
  shiftOut(DATA_PIN, CLOCK_PIN, MSBFIRST, 0b00000000); // Turns all display sections off for one clock period.
  digitalWrite(LATCH_PIN, 1);
}


void serial_output() {  // Optional user-defined function. Used for sending RTC output via UART for debugging purposes etc.
  if (!initial_RTC_seconds_was_stored) {
    previous_RTC_seconds = RTC_seconds;
    initial_RTC_seconds_was_stored = 1;
  }

  if (RTC_seconds != previous_RTC_seconds) {
    Serial.print(RTC_hours);
    Serial.print(":");
    Serial.print(RTC_minutes);
    Serial.print(":");
    Serial.println(RTC_seconds);
    previous_RTC_seconds = RTC_seconds;
  }
}


void compensate() {  // User-defined function dedicated to compensating for time drift.
  if (!initial_RTC_hours_was_stored) {                              // Begin counting hours.
    previous_RTC_hours = RTC_hours;
    initial_RTC_hours_was_stored = 1;
  }
  
  if (RTC_hours != previous_RTC_hours) {
    ++compensation_counter;
    previous_RTC_hours = RTC_hours;
  }
    
  if (compensation_counter >= COMPENSATION_THRESHOLD) {
    compensation_loaded = 1;                                        // When a set number of hours has passed, a compensation is ready to take place.
    compensation_counter = 0;
  }

  if (COMPENSATION_VALUE > 0 || RTC_seconds >= abs(COMPENSATION_VALUE))  // Prevents RTC_seconds from being driven below 0 or above 59
    compensation_all_clear = 1;
    else compensation_all_clear = 0;
  
  if (compensation_loaded && compensation_all_clear && COMPENSATION_VALUE >= -59 && COMPENSATION_VALUE <= 59) {         
    rtc.adjust(DateTime(2022, 2, 22, RTC_hours, RTC_minutes, RTC_seconds + COMPENSATION_VALUE)); // year, month, date, hours, minutes, seconds with compensation value subtracted.
    compensation_loaded = 0;
  }
}


void manual_settime() {  // Settime mode allows setting time manually without neither using IDE nor resetting the MCU.
  // This stuff is executed just once, after that function gets caught into a while() loop.
  settime_mode = !settime_mode;
  settime_seconds = 0;
  settime_minutes = 0;
  settime_hours = 0;
  Serial.print(settime_hours);
  Serial.print(":");
  Serial.print(settime_minutes);
  Serial.print(":");
  Serial.println(settime_seconds);
  settime_toggle_button_wasnt_pressed = !settime_toggle_button_is_pressed;
      
  // While settime mode is on.
  while (settime_mode) {
    
    // Set hours.
    settime_hours_button_is_pressed = !digitalRead(SETTIME_HOURS_PIN);
    if (settime_hours_button_is_pressed && settime_hours_button_wasnt_pressed) {
      delay(10);
      if (settime_hours_button_is_pressed) {
        ++settime_hours;
        if (settime_hours > 23) {
          settime_hours = 0;
        }
        Serial.print(settime_hours);
        Serial.print(":");
        Serial.print(settime_minutes);
        Serial.print(":");
        Serial.println(settime_seconds);
      }
    }
    settime_hours_button_wasnt_pressed = !settime_hours_button_is_pressed;

    // Set minutes.
    settime_minutes_button_is_pressed = !digitalRead(SETTIME_MINUTES_PIN);
    if (settime_minutes_button_is_pressed && settime_minutes_button_wasnt_pressed) {
      delay(10);
      if (settime_minutes_button_is_pressed) {
        ++settime_minutes;
        if (settime_minutes > 59) {
          settime_minutes = 0;
        }
        Serial.print(settime_hours);
        Serial.print(":");
        Serial.print(settime_minutes);
        Serial.print(":");
        Serial.println(settime_seconds);
      }
    }
    settime_minutes_button_wasnt_pressed = !settime_minutes_button_is_pressed;

    // Same as outside settime while() loop, but calculated from temporary settime numbers, not actual RTC output.
    hours_first_digit = settime_hours / 10;
    hours_second_digit = settime_hours % 10;
    minutes_first_digit = settime_minutes / 10;
    minutes_second_digit = settime_minutes % 10;
    seconds_first_digit = settime_seconds / 10;
    seconds_second_digit = settime_seconds % 10;

    // Same as outside settime while() loop.
    display_digit(D1_PIN, hours_first_digit, 0);
    display_digit(D2_PIN, hours_second_digit, 0);    // Decimal point is off to indicate that settime mode is on.
    display_digit(D3_PIN, minutes_first_digit, 0);
    display_digit(D4_PIN, minutes_second_digit, 0);
   
    // Turn off settime mode.
    settime_toggle_button_is_pressed = !digitalRead(SETTIME_TOGGLE_PIN);
    if (settime_toggle_button_is_pressed && settime_toggle_button_wasnt_pressed) {
      delay(10);
      if (settime_toggle_button_is_pressed) {
        rtc.adjust(DateTime(2022, 2, 22, settime_hours, settime_minutes, settime_seconds));  // year, month, date, hours, minutes, seconds
        settime_mode = !settime_mode;
      }
    }
    settime_toggle_button_wasnt_pressed = !settime_toggle_button_is_pressed;
  }
}

Again, digitalWrite does not take a bool argument.

digitalWrite (D1_PIN, current_cathode == D1_PIN ? HIGH : LOW);

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.