Most efficient way to send data to serial?

Hello,

Sorry in advance for a long post with, probably, simple things but I just wanted to show what I tried to do to send data as fast as possible.

I made a program that measures rpm of a wheel in a dynamometer, then sends it to serial port and a c++ applications reads it, measures torque/power and draws a graph. The problem is the time between impulses from a wheel has to be very accurate - the wheel has 60 teeth and max roller speed is going to be about 6000 rpm - that means 1 impulse will come every 166 microseconds. I noticed that while measuring itself goes pretty well so far there is 1 thing that takes massive amount of time for this task - the Serial.print, which I wanted to dedicate this topic to.

I made a few simple programs that measure time of a loop:

1st test (and general idea of a program):

#include <Streaming.h>

unsigned long s;
unsigned long test=0;
unsigned long test2=0;

void setup(){
Serial.begin(9600); // The higher speed the better, but delays between different ways are more visible on lower speed.
}

void loop(){
s=micros();
test=s-test2;
test2=s; //calculation that measures time between loops
Serial.print(test2);
}

Loop time: 1604us.

2nd test - Now I wated to test how much time the calculation itself needs so I made an array that collects 10 samples and then prints them all at once:

unsigned long test3[10];
int i=0;

void loop(){
s=micros();
test=s-test2;
test2=s;
test3*=test;*
i++;
if (i==10)

  • {*
  • for (int a=0; a<10; a++){*
    Serial.print(test3[a]);
  • }*
  • i=0;*
  • } *
    }
    [/quote]
    Output: 217401281212121212812
    No spaces/lines between numbers, because thats another test. Each calculation took either 8 or 12us and thats after I added array and counter (i). Loop time with serial.print increased from 1604us to 21740us.
    3rd test - Now I wanted to put “|” after each loop time:
    > if (i==10)
    > {
    > for (int a=0; a<10; a++){
    > Serial.print(test3[a]);
    > Serial.print("|");
    > }
    > i=0;
    > }
    Output: 33176|12|12|8|12|12|12|12|12|12
    Adding a single char increased time of a serial.print loop from 21740us to 33176us. I was wondering if its really that single char that caused it so I run more tests.
    4th test - 2 serial prints each loop, one with time, one with “|”:
    > void loop(){
    > s=micros();
    > test=s-test2;
    > test2=s;
    > Serial.print(test);
    > Serial.print("|");
    > }
    Output: 5400|
    Time increased from 1604us to 5400us. I was wondering if it was just the serial print or something else so now I wanted to serial print a string with number and “|”:
    > String test3;
    > char buf[50];
    >
    > void loop(){
    > s=micros();
    > test=s-test2;
    > test2=s;
    > ltoa(test, buf, 10); //converts unsigned long to char
    > test3=buf; //test3 is now the number
    > test3=test3+"|"; //and now the “|” is added
    > Serial.print(test3);
    > }
    Output: 5200|
    Time from base loop increased from 1604us to 5200us, but lowered from double serial print from 5400us.
    I must admit I have no idea why adding the “|” increased time of the loop so much.
    Now as seen in 1st program I have a <Streaming.h> library attached, because I heard its more resource efficient, so I run a few tests on it too:
    5th test - same as 1st, just used Streaming instead of serial print:
    > void loop(){
    > s=micros();
    > test=s-test2;
    > test2=s;
    > Serial<<test;
    > }
    Output: 1604s
    Exactly the same as serial print usage. However I thought it may hasten multiple variables printing:
    6th test - Same as 4th, using Streaming instead of Serial print:
    > void loop(){
    > s=micros();
    > test=s-test2;
    > test2=s;
    > Serial<<test<<"|";
    > }
    Output: 5200|
    So indeed it lowered the time from 5400us to 5200us, as much as creating 1 String variable that contained both time and “|”
    Then I tried to create something similar my real program would send: rpm|delay+
    > int i=300;
    >
    > void loop(){
    > s=micros();
    > test=s-test2;
    > test2=s;
    > Serial<<test<<"|"<<i<<"+";
    > }
    Output: 9360|300+
    I wasnt sure if it was the variable amount or chars amount in buffer that caused time to increase so much so last test I run was:
    > int i=30000;
    >
    > void loop(){
    > s=micros();
    > test=s-test2;
    > test2=s;
    > Serial<<test<<i;
    > }

Output: 936030000
Time remained the same, even though variable amounts was lower. Im not quite sure how much time it takes to print few chars - 1604 means its about 400us per char, while 936030000 means its about 1040us per char - however it seems after certain value the 1040us per char remains the same (33176|12|12|8|12|12|12|12|12|12 = 1070us per char and there were some additional calculations).
Now my question is - is there any faster way to send data to serial port (except of increasing transmission speed, even 115200 costs a lot of time)?

1/960 = 1041.66 us

Can you save all the data. Then when done, send it?

steinie44:
Can you save all the data. Then when done, send it?

Yeah, its possible. I think best solution would be collecting data in an array and sending it all at once. However an array maximum size would be about 6000, if I wanted to calculate rpm after each revolution. My Arduino Uno has 2KB SRAM so it wouldnt handle that big array. It has 32 KB flash memory, though so I could store the data there. The question is how big delays in loop would saving data in flash memory cause. Unless there is a better solution, of course, since flash has limited times it can be written.

My Arduino Uno has 2KB SRAM so it wouldnt handle that big array. It has 32 KB flash memory, though so I could store the data there.

You can't write to flash at runtime. People have reported getting faster baud rates than 115200 - try & see if you can get it going fast enough. Alternatively, consider using an arduino ethernet and send your data over UDP.

Your post is very long - I think my brain could only handle the executive summary.

In my experience (testing for another project) the Arduino can send data to a PC pretty close to the rate you would expect for the chosen baud rate - so over 11000 chars per second at 115200 baud. And I think it will work at higher baud rates as well - but the Arduino Serial Monitor won't - I think that may be a Java limitation.

I don't think I tested for the time taken to send a single character - there may be some extra overhead.

One possibility, with limited Arduino RAM, is to read the data into a circular buffer and send it from the buffer every time 10 or 50 bytes (or whatever) is available. The reading could continue while the serial data is being sent.

I suspect you will find that Serial.write is faster as it doesn't convert values into their string equivalent. Remember that Serial.print() for an unsigned long will have to produce a lot of characters before anything caan be passed to the serial buffer.

I also would do my test timing differently - I would repeat whatever I want to measure 1000 or 10000 times and measure the total time taken.

...R