How can i speed up serial.print and serial read processing time?

Analog Read Sampling Rate: 4,000 Sample/Second.
My data is 2 Bytes for each sample point
Thus, if I want to send out 2 Bytes data before next analog read, it needs to be within 250 microseconds (1/4000 sample per second = 250 us per sample)

Baud rate is set to 115,200 , so it’s actual throughput can be around 11,520 Byte/Sec. So It takes 174 microseconds to send 2 Bytes

Sounds realistic for me.

However, calling Arduino Serial library’s Serial.Print takes about 400~600 microseconds to complete the process.

 for (i=0; i<REMAIN_NUM; i++){
        Serial.println(ch1[i]);      
    }

I guess the slow processing speed of serial.print is probably because serial.print waits until it gets return value.

“Returns
size_t (long): println() returns the number of bytes written, though reading that number is optional”

Is there any way to speed up the process time of serial.print?

I guess the slow processing speed of serial.print is probably because serial.print waits until it gets return value.

Serial.print etc does NOT wait, it adds the output to the output buffer. However if the output buffer is full then it will wait until it can add your output to the buffer before returning.

You can't speed it up.

Mark

Try a faster baud rate: 230,400

jenaflex:
Baud rate is set to 115,200 , so it's actual throughput can be around 11,520 Byte/Sec. So It takes 174 microseconds to send 2 Bytes

Sounds realistic for me.

However, calling Arduino Serial library's Serial.Print takes about 400~600 microseconds to complete the process.

Is there any way to speed up the process time of serial.print?

~~BAUD rate is not BYTE rate!!! ~~ (update: Reacted too fast, sorry)
One Baud is one unit of information, which is typically but not necessarily one bit. To send one byte the serial hardware needs a start bit and a stop bit and a time out between the bytes, typical about 1 bit's time. So in practice you need to divide by 11 to get the max number. So 115200 is 10K at best.

Please read my Post about async analogReads combined with fast Serial.

I did a little testing, and tried the following code.

byte foo[] = {'.', '.'};
byte bar[] = {'.', '.','.', '.'};

void setup() {
  // put your setup code here, to run once:
Serial.begin (115200);
unsigned long strt = micros();
Serial.write(foo,2);
unsigned long nd = micros() -strt;
Serial.println();
Serial.println(nd);
strt = micros();
Serial.write (bar,4);
nd = micros() -strt;
Serial.println();
Serial.println(nd);
}

void loop() {
}

Run this and fire up the Serial Monitor.
Results were 36 and 44 uSec.

Uh, robtillaart, of course baud <> byte. That’s sort of why he said:

Baud rate is set to 115,200 , so it’s actual throughput can be around 11,520 Byte/Sec.

lar3ry:
Uh, robtillaart, of course baud <> byte. That’s sort of why he said:

Baud rate is set to 115,200 , so it’s actual throughput can be around 11,520 Byte/Sec.

OK sorry, I reacted to quickly, I have updated my post above

and a time out between the bytes, typical about 1 bit's time.

No, the start bit can come immediately after the stop bit of the previous frame.

I thought the baud was the modulation rate and you could have more than one bit per baud. For example 256 QAM can have 8 bits per baud.

Part of the trouble is that the Hardware Serial library is so incredibly inefficient (I don’t think they could have made it less efficient if they tried!).
After each byte is sent, there is an interrupt called which spends a lifetime loading the next byte into the buffer to send.
The write function also wastes lots of time doing calculations as to where to put a character in a ring buffer.
I’ve attached a file which is a quite heavily optimised version. The only down side with it is I have used a load of inline assembler to optimise the heck out of things. This shouldn’t be an issue if I have done it properly - it is working fine for me so far.

The other issue is the print() function is very inefficient. If your ch1 variable is a character to be printed, you can use Serial.write(ch*); directly to save time.*
HardwareSerial.cpp (15.9 KB)

lar3ry:
I did a little testing, and tried the following code.

byte foo[] = {'.', '.'};

byte bar = {’.’, ‘.’,’.’, ‘.’};

void setup() {
  // put your setup code here, to run once:
Serial.begin (115200);
unsigned long strt = micros();
Serial.write(foo,2);
unsigned long nd = micros() -strt;
Serial.println();
Serial.println(nd);
strt = micros();
Serial.write (bar,4);
nd = micros() -strt;
Serial.println();
Serial.println(nd);
}

void loop() {
}



Run this and fire up the Serial Monitor.
Results were 36 and 44 uSec.

Uh, robtillaart, of course baud <> byte. That's sort of why he said:



> Baud rate is set to 115,200 , so it's actual throughput can be around 11,520 Byte/Sec.

I forgot to mention that I need to send out a 8 bytes array. My observed time is 496us

