Clock Shifting Problem

Hi,

i wrote a timer & clock code but the clock is not working properly. It is shifting 1 minutes and 10 seconds in every 24 hour even if i don’t set timer. I could not find the cause. Any idea?

#include <Multiplex7Seg.h>
#include <Timer.h>
#define setPin      A0
#define minutePin   A1
#define hourPin     A2
const int ledPin=   13;
byte digitPins[] = {9, 10, 11, 12};         
byte segmentPins[] = {2, 3, 4, 5, 6, 7, 8}; 
int second=0, minute=0, hour=0;
Timer t,t1;
int ledState = LOW; 
int timer_saatdeg=0,saat,ikinciset=0,setocak=0,saniye=0,role=17,buzzer=18,dakika=0,saat_kur,dakika_geri,clock,clock_san,dakika_lock;
int timergecisi=0,timer2=0,i,dakika_hiz,longpress,longpressbackward,dakika_gecis,dakika_gecisgeri,dakika_hizgeri;
long previousMillis = 0;
long interval = 1000;
void setup() {
 Multiplex7Seg::set(1,4, digitPins, segmentPins);
 Serial.begin(9600); 
pinMode(minutePin, INPUT);
pinMode(hourPin,   INPUT);
pinMode(setPin,    INPUT);
digitalWrite(minutePin, HIGH);
digitalWrite(hourPin, HIGH);
digitalWrite(setPin,  HIGH);
pinMode(role,         OUTPUT);
pinMode(buzzer,       OUTPUT);
pinMode(ledPin,       OUTPUT); 
int tickEvent=t.every(1000,zaman);
int tickEvent1=t1.every(1000,zaman1); 
}
void loop() 
{
  if(dakika>=59&&dakika_geri==0&&saat_kur==0)
  {
    saat++;
    dakika=-1;
  }  
  if (second > 59) {
  minute++;
  second = 0; 
  }

  if (minute >= 60) {
  hour++;
  minute = 0; 
  }
  
  if (hour >= 24) {
  hour = 0;
  }
  t.update();
  t1.update();
  checkButtons();
}

void zaman1()
{ 
  serialOutput();
  second++;  
}

