real time clock drifting off

Posted on
  • Hey everyone,
    I'm using the Espruino Pico's real time clock for my clock project. Basically when I press the button, the Pico shows the time on some 7-segment LEDs for about a second and goes back to deep sleep.

    function data() {
      full=(new Date()).toString();
      words = full.split(' ');
      month = words[1];
      date = words[2];
      bigtime = words[4];
      small = words[4].split(':');
      hour = small[0];
      if (hour > 12) {
        hour = hour-12;
      }
      min = small[1];
    }
    
    function hourDriver(){
    data();
    switch(Number(hour)) {
      //some code
      }
    }
    
    function minuteDriver(){
    data();
    switch(Number(min)) {
      //some code
      }
    }
    
    setWatch(function (e) {
      data();
      for(i=0;i<60;i++){hourDriver();minuteDri­ver();}
      allClear(); //initializes 7-segment display
      setDeepSleep(1);
    }, B8, {edge:"falling", repeat:true, debounce:50});
    

    No matter what I do, the time seems to be drifting off about 5 to 10 minutes after a full day of use. What did I do wrong??? Or is the Pico's clock not the most accurate?

    Thanks!

    If you want the entire code it's in the comment below.

  • 
    var dg1 = A8;
    var dg2 = B7;
    var A = B14;
    var B = B13;
    var C = B10;
    var D = B1;
    var E = A7;
    var F = A6;
    var G = A5;
    var DP =  B15;
    
    function allClear() {
      digitalWrite(A , 0);
      digitalWrite(B , 0);
      digitalWrite(C , 0);
      digitalWrite(D , 0);
      digitalWrite(E , 0);
      digitalWrite(F , 0);
      digitalWrite(G , 0);
      digitalWrite(DP , 0);
      digitalWrite(A8, 1);
      digitalWrite(B7, 1);
    }
    
    /*
    A little help?
    
                ----A----
                |       |
                F       B
                |       |
                ----G----
                |       |
                E       C
                |       |
                ----D----
    */
    
    function one() {
      digitalWrite(F, 1);
      digitalWrite(E, 1);
    }
    
    function two() {
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(G, 1);
      digitalWrite(E, 1);
      digitalWrite(D, 1);
    }
    
    function three() {
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(G, 1);
      digitalWrite(C, 1);
      digitalWrite(D, 1);
    }
    
    function four() {
      digitalWrite(F, 1);
      digitalWrite(G, 1);
      digitalWrite(B, 1);
      digitalWrite(C, 1);
    }
    
    function five() {
      digitalWrite(A, 1);
      digitalWrite(F, 1);
      digitalWrite(G, 1);
      digitalWrite(C, 1);
      digitalWrite(D, 1);
    }
    
    function six() {
      digitalWrite(A, 1);
      digitalWrite(F, 1);
      digitalWrite(E, 1);
      digitalWrite(G, 1);
      digitalWrite(C, 1);
      digitalWrite(D, 1);
    }
    
    function seven() {
      digitalWrite(F, 1);
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(C, 1);
    }
    
    function eight() {
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(C, 1);
      digitalWrite(D, 1);
      digitalWrite(E, 1);
      digitalWrite(F, 1);
      digitalWrite(G, 1);
    }
    
    function nine() {
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(C, 1);
      digitalWrite(G, 1);
      digitalWrite(F, 1);
    }
    
    function zero() {
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(C, 1);
      digitalWrite(D, 1);
      digitalWrite(E, 1);
      digitalWrite(F, 1);
    }
    
    function aa() {
      digitalWrite(A, 1);
      digitalWrite(B, 1);
      digitalWrite(C, 1);
      digitalWrite(E, 1);
      digitalWrite(F, 1);
      digitalWrite(G, 1);
    }
    
    function bb() {
      digitalWrite(C, 1);
      digitalWrite(D, 1);
      digitalWrite(E, 1);
      digitalWrite(F, 1);
      digitalWrite(G, 1);
    }
    
    function cc() {
      digitalWrite(A, 1);
      digitalWrite(F, 1);
      digitalWrite(E, 1);
      digitalWrite(D, 1);
    
    }
    
    function data() {
      full=(new Date()).toString();
      words = full.split(' ');
      month = words[1];
      date = words[2];
      bigtime = words[4];
      small = words[4].split(':');
      hour = small[0];
      if (hour > 12) {
        hour = hour-12;
      }
      min = small[1];
    }
    
    /*
        digitalWrite(A8, 1);
        digitalWrite(B7, 0);
        three();
        allClear();
        digitalWrite(A8, 0);
        digitalWrite(B7, 1);
        five();
        allClear();
    */
    
    function hourDriver() {
      data();
      switch (Number(hour)) {
        case 00:
        case 12:
          allClear();digitalWrite(A8, 0);
          cc();
          break;
        case 01:
          allClear();digitalWrite(A8, 0);
          one();
          break;
        case 02:
          allClear();digitalWrite(A8, 0);
          two();
          break;
        case 03:
          allClear();digitalWrite(A8, 0);
          three();
          break;
        case 04:
          allClear();digitalWrite(A8, 0);
          four();
          break;
        case 05:
          allClear();digitalWrite(A8, 0);
          five();
          break;
        case 06:
          allClear();digitalWrite(A8, 0);
          six();
          break;
        case 07:
          allClear();digitalWrite(A8, 0);
          seven();
          break;
        case 08:
          allClear();digitalWrite(A8, 0);
          eight();
          break;
        case 09:
          allClear();digitalWrite(A8, 0);
          nine();
          break;
        case 10:
          allClear();digitalWrite(A8, 0);
          aa();
          break;
        case 11:
          allClear();digitalWrite(A8, 0);
          bb();
          break;
      }
    }
    
    function minuteDriver() {
      data();
      switch(Number(min)) {
        case 00:
        case 01:
        case 02:
        case 03:
        case 04:
          allClear();digitalWrite(B7, 0);
          break;
        case 05:
        case 06:
        case 07:
        case 08:
        case 09:
          allClear();digitalWrite(B7, 0);
          digitalWrite(G, 1);
          break;
        case 10:
        case 11:
        case 12:
        case 13:
        case 14:
          allClear();digitalWrite(B7, 0);
          digitalWrite(F, 1);
          break;
    
        case 15:
        case 16:
        case 17:
        case 18:
        case 19:
          allClear();digitalWrite(B7, 0);
          digitalWrite(F, 1);
          digitalWrite(G, 1);
          break;
    
        case 20:
        case 21:
        case 22:
        case 23:
        case 24:
          allClear();digitalWrite(B7, 0);
          digitalWrite(A, 1);
          break;
        
        case 25:
        case 26:
        case 27:
        case 28:
        case 29:
          allClear();digitalWrite(B7, 0);
          digitalWrite(A, 1);
          digitalWrite(G, 1);
          break;
        
        case 30:
        case 31:
        case 32:
        case 33:
        case 34:
          allClear();digitalWrite(B7, 0);
          digitalWrite(B, 1);
          break;
    
        case 35:
        case 36:
        case 37:
        case 38:
        case 39:
          allClear();digitalWrite(B7, 0);
          digitalWrite(B, 1);
          digitalWrite(G, 1);
          break;
        
        case 40:
        case 41:
        case 42:
        case 43:
        case 44:
          allClear();digitalWrite(B7, 0);
          digitalWrite(C, 1);
          break;
        
        case 45:
        case 46:
        case 47:
        case 48:
        case 49:
          allClear();digitalWrite(B7, 0);
          digitalWrite(C, 1);
          digitalWrite(G, 1);
          break;
        
        case 50:
        case 51:
        case 52:
        case 53:
        case 54:
          allClear();digitalWrite(B7, 0);
          digitalWrite(D, 1);
          break;
        
        case 55:
        case 56:
        case 57:
        case 58:
        case 59:
          allClear();digitalWrite(B7, 0);
          digitalWrite(D, 1);
          digitalWrite(G, 1);
          break;
      }
    }
    
    function random(state) {
      
      if (state === true || state === 1) {
        clearTimeout();
        random();
      } else if (state === false || state === 0) {
        allClear();
        clearTimeout();
        setDeepSleep(1);
        return;
      }
      
      var what = Math.floor((Math.random() * 3));
      
      if (what === 0) {
        allClear();
      }
      else if (what == 1 || what == 2) {
        digitalWrite(A, Math.floor((Math.random() * 2)));
        digitalWrite(B, Math.floor((Math.random() * 2)));
        digitalWrite(C, Math.floor((Math.random() * 2)));
        digitalWrite(D, Math.floor((Math.random() * 2)));
        digitalWrite(E, Math.floor((Math.random() * 2)));
        digitalWrite(F, Math.floor((Math.random() * 2)));
        digitalWrite(G, Math.floor((Math.random() * 2)));
      }
    
      setTimeout(random, ((Math.random() * 1000) + 1));
      
    }
    
    function swirl() {
      for (delay=35;delay>0;delay=delay-2) {
        digitalWrite(B14, 0);
        digitalWrite(B13, 1);
    
          digitalPulse(A1, 1, delay);
    
        digitalWrite(B13, 0);
        digitalWrite(B10, 1);
    
          digitalPulse(A1, 1, delay);
    
        digitalWrite(B10, 0);
        digitalWrite(B1, 1);
    
          digitalPulse(A1, 1, delay);
    
        digitalWrite(B1, 0);
        digitalWrite(A7, 1);
    
          digitalPulse(A1, 1, delay);
    
        digitalWrite(A7, 0);
        digitalWrite(A6, 1);
    
          digitalPulse(A1, 1, delay);
    
        digitalWrite(A6, 0);
        digitalWrite(B14, 1);
    
          digitalPulse(A1, 1, delay);
      }
      digitalWrite(B14, 0);
      for (i=0;i<8;i++){
        digitalWrite(D, 0);
        digitalWrite(A, 1);
          digitalPulse(A1, 1, 90);
        digitalWrite(A, 0);
        digitalWrite(G, 1);
          digitalPulse(A1, 1, 90);
        digitalWrite(G, 0);
        digitalWrite(D, 1);
          digitalPulse(A1, 1, 90);
      }
    }
    
    //var l = false;
    //setWatch( function(e) { l=!l; random(l);}, B8, {repeat:true, edge:'falling', debounce:100});
    
    function onInit() {
      data();
      digitalWrite(A8,0);
      digitalWrite(B7,0);
      swirl();
      digitalWrite(D, 0);
      setDeepSleep(1);
    }
    
    pinMode(B8, 'input_pullup');
    pinMode(B5, 'input_pullup');
    
    setWatch(function (e) {
      data();
      for(i=0;i<60;i++){hourDriver();minuteDri­ver();}
      allClear();
      setDeepSleep(1);
    }, B8, {edge:"falling", repeat:true, debounce:50});
    
    /*
    var state = false;
    setWatch(function (e) {
      state=!state;
      random(state);
      setDeepSleep(1);
    }, B5, {edge:"falling", repeat:true, debounce:50});
    */
    
    setInterval(function(e) {
      data();
    }, 1800000);
    
    setDeepSleep(1);
    
    save();
    
  • ...your're obviously not doing anything wrong: [PICO CLOCK ACCURACY}(https://www.espruino.com/Pico+Clock) search provided me this link...

    Not very encouraging... but that it is that bad, I'm surprised, because I'm working on something that would need some better time keeping... glad you pointed out your 'real time' experience.

    Which makes me think about a different solution. Most simple is to add a watch crystal, which was initially @Gordon's intent for the PICO, but for various reasons it just didn't pan out. You find other entries in the forum, such as from @DrAzzy, who did work with adding a watch crystal. For 'simplicity' - when no connectivity to a time base is available - adding an RTC module is the only answer... and there are some out there w/ few wires only protocol and very easy to add...

  • Do you think a simple software solution can be added? For example, add 5 minutes to the time every 10 hours or so?

  • The question is what causes the drift and is it really predictable... but you can observe and if the target environmental conditions are stable, it may work. If you can 'afford' to spend the money on a ESP8266 WiFi module, you go once in a while to the Web and get the time for free from there. Power seems not an issue, I guess, because running a multi 7-Segment LED display needs to be fed as well.

  • Yes, I'm afraid this is just an issue with the Pico's internal RC oscillator (although if you've updated your firmware I think it's better than the original firmware that shipped on the early ones).

    You can add a crystal to the Pico, but it has to be a very specific one (Abracon ABS06-107) - and it's pretty small so is a pain to solder. Probably the easiest way around it is to add an external RTC like http://www.espruino.com/DS3231

    Are you making your clock mains-powered? If so then you could try this code:

    var now = new Date(getTime()*1000);
    var lastSysTick = peek32(0xE000E018);
    setInterval(function() {
      var st = peek32(0xE000E018);
      if (st>lastSysTick) now.ms += 0x1000000 / 80000;
      lastSysTick = st;
    }, 1);
    

    It stores the current time in a variable called now - which is calculated from the 'SysTick' timer which runs off the high speed oscillator (which should be more accurate).

    Unfortunately to do that your device can't use setDeepSleep so it'll end up drawing more power.

    I know this isn't much help, but this is actually only an issue on the Pico. The original and WiFi boards have a crystal for a low speed oscillator, and the Bluetooth LE boards are pretty good at self-calibrating. It's just unfortunate that the chip used for the Pico had an issue where you had to use a very specific oscillator type, and I wasn't able to get them fast enough in the quantity needed for production.

  • Oh well. I guess I'll use the MDBT42Q module for my future projects then.
    Is there any way for the pico to keep accurate time then? I found out that the pico tends to be about a minute late every 43 minutes from calibration. Will it be possible to write code to add a minute to the time every 43 minutes? Is it even possible?

    Thanks!

  • Have you tried the code above? Or you need the low power?

    The simplest method would be to just keep your own time using the RTC as a base. For instance this should work based on what you say about calibrating it:

    var bootTime = getTime();
    
    function getTime() {
      return new Date((bootTime+(getTime()-bootTime)*44/4­3)*1000);
    }
    
  • Also, you could always try adding the ABS06-107 crystal. It only costs around £1 (plus delivery) so it's not the end of the world if you don't manage to get it on properly.

  • Yeah, I'll try that. How accurate is the crystal? How long will it last before it drifts off? Also, are the capacitors required? I read another forum thread where someone else's crystal only worked after adding the capacitors, but in the documentation it says they aren't.

  • Supposedly the crystal is 20 PPM accuracy, so 1 second in 50000 - about 2 seconds a day.

    Well, I didn't think the caps were required (since the capacitance is so low and the board has some itself), but I just tried now and it did seem to need them - so yes, I'd add some. They're only a few pence each.

    You could always add the crystal, try, and add caps if it's not working.

    If you have access to a hot air gun, flux, and tweezers then it'll actually be pretty easy to do. I think with a standard soldering iron it's going to be a bit more of a struggle - but you could put the crystal on its side so you can solder the contacts to the pads more easily.

    You can try the following code:

    
    (function() {
      console.log("LSEON",peek32(1073887344)&1­);
      console.log("LSERDY",peek32(1073887344)&­2);
      console.log("LSION",peek32(1073887348)&1­);
      console.log("LSIRDY",peek32(1073887348)&­2);
      console.log(["NONE","LSE","LSI","HSE"][(­(peek32(1073887344)&768)>>8)]);
      console.log("RTCEN",!!peek32(1073887344)­&32768);
      // RTC subsecond
      console.log("Subsecond:", peek32(0x40002828));
    })();
    

    It should report:

    LSEON 1
    LSERDY 2
    LSION 0
    LSIRDY 0
    LSE
    RTCEN 0
    

    When it's working.

  • While a little unpleasant due to the size of the parts, since all the parts you need to add are two terminal devices, it definitely can be done with an iron. Honestly, for two terminal parts, even small ones, I find soldering iron to be easier than hot-air. I only use air for large or no-lead parts.

    You put a bit of solder onto one pad for each part, hold in place with tweezers, and melt the solder on that side to solder that end, then go back and solder the other end. Might not even need additional flux.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

real time clock drifting off

Posted by Avatar for BootySnorkeler @BootySnorkeler

Actions