[quote author=Tom Carpenter link=topic=196328.msg1448997#msg1448997 date=1383161780]
Part of the trouble is that the Hardware Serial library is so incredibly inefficient (I don't think they could have made it less efficient if they tried!).
After each byte is sent, there is an interrupt called which spends a lifetime loading the next byte into the buffer to send.
The write function also wastes lots of time doing calculations as to where to put a character in a ring buffer.
I've attached a file which is a quite heavily optimised version. The only down side with it is I have used a load of inline assembler to optimise the heck out of things. This shouldn't be an issue if I have done it properly - it is working fine for me so far.

The other issue is the print() function is very inefficient. If your ch1 variable is a character to be printed, you can use Serial.write(ch*); directly to save time.*
[/quote]
I changed to serial.write, but for 8-byte array, it takes 496us to execute.

jenaflex:
I changed to serial.write, but for 8-byte array, it takes 496us to execute.

Odd... I added in another 8 byte array, with the same code to send and test the time, and I get this...

..
36
....
44
........
84

Don't forget, transmission is buffered; the write may return before the data has been sent.

AWOL:
Don't forget, transmission is buffered; the write may return before the data has been sent.

Good point. I am wondering what jenaflex was measuring for elapsed time.

It was suggested eralier that a byte at 115200 took 174 uSec, but according to my calculations, a byteshould take 86.8 microseconds ( 11520/1 ) or so. Of course, transferring over USE from the buffer has to be done in packets, one packet per USB frame (1 mSec or so), and I have no idea how many bytes they send in a packet.

So, the big question is "What baud rate is necessary to prevent the serial buffer from filling?"
The strange thing is that if I go above 115200, I start seeing diminishing returns...

230400:
..
36
....
44
........
88

460800
..
40
....
72
........
120

921600
..
40
....
76
........
144

So at the higher baud rates, it's taking longer to send the bytes to the serial buffer, probably because it's being emptied faster.

        time = micros();
        for (i=0;i<GYR_NUM;i++){ // GYR_NUM=8, 8 Bytes
            //inGyrData[i] = Serial.read();
            Serial.write(Serial.read());
        }
        time = micros() - time;
        
        //Serial.write(inGyrData, GYR_NUM);
        
        Serial.print("Serial Time = "); 
        Serial.println(time);

Baud rate 115200

Serial Time = 528
Serial Time = 532
Serial Time = 528

jenaflex:

        time = micros();

for (i=0;i<GYR_NUM;i++){ // GYR_NUM=8, 8 Bytes
            //inGyrData[i] = Serial.read();
            Serial.write(Serial.read());
        }
        time = micros() - time;

Ahh. OK, you are not sending 8 bytes at a time, but 8 bytes individually, and the time also includes Serial.read() of 8 bytes, again, individually. A call to a Serial routine definitely has overheard.

If this is the sort of thng you’re doing in your program, there’s definitely room for speedup. Have a look at Serial.readBytes(0. Wait until 8 bytes are there, read them all in one call to Serial.readBytes(buf,length), and write them with Serial.write(buf,length).

The library (and hardware) actually output faster (!) than the theoretical maximum as this shows:

Code:

void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  Serial.println ("1234567890");
  }  // end of setup

void loop () { }

Data output:

The theoretical speed would be:

1/11520 * 10 = 0.00086805 seconds

In other words, 868 µS. You can see we measure 851 µS, so it is about 2% faster than expected. However the datasheet shows that the USART will run at 2.1% fast at 16 MHz with 115200 selected (and USX0 set). This then agrees exactly with what the hardware is capable of.

for (i=0; i<REMAIN_NUM; i++){
    Serial.println(ch1[i]);      
}

That is, of course, sending an extra two bytes, so that isn’t helping. And depending on what ch is declared as, this could be even more than your predicted two bytes.

Plus, an analogRead usually takes 104 µS, so you have to add that in as well. You can do analogRead asynchronously, which means you can transmit the previous reading while doing the next one.

http://www.gammon.com.au/interrupts

To actually send two bytes should take:

1/11520 * 2 = 0.00017361 seconds

(roughly 174 µS).

So if you read asynchronously, and transmit two bytes (and not the extra carriage-return/linefeed) you will be closer to your target.

Baud rate is set to 115,200 , so it’s actual throughput can be around 11,520 Byte/Sec. So It takes 174 microseconds to send 2 Bytes

Actually: around 11,520 Byte/Second.

However your calculation of 174 µS was correct.

Timing inside the sketch is useless in this case because of the buffered writes. You ned to measure externally as I did.

jenaflex:

        time = micros();

for (i=0;i<GYR_NUM;i++){ // GYR_NUM=8, 8 Bytes
           //inGyrData[i] = Serial.read();
           Serial.write(Serial.read());
       }
       time = micros() - time;
       
       //Serial.write(inGyrData, GYR_NUM);
       
       Serial.print("Serial Time = ");
       Serial.println(time);




Baud rate 115200

Add:

Serial.flush()

before the line:

time = micros() - time;

That way you get the complete time for transmission to have finished (i.e. the buffer to have emptied). Granted that won’t be very accurate, but it will give you a rough indication.


P.S. The HardwareSerial.cpp file I uploaded before has a glitch in it. I’ve uploaded a correction.

HardwareSerial.cpp (16.4 KB)

Actually: around 11,520 Byte/Second.

You're the second guy who misread what he typed.

Timing inside the sketch is useless in this case because of the buffered writes. You ned to measure externally as I did.

That's good info, Nick. I wonder, is there a way to get a good idea of the optimum number of bytes to send in one call to Serial.write() or Serial.print()?