Timer gains 14 seconds every hour

I'm working on a project and used a bit of code which I found on line to intergrate as a timer in my program. Once I got it working, I found that it gains aprox. 14 seconds every hour. I put in a couple lines to correct for this but I'm still curious as to why it's occuring in the first place. I would think a pulse width modulated signal would be more accurate. Anyway, as I'm new at this, I may be doing some little something wrong. If anyone sees the reason for the gained 14 seconds in my code, could you please let me know? Thanks.

const int  buttonPin = 3;    // the pin that the button is attached to
const int ledPin = 8;        // to light an LED during development
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button
int incomingByte;            // to read input into
int TM[3]={0,0,0};
String readString;
int clockInt = 0; // digital pin 2 is now interrupt 0
int masterClock = 0; // counts rising edge clock signals
int seconds = 0; // variable
int minutes = 0; // variable
int hours = 0; // variable

void setup() 
{
  pinMode(ledPin, OUTPUT);     
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);  
  attachInterrupt(clockInt, clockCounter, RISING);
      // clockInt is our interrupt, clockCounter function is called
      // when invoked on a rising clock edge
  analogReference(DEFAULT);
  analogWrite(10, 127); // this starts our PWM clock with a 50% duty cycle
  
}

void loop() 
{
  if (Serial.available() > 0) { //if any key is pressed 
   int incomingByte = Serial.read(); //read input into incomingByte
   
   switch(incomingByte){ //depending on the value of incomingByte...
     
    case 'z':
        // z is just here so I can check the time without having to set it
      Serial.print ("P  Station time is    ");
    
      Serial.print(hours);
      Serial.print(":");
      Serial.print(minutes);
      Serial.print(":");
      Serial.println(seconds);
      Serial.println ("");
      break;
      
    case 'J':
      
      Serial.print ("Set Station time       ");
      if (hours < 10){
        Serial.print("0");
      }
      Serial.print(hours);
      Serial.print(":");
      if (minutes < 10){
        Serial.print("0");
      }
      Serial.print(minutes);
      Serial.print(":");
      if (seconds < 10){
        Serial.print("0");
      }
      Serial.print(seconds);
      Serial.print (" ");
      
      hours = TimeProcess(); //hours, minutes, and seconds are populated
      Serial.print(":"); //colons pops up between each set of 2 numbers
      minutes = TimeProcess();
      Serial.print(":");
      seconds = TimeProcess();
      
      Serial.println ("");  // cariage return
      Serial.print ("  Station time         ");
      if (hours < 10){ //puts a "0" in the first digit location if needed
        Serial.print("0");
      }
      Serial.print(hours);
      Serial.print(":");
      if (minutes < 10){ //same as was done for hours
        Serial.print("0");
      }
      Serial.print(minutes);
      Serial.print(":");
      if (seconds < 10){ //same as was done for hours
        Serial.print("0");
      }
      Serial.print(seconds);
      Serial.println (" ");      
      break;
      
      default:      
      Serial.println ("Invalid entry. Enter J to set the Time.");
    }
  }
}



void clockCounter() // called by interrupt
{
  masterClock ++;  //with each clock rise add 1 to masterclock count
  if(masterClock == 489) // 490hz reached
  {
    seconds ++;  //after one 490Hz cycle add 1 second
    masterClock = 0; //Reset after 1 second is reached
    //tone(13, 100, 500); //using tone to pulse LED without delay call
    if (seconds == 60) // Now getting into real time keeping
    {
      minutes ++;  //increments minutes by 1
      seconds = 0;   //reset the seconds variable 
      if (minutes == 30 || seconds == 30) //at each half hour and 30 seconds do this
      { //I could have put this anywhere within the hour. I just choose 30 min 30 sec
        seconds = (seconds - 14); //corrects the clock
      }
      if (minutes == 60)//when minutes reach 60
      {
        hours ++;//incriments hours by 1
        minutes = 0;//resets the minutes variable
        if (hours == 24)//when hours reach 24
        {
          //days ++;
          hours = 0;//rest the minutes variable
        }
      }
    }
  }
  return;
}

long TimeProcess() //populates int hours, minutes, or seconds for easy printing
{
      long var;
      int cnt = 0;
      for (int complete = 0; complete != 1;)//"complete" is initialized at 0 then the condition of the 
                                            // for loop is that "complete" is not 1
      { //loop will continue until "complete" becomes 1
       while (Serial.available() > 0 )
       {  //Serial.available recognizes digits pending in serial buffer
          char c = Serial.read(); //reads data in serial buffer one byte at a time.
          TM[cnt] = int(c - 48); //contents of c are copied to array TM 
          cnt++; //cnt is incrimented with each entery into the array
          readString += c; //contents of char c are placed in readString
          Serial.print (c); //prints each charicter as it's typed
          delay(2);
          if (cnt == 2) // when 2 digits are entered do this
          {
            Serial.print ("");
            complete = 1; //make "complete" 1 which satisfies the for loop
          }
       }
      }
     if (readString.length()>0) //when readString gets content, do something
       {
         char carray[readString.length() + 1]; //creates an array of readString content
         readString.toCharArray(carray, sizeof(carray)); // transfers readString into char array named carray.
         long f = atol(carray); //creates long variable "f" equal to "array to long" conversion of data in carray.
         var = f; //var is equal to "f" which we just converted to a number from array of chars.
         readString=""; // make readString empty for next time around
         readString[2]; //allocate 8 bytes of memory for readString
       } 
     return var;
}

The counters for the timers are not incremented during interrupts.

Mark

I wonder if this delay have something to do with your 14 second problem? Maybe, maybe not.

while (Serial.available() > 0 )
{ //Serial.available recognizes digits pending in serial buffer
char c = Serial.read(); //reads data in serial buffer one byte at a time.
TM[cnt] = int(c - 48); //contents of c are copied to array TM
cnt++; //cnt is incrimented with each entery into the array
readString += c; //contents of char c are placed in readString
Serial.print (c); //prints each charicter as it's typed
delay(2);

      for (int complete = 0; complete != 1;)//"complete" is initialized at 0 then the condition of the 
                                            // for loop is that "complete" is not 1

Mangling a for loop to make a while statement just feels wrong.

Im-just-Rusty:
I would think a pulse width modulated signal would be more accurate.

No. It's the same clock; the processor's clock. What you are doing will never be more accurate than millis / micros.

void clockCounter() // called by interrupt
{
masterClock ++; //with each clock rise add 1 to masterclock count
if(masterClock == 489) // 490hz reached
{

Two things... 1. The "489" is wrong. 2. The "490" is also wrong.

The frequency is slightly higher than 490 which puts the target value between 490 and 491. Unless you account for the fractional part your clock will not be accurate; it will be off by at least 1/490*3600 = 7 seconds per hour.

Hmmm.

1/490*3600 = 7 seconds per hour.

and with that delay I pointed out, 3600 second in 1 hour, 3600 * delay(2) = 7.2 seconds total delay, per hour
So 1/490*3600 = 7 seconds per hour + **7.2 seconds total delay ** = 14.2 seconds per hour.

I'll try taking out the delay and also try to figure out an answer to the frequency issue and see if that helps. Thanks. I'll try to post any results I come up with (although I am pretty slow at all this programing stuff lol). :slight_smile: