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);
}
Philip Allgaier
  • 245
  • 3
  • 13
hawkar
  • 553
  • 2
  • 6
  • 12

3 Answers3

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.

Cybergibbons
  • 5,420
  • 7
  • 34
  • 51
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.

JRobert
  • 15,407
  • 3
  • 24
  • 51
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)