10

I have been using the Arduino to record some data. In my Arduino sketch I also used the millis() function so I can keep track of the time at which each value I am measuring is taken. However, I noticed that the timing isn't correct. For example 30 seconds in real life only comes out as 10 seconds (made up example).

Am I correct in saying that the Arduino delay function affects the time keeping using millis()? In other words suppose I have a delay of 50ms, does that mean the millis() function stops for that duration as well and then continues and so on for the duration of the connection? I noticed this when I tried plotting some data and finding that the frequency of the peaks in my data was too frequent given the time which had passed by. So I want to know if that is the reasoning for this mismatch of timing and if so, how do I fix this so that I can keep the time each sample occurs?

To give some context here is my sketch:

#include <eHealth.h>    

unsigned long time;
// The setup routine runs once when you press reset:
void setup() {
  Serial.begin(9600);  
}

// The loop routine runs over and over again forever:
void loop() {

  float ECG = eHealth.getECG();
  time = millis();
  Serial.print(time);
  Serial.print(" ");
  Serial.print(ECG, 5); 
  Serial.println("");    

  delay(50);
}
4
  • Are you using one of the official Uno boards? Commented Mar 10, 2014 at 0:11
  • 1
    Actual timing instead of made-up values (a serial monitor that timestamps the lines is ideal) would probably help figure out what's going on. Commented Mar 10, 2014 at 2:35
  • 3
    The computation of millis() is interrupt driven, so delay() should not affect it. Commented Mar 10, 2014 at 3:17
  • I have the same problem but only when i integrate it (millis()) into complex code. I guess complexity of the code affects its accuracy in way it goes more and more into delay with the complexity of the code. Is there any way to avoid this? maybe using separated RTC module?
    – Josip7171
    Commented Nov 16, 2016 at 10:59

3 Answers 3

10

millis() is interrupt driven so delay() won't impact it, at least not on an ATmega based board.

That isn't to say that millis() is totally accurate either. Each tick of the timer is not exactly 1ms, but is 1.024ms. This error gradually accumulates until a correction is made. This can be seen in the implementation of the TIMER0_OVF (timer 0 overflow) interrupt handler.

Another source of inaccuracy is the oscillator/crystal itself, which is not exactly 16MHz. It is pretty close though, and as long as temperature doesn't change too much, is relatively stable.

The above means that you might be about 1ms out when using millis(). This doesn't sound like your problem.

Another potential issue would be what getECG() is doing - it might be very slow.

float eHealthClass::getECG(void)
    {
        float analog0;
        // Read from analogic in. 
        analog0=analogRead(0);
        // binary to voltage conversion
        return analog0 = (float)analog0 * 5 / 1023.0;   
    }

analogRead() is slow, but not so slow as to impact a loop like this.

Another problem I have seen people have is when they change the clock speed but don't correctly change boards.txt. This means that the constants used in the millis() implementation are wrong and the times are wrong.

If you actually want to read values every 50ms, a much better way of implementing this is to do the following

static long lastUpdate;

if (millis() - lastUpdate > 50)
{
    lastUpdate = millis();
    //Do stuff
}

We'd really need to see the timestamps you are getting. If you actually seeing 30s showing as 10s, then there is something else at work.

6
  • 2
    Please note that, for the Uno, the clock is not crystal-driven, but just uses a ceramic resonator which is less accurate than a crystal.
    – jfpoilpret
    Commented Mar 10, 2014 at 9:25
  • @jfpoilpret Ah good to know. Looking at the schematic, this would be the CSTCE16M0V53-R0 Murata CERALOCK device.
    – Chris O
    Commented Mar 10, 2014 at 14:33
  • Resonators have a poor initial tolerance (often 0.5-2%) and poor temperature stability, but if you calibrate timing loops when using them, they can be fine as long as temperature doesn't move. Commented Mar 10, 2014 at 15:33
  • 2
    Millis() still works on a timer that ticks every 1.024ms, but they added error compensation, in the form of incrementing whenever an error meter variable gets too high. I think it's Roman Black's algorithm actually. So timing should be much closer to 1ms exactly.github.com/arduino/Arduino/blob/master/hardware/arduino/cores/… Commented Mar 11, 2014 at 3:27
  • For those still interested, see the comment I have posted on JRobert's answer, I didn't want to reply with my own answer since I don't have one, I have just rephrased the problem.
    – hawkar
    Commented Mar 11, 2014 at 16:36
2

If interrupts are turned off for any significant fraction eHealth.getECG() call duration, millis()'s count could fall behind. Otherwise, millis() should return much more accurate time than the 3x errors you described.

You said your sampled signal appears higher in frequency than what you expected, which could happen if your sample rate is lower than you intended. Are you a assuming a 20Hz sample rate? Your loop could be taking a fair bit longer than 50ms, which you would see in the printed times, but those should still track the clock time. If you didn't account for that but assumed 50ms/sample, you'd see an apparent speed up of of the data.

If this is not the issue, then the next step would be to toggle an output while you're in loop(), and measure the frequency of the resulting square wave with a frequency meter (some inexpensive DVMs can do this) or a 'scope. Do the same thing with an empty loop(). The first experiment will your real sampling rate or interval; the second will tell you whether millis() (ie., the timer0 frequency) is what you expected.

2
  • 1
    I have been playing around with it further and have come to realise that the issue is not with the Arduino, the millis() function for the most part works very well, some of the values are not exactly 8ms (delay) apart but from what you have said that is to be expected. The 3x error I described is true of the Python side of things which I using to receive the data. Any idea what that could be a result of, I am using Python's pyserial and it is slow as hell.
    – hawkar
    Commented Mar 11, 2014 at 16:34
  • I don't know enough about your implementation to give you more than a 1/2@'d guess, but is the Python side slow enough to be dropping samples?
    – JRobert
    Commented Mar 11, 2014 at 16:39
0

Same here. I can add that if interruptions are turned off, the time measured is "real time". Anyway, I dont understand why this delay, because if the loop take too long, anyway the millis() should return real time values (only with more distance between each value)

3
  • 1
    What does "same here" refer to? Answers should stand on their own, as StackExchange can re-order things (unlike a forum). So "same here" could mean anything depending on what reply/question your answer appears underneath.
    – Nick Gammon
    Commented Sep 3, 2015 at 20:51
  • This post would be more appropriate as a comment, although (admittedly) you lack sufficient reputation . Commented Sep 14, 2015 at 0:49
  • Sorry, I though when you answer something, its obvious it's refering to the main post, otherwise it would be a comment
    – user48711
    Commented Nov 12, 2015 at 19:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.