void checkButtons() 
{static boolean  minPressed=false, hourPressed=false, setPressed=false;
/////////////////////////////////////////////DAKİKA AZALTMA/////////////////////////////////////////////////////////////////////////////////////////////  
  if(timer_saatdeg==0&&digitalRead (minutePin)==HIGH&&minPressed==false)
    {
      minute++;
      minPressed = true; 
    }
  else if(ikinciset==1){ 
  if (digitalRead (minutePin)==HIGH &&dakika_hizgeri==0&&(clock>=2)) 
  {
    digitalWrite(ledPin,LOW);
    delay(250); 
    dakika--;
    dakika_geri=0;
    longpressbackward++;
    dakika_gecisgeri=1;
    dakika_gecis=0;
    if(longpressbackward>=15)
    dakika_hizgeri=1; 
  }
   if (digitalRead (minutePin)==HIGH&&dakika_hizgeri==1&&(clock>=2)) 
  {
    delay(100);
    dakika--;
    longpress++; 
    dakika_gecisgeri=1;
    if(longpressbackward>=30)
    dakika_hizgeri=2; 
  }
   if (digitalRead (minutePin)==HIGH&&dakika_hizgeri==2&&(clock>=2)) 
  {
    delay(50);
    dakika--;
    dakika_gecisgeri=1; 
  }
  if(dakika_gecisgeri==1)
  { 
    setocak=0;
    if(saat>=1&&dakika<0)    //////geri dönüşte clock=100 ise 59 yapmak için
    {
      dakika=59;
      saat=saat-1;
      dakika_geri=1;
    }
    clock=saat*100+dakika;
    Multiplex7Seg::loadValue(clock);   
    dakika_lock=0;
    dakika_gecisgeri=0;
  }
  }
   if (digitalRead (minutePin)==LOW)
   {
    dakika_hizgeri=0;
    longpressbackward=0;
    minPressed = false;
   }
/////////////////////////////////////////////DAKİKA AZALTMA///////////////////////////////////////////////////////////////////////////////////////////// 
  
/////////////////////////////////////////////DAKİKA ARTTIRMA//////////////////////////////////////////////////////////////////////////////////////////  
  if(timer_saatdeg==0&&digitalRead (hourPin)==HIGH&&hourPressed==false)
    {
      hour++;
      hourPressed = true;
    }
  else if(ikinciset==1){
  if (digitalRead (hourPin)==HIGH&&dakika_lock==0&&dakika_hiz==0) 
  {
    delay(300);
    dakika++;
    longpress++; 
    dakika_gecis=1; 
    if(longpress>=15)
    dakika_hiz=1; 
     digitalWrite(ledPin,LOW);
  }
   if (digitalRead (hourPin)==HIGH&&dakika_lock==0&&dakika_hiz==1) 
  {
    delay(150);
    dakika++;
    longpress++; 
    dakika_gecis=1;
    if(longpress>=30)
    dakika_hiz=2; 
  }
   if (digitalRead (hourPin)==HIGH&&dakika_lock==0&&dakika_hiz==2) 
  {
    delay(100);
    dakika++;
    dakika_gecis=1;
  }
  if(dakika_gecis==1)
  {
    setocak=0;
    if(clock>=958)
    {
      clock=958;
      dakika_lock=1;
    }
    saat_kur=0;
    dakika_geri=0;
    clock = saat*100+dakika;
    if(clock==60||clock==160||clock==260||clock==360||clock==460||clock==560||clock==660||clock==760||clock==860||clock==960) //////////////////dakika 59'dan sonra arttırıldığında 60 yerine 1:00 olması için yazılan kod
    {
      saat=saat+1;
      dakika=0;
      clock = saat*100+dakika;
    }
    Multiplex7Seg::loadValue(clock);   
  } 
  }
  if (digitalRead (hourPin)==LOW)
  {
    dakika_hiz=0;
    dakika_gecis=0;   
    longpress=0;
    hourPressed=false;
  }
/////////////////////////////////////////////DAKİKA ARTTIRMA////////////////////////////////////////////////////////////////////////////////////  

/////////////////////////////////////////////SETLEME //////////////////////////////////////////////////////////////////////////////////////////
  if (digitalRead (setPin)==HIGH && setPressed == false) 
  {
    if(ikinciset==0)
    {
      digitalWrite(ledPin,LOW);
      Multiplex7Seg::loadValue(0);
      timergecisi=1;
      timer_saatdeg=1;
      ikinciset=1;
      setocak=0;
      setPressed = true;
      dakika=0;
      //zamanidurdur=1;
    } 
    else{
    if(saat>=1&&dakika<=0)/////////////timer kurulduğunda saat 1:00 ise 
    {
      saat-=1;
      dakika=59;      
      saniye=60; 
      clock = saat*100+dakika;
      if(saat>=1&&dakika>=0) 
      Multiplex7Seg::loadValue(clock); 
      setocak=1;
      saat_kur=1;
      setPressed = true;
    }  
    else if(dakika!=0)/*||(saniye<59&&dakika<1))*/
    {saat_kur=1;
      saniye=60;
      dakika-=1;
      setocak=1;
      clock = saat*100+dakika;
      if(saat>=1&&dakika>=0)
      {Multiplex7Seg::loadValue(clock); }
      /*if(saat<1&&dakika<=0)
      {clock = saat*1000+dakika;Multiplex7Seg::loadValue(clock);}*/
      setPressed = true;
    } 
    }
  }
 if (digitalRead (setPin)==LOW) setPressed = false; 
/////////////////////////////////////////////SETLEME //////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////TİMER BİTİŞ //////////////////////////////////////////////////////////////////////////////////////////
  if(timer2==1)
  {   
      digitalWrite(role,HIGH); 
      delay(2500);
      digitalWrite(role,LOW);
      delay(1000);       
      saniye=0;
      setocak=0;
      timer2=0;
      for(i=0;i<5;i++)
      {
         digitalWrite(buzzer,HIGH); 
         delay(500);
         digitalWrite(buzzer,LOW);
         delay(500);       
      }   
      timergecisi=0;
      timer_saatdeg=0;
      ikinciset=0;
      //zamanidurdur=0;
   } 
/////////////////////////////////////////////TİMER BİTİŞ //////////////////////////////////////////////////////////////////////////////////////////
}
/////////////////////////////////////////////TİMER SANİYE SAYIM ///////////////////////////////////////////////////////////////////////////////////
void zaman()
{
  if(setocak==1)
  {
    blinkLED();
    saniye--;
    if(saniye<0)
    {
      if(saat>=1&&dakika<=0)/////////////timer kurulduğunda saat 1:00 ve katları ise 
      {
        saat_kur=1;      
        saat-=1;
        dakika=59;      
        clock = saat*100+dakika; 
        Multiplex7Seg::loadValue(clock);
        saniye=60;
      }
      else
      {
        saniye=59;
        dakika-=1;
        clock = saat*100+dakika; 
        if(saat>=1&&dakika>=0)
        Multiplex7Seg::loadValue(clock); 
      }
    }
   if(saat<1&&dakika<60&&dakika!=0) 
   {
     clock_san=clock*100+saniye;
     Multiplex7Seg::loadValue(clock_san);
   }
   else if(saat<1&&dakika<60&&dakika==0)
   {
     clock_san=10000+saniye;
     Multiplex7Seg::loadValue(clock_san);
     if(clock_san==10000)
     saniye=0;
   }
      if(clock==0&&saniye==0)
      {
        timer2=1;
        dakika=0;
      }    
  } 
}
/////////////////////////////////////////////TİMER SANİYE SAYIM ///////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////SAAT SAYIM ///////////////////////////////////////////////////////////////////////////////////
void serialOutput() 
{
  if(timergecisi==0)
  {
      blinkLED();  
      /*Serial.print(hour, DEC); 
      Serial.print(":"); 
      Serial.print(minute, DEC); 
      Serial.print(":"); 
      Serial.println(second, DEC);*/
      if(hour==0)
      { 
        int timp1 = ((hour+1)*10000)+minute;
        Multiplex7Seg::loadValue(timp1);
      }
      else if(hour>=10 &&hour<=24 )
      {
        int timp2 = hour*100+minute;
        Multiplex7Seg::loadValue(timp2);
      }
      else 
      {
        int timp3 = ((hour*100))+minute;    
        Multiplex7Seg::loadValue(timp3);
      }
  }
}
/////////////////////////////////////////////SAAT SAYIM ///////////////////////////////////////////////////////////////////////////////////

