10

I am using an Arduino Uno to send time and voltage information over the serial port to Python to plot. However the interval timings between successive time stamps appears to be increasing over time, affecting my plotting. This is especially true when the baud rate is set to 9600, where my initial time differences maybe 1320 and increases to 16400 after a relatively short period of time. When this rate is put to maximum of 115200 bps the change is slower and less noticeable, from around 1340 to 1500 even after a relatively long run of sending. All times are given in microseconds.

I would like to know if I can reduce or eliminate this effect, and if not understand why it exists. I have read things about interrupts and delays causing this, but I do not fully appreciate the complexity of the electronics at hand and would like to know:

  1. Can I get greater precision in the timing?
  2. What causes this change in timing?

Here is what I currently have:

#include <eHealth.h>

extern volatile unsigned long timer0_overflow_count;
float fanalog0;
int analog0;
unsigned long time;    

byte serialByte;
void setup() {
  Serial.begin(9600);
}

void loop() { 
  while (Serial.available()>0){  
    serialByte=Serial.read();
    if (serialByte=='S'){        
      while(1){
        fanalog0=eHealth.getECG();  
        // Use the timer0 => 1 tick every 4 us
        time=(timer0_overflow_count << 8) + TCNT0;        
        // Microseconds conversion.
        time=(time*4);   
        //Print in a file for simulation
        //Serial.print(time);
        //Serial.print(" ");
        Serial.print(fanalog0,5);
        Serial.print("\n");

        if (Serial.available()>0){
          serialByte=Serial.read();
          if (serialByte=='F')  break;
        }
      }
    }
  }
}
  • What do you mean by "precise"? The times given by the counter are going to be fairly precise, accurate and with a good resolution. Do you want the times to be deterministic (i.e. always the same)? – Cybergibbons Mar 11 '14 at 11:48
  • Apologies, yes I guess that is what I meant, for the difference between them to be consistent and if not, the reason why they are not – user3284376 Mar 11 '14 at 12:02
  • Add the timestamp at PC end rather than Arduino end, or use a RTC (real time clock) module. RTC modules are pretty cheap to find on various webstores, just ensure that the store links to the datasheet. Another method is to program a timer and use an interrupt service routine to get reasonably accurate timing. – jippie Mar 11 '14 at 17:09
  • What does eHealth.getECG() do? Does that call always last the same amount of time? – jfpoilpret Mar 11 '14 at 20:33
  • Can you specify how long a "relatively short period of time" lasts? Is it always the same after restarting the Arduino? – jfpoilpret Mar 11 '14 at 20:56
3

I can think of a few things that can impact the "consistency" of the serial write timings:

  • size of the data to be printout

this may be the most obvious thing to think of, but indeed the more you print, the more it'll take to handle it.

Solution: print format the string into a string of known length.

  • using buffered serial

on unix you can access the serial port using a buffered or an unbuffered way. Using the buffered way for a long time may make it a bit slower as the buffer fills, usually it happens when data is incoming faster than you're reading it…

Solution: use the unbuffered serial line (e.g.: on Darwin/OSX it's /dev/cu.usbmodemXXX instead of /dev/tty.usbmodemXXX)

  • priority of the timers

it looks like your using a TC interrupt, and AVRs have priorities in the way interrupts are handled, I don't know the order of priority for the Atmega328, and it's not one of the most documented feature around, so I don't know how safe is TC0 versus the UART interrupt.

Solution: look up further in the documentation/datasheet about interrupt priorities and change the timer if needed ; and/or do a test without having the other timer running.

  • the data you're reading from takes more time to read from over time

some drivers need to average or do some operations over the previous values, so the more values you measure, the longer the buffer is, and the longer it takes to calculate the value, until you've reached the maximum size of the buffer.

Solution: look at the source code of the library you're using, and either optimize it, remove the calcul if there's one or take that increasing processing time into account.

  • avoiding the arduino framework overhead

but if you really want to optimize serial output from the arduino, you should avoid using the arduino overhead… But it's way less elegant and comfortable to use.

I'm pretty sure there are other points I'm missing, but that's the first things that I'd check before digging further.

HTH

3

Use a timer and ISR (interrupt service routine) to make timing more accurate.

Take a look at my 1ms timed interrupt Proof of Concept. The idea is to have a reasonably accurate 1ms 'heartbeat' in the system that can be used to trigger other events. In the PoC it is used to blink an LED at ½Hz, but having access to the new variables millisecondCounter and secondCounter enables you to trigger events in the main loop at arbitrary (but accurately timed) moments.

  • 2
    Your PoC is very interesting but it has a flaw (easy to fix) in the fact that it reads a 2-byte value while interrupts are enabled (in loop()), this value being modified by an ISR. It can happen that loop() reads a bad value (in the middle of a modification by the ISR). I've posted a comment on your blog about it. – jfpoilpret Mar 12 '14 at 6:32
  • @jfpoilpret interesting point you make there, never thought of an interrupt occuring half way retrieving the value from RAM. I'm going to check the disassembly this evening and update the article. Maybe a good reason to write another article too :o) – jippie Mar 12 '14 at 6:49
  • I created a sample from your PoC and could see the problem occur at least once every 10 seconds on my UNO. But of course in reality it highly depends on what you do in your loop(): my sample just got the milliseconds value, ompare it to the previous read value and if difference > 0 (other than reset counter to 0) , display a message. – jfpoilpret Mar 12 '14 at 6:52
  • @jfpoilpret never really noticed it. I just use it as a heartbeat to monitor the food buckets for my cats and make an LED flash when my cats will be potentially disappointed ... ;o) It definitely will change the way how I use ISR's in future. – jippie Mar 12 '14 at 6:56
  • 1
    It shows a crystal connected to a block ATMEGA16U2 and a resonator connected to ATMEGA328P-PU. The 16U2 is for the serial interface the 328P is "The Arduino". Interestingly enough the 16U2 would be able to push its clock to another chip, e.g. the 328P. – Udo Klein Apr 28 '14 at 13:58
2

Your code includes the duration of the output in subsequent measurements. Thus depending on the length of the output you will measure different times. This can be fixed by formating to fixed length output.

The next issue is that the UNO has a very poor timebase. Have a look here for a comparison of different Arduino types vs. the DCF77 time reference.

Conclusion: if you need precise timing either get an Arduino with crystal or go for an RTC. I can highly recommend DS3231 / DS3232 RTCs since these usually achieve 2 ppm accuracy out of the box.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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