Accuracy of setInterval

Posted on
  • Hi

    I'm trying to add high-quality logging and I want to put an accurate time-stamp
    on each line. Gordon has provided an example using setInterval to increment
    an internal clock global variable, which can then be read by other code. But my
    experience of other microcontrollers made me a bit concerned about how this
    might work. For example, perhaps there's some other code which delays setinterval
    by a few MS - not significant really but perhaps this might miss the rollover to the
    next second.

    So, I've just written a quick test program. It uses setInterval to setup a callback every
    1 second, and then calls getTime() to see how close setInterval is being called to every second. (I couldn't get the upload code attachment to work, so it's pasted below)

    The results are quite surprising - setInterval is calling my target function with an accuracy of less than 1/10 ms. To try to push it off track, I've setup another 10
    setIntervals, all just wasting time. It doesn't affect it... even if I make them waste
    a lot of time. (This is a bit of a puzzle, it almost suggests that successive setIntervals
    will interrupt each other to make sure they all start close to target time??)

    Of course, it's possible that some other activity - e.g. hardware interfacing might hit the accuracy if interrupts are off for a while.

    var startTime=0;
    var seconds=0;
    var acc=0;
        
    function checkTime()
    {
        var t=getTime();
           
        seconds++;
      
        var d=(t-startTime)-seconds;
        acc = acc+d;
        
        print(seconds,t-startTime,d, acc/seconds, (d-acc/seconds)*1000);
    }
    
    
    function wasteTime()
    {
    var a,b,c;
      
        for(var i=0;i++;i<200)
          a=b*c+b*a;
    }
    
    function testTime()
    {
        startTime=getTime();
      
        for(var i=0;i<10;i++)
          setInterval(wasteTime,1000);
      
        setInterval(checkTime,1000);
    }
    
  • There's a bit of a delay caused by executing the JavaScript itself (it's not that fast). If you instead do:

    function checkTime()
    {
        var t=getTime();
        if (startTime==0) startTime=t-1;
    ...
    
    function testTime()
    {
        startTime=0;
    ...
    

    So you're comparing subsequent setIntervals with the time from the first, you should get a much better accuracy.

    Espruino isn't pre-emptive, but it tries to schedule intervals so that on average they are very accurate. If you add a watch crystal to Espruino you can happily run it as a reasonably accurate clock using nothing more than setInterval(...,1000).

    So for instance something like this could happen:

    checkTime called at time 3.3 (0 sec)
    wasteTime called at time 4.2, takes 300ms
    checkTime called at time 4.5 (1.2 sec)
    checkTime called at time 5.3 (2 sec) <----- 
    

    Hope that helps. It's worth noting that by default Espruino uses the internal RC oscillator for time, which could be +/- 2%. For proper logging you'll want to add a watch crystal - see http://www.espruino.com/Clocks

  • Gordon

    Thanks. I'll certainly be using the crystal, but drift in the clock bothers me less than scheduling inaccuracies.

    I'd note that my testing so far really shows quite surprising accuracy - even if I change wastetime() to loop for 20k times, it's still accurate. So much so that I
    think something must be wrong....

    OK, some questions

    1. You say it's not pre-emptive - does that mean if I have a time-consuming function which lasts 10 seconds, setIntervals won't get called at all? (I know I shouldn't do this, but ...)

    2. Is there anything in the system which might cause it to block setIntervals for some largeish time period - I'm thinking bit-bang serial, I2C, CC3000..

    3. I presume getTime() returns a value that is read directly from hardware when you call it?

    I see two solutions to deliver an accurate GetDateTime function which includes both calendar date and milliseconds:

    1. Your suggestion - a global date-time variable updated every second

    2. A getDT function which, when called, reads GetTime and updates a global clock variable according to time elapsed since the last time it was called.

    If setInteval can get delayed, and I have long-ish functions, then I suspect I need to use #2. The problem is that so far I can't duplicate a problem case :)

    Martin

  • You say it's not pre-emptive - does that mean if I have a time-consuming function which lasts 10 seconds, setIntervals won't get called at all?

    Yes, that's true. However setWatch will log input state changes using IRQs, and Waveform and digitalPulse work in similar ways too so will be unaffected.

    Is there anything in the system which might cause it to block setIntervals for some largeish time period - I'm thinking bit-bang serial, I2C, CC3000.

    Yes - The main offender is CC3000, which is a total disaster thanks to TI :( However if you have no active HTTP clients or servers then it should be fine.

    I presume getTime() returns a value that is read directly from hardware when you call it?

    Yes and no. It's based on the internal RTC, but I do some magic with SysTick that allows it to work down to 1uSec even though the RTC runs at 32/40kHz.

    I see two solutions to deliver an accurate GetDateTime function which includes both calendar date and milliseconds:

    Your suggestion - a global date-time variable updated every second

    From the Clocks page? It's there because it's nice and easy to understand - the suggestion below is better though.

    A getDT function which, when called, reads GetTime and updates a global clock variable according to time elapsed since the last time it was called.

    Yes, that's probably the best bet - especially from the point of view of power saving. However, a proper implementation of Date and a value that stored how many seconds you have to subtract from getTime() to get to 1 Jan 1970 would be pretty good.

    If setInteval can get delayed, and I have long-ish functions, then I suspect I need to use #2. The problem is that so far I can't duplicate a problem case :)

    I'd use #2 anyway - or my #3. Please don't let functions run for over a second though - it makes me sad :) In pretty much all cases there are ways to work around it, and Espruino will work a lot better.

  • Thanks. I'll look at Date() - it would be nice to follow standards.

    Don't worry - I'm not going to let functions run for a second. I was trying to simplify my post by using an over-egged example :)

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

Accuracy of setInterval

Posted by Avatar for mgg1010 @mgg1010

Actions