void blinkLED() {
unsigned long currentMillis = (millis());
 
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
    digitalWrite(ledPin, ledState);
  }
}

I have not looked at your code in detail but it looks like your not using an external RTC (real time clock) to keep time and only relying on the MCU frequency. If that is the case then 70 seconds drift a day is probably about right as the resonator used to generate the 16MHz frequency can be off and/or drift with temperature change.

Yes. like you said i am not using external RTC like ds1307. Trying to do everything with code.

But it is not definite. I mean the 70 second is not definite. I left the circuit on for 10 days and observed it. Every day the clock delays more than 1 minute but one day it delays 70 seconds another day 75 or 68. If i set the timer it delays 4 seconds for every time i set it. Is that just because of MCU freq.?

iskender: Yes. like you said i am not using external RTC like ds1307. Trying to do everything with code.

But it is not definite. I mean the 70 second is not definite. I left the circuit on for 10 days and observed it. Every day the clock delays more than 1 minute but one day it delays 70 seconds another day 75 or 68. If i set the timer it delays 4 seconds for every time i set it. Is that just because of MCU freq.?

Drift is relative to the time span. If a clock loses a second a day, that doesn't mean it will lose a second a minute. The drift for 4 seconds is measured in microseconds, which you can't measure unless you have a good RTC to compare to. Did you compare with your watch? The reason for the drift has already been explained above.

I turned on the circut and started a chronometer on my computer at the same time and went to work. When i came home i have seen that the chronometer shows 9 hours and 42 minutes but the clock was 9 hours 41 minutes. I made another circut with a non common MCU (HOLTEK HT48R09A) to do exactly the same thing. It is working properly and i did not use RTC on Holtek too. I wonder why holtek is working properly but arduino is not.

Edit - what kind of crystal does the Holtek device use?

It is not using external cyristal.

if your arduino drift is deterministic you can build a correction routine that would make it more precise.

could you please give me an example?

You are just lucky that the RC oscillator in the Holtek happens to be so accurate. To see what I mean, try running it at a different temperature. Neither it nor the Arduino clock circuit is intended to have long term clock accuracy. It is essentially there to provide a stable frequency. It is possible to design a better clock circuit for either, but so few applications use it that it would be a needless waste of money. Once you factor in the huge advantage of the ability to maintain time without power to the processor, and the extremely high performance to cost ration of new RTC devices on the market, the argument for a dedicated RTC circuit when accurate time is required becomes a real no-brainer.

iskender: could you please give me an example?

Sure. You can maintain an unsigned long variable and periodically update it from millis(). Every time you do this, you can add a small offset. This will create a timer that runs slightly faster than millis(). You couldn't use it for small time values, but it doesn't sound like that's what you need it for.

Or, here's a more creative idea. Need the serial port? No? Well, connect TX to RX so you have a loopback. The serial is clocked from a 16Mhz crystal. So you could get a timebase from that with some fiddling.

now if there is a 16mhz cristal for the serial port, why is it not used for the clock? :)

for time adjustments, you'd have to use a float, or a clever nom/denominator integer math, becuause the deltas will be small.

Time = clock * nom / denom; Time = (uint32_t) (clock * float_adjustment); // this will take many cycles

sonyhome:
now if there is a 16mhz cristal for the serial port, why is it not used for the clock? :slight_smile:

for time adjustments, you’d have to use a float, or a clever nom/denominator integer math, becuause the deltas will be small.

Time = clock * nom / denom;
Time = (uint32_t) (clock * float_adjustment); // this will take many cycles

Bull, you don’t need any floats. The discrete differential math is extremely simple. Take up many cycles? For something you need to do, at most every 100ms.? Make me laugh…

I don’t suggest doing it this way, calculating on demand. Instead, keep a corrected Time accurate by periodically updating it. That’s where the easy math comes in. The smaller the delta, the more accurate it will become because you can add only one atomic time unit at each update (clock++). The only thing I can’t get my head around is how you would deal with negative adjustments without making time “go backwards”.

you're not wrapping your head around makes me laugh: :P just kidding.

time goes backwards if you do adjustments like ntp or date change of time, not if you just insert a fixed ratio to make the clock somewhat more precise.

I think you're talking of adding/subtracting one tick every so often hence the risk:

You could store the time duration D for which a +/- 1 adjustment is needed. Every read of time T, you divide T/D = adjustment (integer division). If it is substracted you should never see time going backwards... at best it would stall because 2 successive reads would never correct by more than 1 unit of time.

iskender: It is not using external cyristal.

It does have in in-built crystal & RC oscillator so if the crystal is used then timings will be more accurate than with the arduino's external resonator.

Riva: It does have in in-built crystal & RC oscillator so if the crystal is used then timings will be more accurate than with the arduino's external resonator.

The Holtek advertises a built in crystal on the sales blurb summary, but when you dig into the datasheet, you find that it is just a built in circuit to drive an external crystal.

aarg: The Holtek advertises a built in crystal on the sales blurb summary, but when you dig into the datasheet, you find that it is just a built in circuit to drive an external crystal.

They should be done under the trades description act for writing

On-chip crystal and RC oscillator

OP must be lucky the RC oscillator is so accurate.

By the way i tried the circuit with the holtek in different temperatures, it was almost accurate. Way better than arduino. Is it just luck?

Holtek datasheet:

The RC oscillator provides the most cost effective solution. However, the frequency of oscillation may vary with VDD, temperatures and the chip itself due to process variations. It is, therefore, not suitable for timing sensitive operations where an accurate oscillator frequency is desired.

So, yeah.