Serial Receive data rate much lower than expected

I tested data transfer rates between a PC and Arduino using a very simple sketch.

I am using a baud rate ot 115,200 bps. At 8 bits / byte, this should ideally be 14,400 bytes / sec. Assuming we have a start and stop bit, and it actually takes 10 bits to send 1 byte of useable data, this drops to 11,520 bytes / sec.

However, when I ran my test with actual data and timing the results, I found a large discrepancy. Arduino doesn’t seem to have a problem sending the data at the correct data rate. When the Arduino is the receiver, the actual data rate is only 30% of what it should be!

Arduino → PC = 8,400 bytes / sec. Ideally this would be 11,520 bytes / sec, but it’s somewhat close.
PC → Arduino = 3,400 bytes / sec. This is nowhere near 11,520 that it should be.

Increasing the baud rate beyond 115200 does not seem to make any difference on the send or receive speed.

I realize this may be due to the 128-byte receive buffer limitation. (I tested with a larger buffer in HardwareSerial.h, with no improvement.) Is there a faster way to receive all the data in the receive buffer without calling Serial.read() 128 times? I would really like to move all the data in the receive buffer out at once, since I think that the Serial.read() function might be really slowing things down.

Any thoughts? Anyone test their actual send / receive data rates and could share?

I’m really hoping to achieve a data rate of 8,000 bytes / sec or higher if I can for the Arduino’s receive.

Here’s my test code. I have a program running on my PC which sends data to the Arduino as fast as it can and does the timing. Every 256 bytes received, the Arduino sends the number of BytesRead back to the PC. The PC can then calculate the data rate as: (BytesRead / (current time - start time)).

/*
SerialSpeed2
21 August 2010

Test the sending speed for a serial connection.
9,600 baud = 960 Bytes/sec
115,200 baud = 11,520 Bytes/sec
*/

long BytesRead=0;

void setup()
{
Serial.begin(115200);
establishContact();
}

void establishContact() {
while (Serial.available() <= 0) {
digitalWrite(13, HIGH); // set the LED on
Serial.println(“Waiting For Connection…”); // send an initial string
delay(300);
digitalWrite(13, LOW); // set the LED off
delay(300);
} //End While
} //End Establish Contact

void loop()
{

while (Serial.available() > 0)
{
Serial.read();
BytesRead++;
if (BytesRead % 256 == 0)
{
digitalWrite(13,!digitalRead(13));
Serial.println(BytesRead);
}//End if
}//End While
}// End Loop

Assuming that the Arduino isn't dropping bytes (can you tell?), the speed that the arduino receives data is completely dependent on how fast the other side (PC+USB+FTDI) is sending the data. So perhaps the PC is not sending data any faster than 3400B/s... Given the structure of windows, and USB, this is not as unlike as it might seem, especially if the remote side is sending one byte at a time. USB has LOTS of overhead per "message." As does Windows "per system call."

Thank you westfw. You were exactly right. There appears to be quite a bit of overhead for sending one byte at a time.

I increased the amount of data the PC was sending each time. Instead of sending just 1 byte, I increased it to send 256 bytes at a time. This apparently works much better, as now I am getting aprox 10,000 bytes / sec. That's much more like what I was expecting.

Now I will increase by baud rates and see if it increases beyond that.

However, I am still wondering if there is a better way to receive data on the Arduino. Can I copy the entire receive buffer to another location somehow?

It appears that increasing the Baud rate beyond 115,200 also increases the transfer rate, but this requires also increasing the amount of data sent at a time to 2048 bytes or more at a time to see any performance increase.

I just looked into the code of Hardware Serial. According to the amount of code I see, I should estimate it to need around 50us per byte. But this very very unprecise!!

The UART is "double buffered" which means that the next byte is assembled by the hardware whilst the software can process the current byte. So at 90us per streamed byte there is still a little bit room for user processing, but not much.

The same applies to transmitting bytes, but as you have measured 110us per byte - most likely quite accurately - this indicates that this is the time the Arduino needs for the handling of Serial.write, including your loop overhead.

It seems 115k baud is the limit. If you cancel the call to Serial.available() you might gain a little bit. The bottleneck however seems to be the transmission. This is a little bit funny, as the code for transmission is very lean in hardwareSerial...

With a bit of further testing, I was able to boost the data rate to about 20,300 bytes / sec. (162,400 bits / sec). That was sending 2500 bytes at a time from the PC. I'm not sure what the loss rate is yet, if any. I'll post back when I have that data. That's a little more than 115,200, but not a whole lot.

With a bit of further testing, I was able to boost the data rate to about 20,300 bytes / sec. (162,400 bits / sec). That was sending 2500 bytes at a time from the PC. I'm not sure what the loss rate is yet, if any. I'll post back when I have that data. That's a little more than 115,200, but not a whole lot.

The arduino serial library uses a rather small input FIFO buffer, 128 bytes I think. Wouldn't the actual reliable data rate also depend on how much other stuff the arduino sketch has to do?

Lefty

A buffer is meant to mitigate peak traffic and has little to do with max throughput.... 128 bytes is my no means small.

A buffer is meant to mitigate peak traffic and has little to do with max throughput.... 128 bytes is my no means small.

That didn't answer my question. Isn't the max throughput for an Arduino dependent on how much other stuff the Arduino is trying to do in addition to servicing the serial data input. I would think that it wouldn't take too many blocking statements to overflow the buffer at 20k bytes a second?

Lefty

I would think that it wouldn't take too many blocking statements to overflow the buffer at 20k bytes a second?

Absolutely. Unless I've made a stupid miscalculation (quite possible!) it would take just 6.4ms to fill the buffer at that rate. The sketch really can't afford to be held up for very long before bytes start falling on the floor.

There is no blocking, as Serial works interrupt driven. Nevertheless all code that has to be performed for any byte must be processible in a (slightly) shorter time than the byte transfer time. The buffer just allows that this need not be hard synchronized.

it wil be different when you set-up a really HUGE buffer...

Absolutely. Unless I've made a stupid miscalculation (quite possible!) it would take just 6.4ms to fill the buffer at that rate. The sketch really can't afford to be held up for very long before bytes start falling on the floor.

Thanks and that was my point. Knowing what the absolute max throughput for the serial port is certainly a interesting tidbit, but certainly if a specific sketch is say using other interrupt ISR functions, performing other I/O operations or lots of floating point operations, then certainly the buffer can be overflowed?

I would think it would always be better to implement some kind of flow control with the PC program rather then just allow it to send 20kB/sec continuously?

Lefty

I think you are barking up the wrong tree. The main problem is: How shall that - received - data be processed? See my remark above. 6.4 ms is eternity! There is hardly any chance to increase the buffer to 512 bytes. Would be 25 ms then.. So what?

You always need the algorithm to assess needed resources. Finding out the limits however is fine.

In fact I had been very astonished to hear that 115k really works - never dared to try it before ;D

How are you measuring the "speed" ?

Sorry, I missed that part in the original post.

You need to consider not only the baud rate between the Arduino and the FT232R, but also the buffer in the FT232R and the latencies in the PC and FT232R. The FT232R has 128 bytes of buffer from PC to Arduino, sending data in multiples of 64 is most efficient since packets USB packets are full.

The FT232R has 256 bytes of buffer from Arduino to PC. It also has a configurable latency which defaults to 16ms. This can be reduced to 1ms in the COM port configuration. This does mean that even though you get a high "speed" for larger packets, a single character from arduino to PC will take 16ms, so mess up your "speed" calculations on small packets.

I have revisited the original speed calculations. There was no flow control, so the PC was just spitting data at the Arduino as fast as it possibly could. This meant that the 128 byte buffer on the Arduino was being over-run constantly. However, this kept the buffer full all the time, which increased the bytes / sec.

So, if you only care about the most current data, and don’t care if old data is overwritten, then you can sustanably reach 20,000 bytes / second.

However, this was not the desired outcome for my application. I implemented some flow control, but that drastically limits the data rate. There’s a bit of lag time between telling the PC to send more data and when it actually arrives. In most cases the receive buffer completely empties before more data arrives.

Changing the latency on the FTDI COM connection seems to help a little, since the packets must be < 128 bytes in order not to overflow the buffer.

At lower baud rates, you get better performance by sending packets that are half of the buffer size (ie: 64 bytes). At higher baud rates (above 57600) it does not really matter if the receive buffer empties or not, because the data comes in fast enough that you can refill an entire buffer at once rather than requesting half-buffer packets.

The real limitation is how fast you can empty the receive buffer. Is there a way that I can copy the entire buffer as a byte array instead of having to call Serial.read() 128 times?

Is there a way that I can copy the entire buffer as a byte array instead of having to call Serial.read() 128 times?

And then what? Remember that the Arduino only has 2k (or less) of RAM in the first place. Once your data is in your private buffer instead of the library buffer, how are you going to process it faster?

For the highest speed, replace the existing serial ISRs with ones that know about your data format and give you appropriate "chunks" (packets, keywords, whatever.)

That said, I would expect than a 16MIP CPU like the Arduino's AVR to run into the limits of its abilities at rates not too much faster than 115200bps, assuming that you need to do something with the data other than just throw it away. High speed access takes smarter hardware. (For fun, turn off an LED in the serial ISR, and turn it onwhen you leave. Then watch how dim it gets as you increase the bitrate. (the brightness indicates the amount of CPU left over to DO something with the data. The last time I did this sort of thing (on a 10MHz xx186 system), the results were really depressing, since that box was supposed to handle 8 simultaneous ports.)

I'm using a circular buffer to eliminate the 2k ram issue. I just need more data buffered at a time than just 128 bytes. 1024 bytes would work much better. So, if I could copy the 128 byte buffer into a location of my 1024 byte circular buffer, this would increase performance for me.

There is minimal processing of the data going on, it's just critical that my circular buffer does not run out of data.

I'll check out modifying the core Serial ISR's to see if I can have them return me a pointer to the buffer and a buffer length or something like that. I just hate modifying the core files because it makes it so that the code can't be used easily by others.

I just need more data buffered at a time than just 128 bytes. 1024 bytes would work much better. So, if I could copy the 128 byte buffer into a location of my 1024 byte circular buffer, this would increase performance for me.

Well if you have SRAM to spare wouldn't it be much simpler to increase the existing serial buffer size to what you want?

In the Arduino core there is C:\Documents and Settings\Primary Windows User\My Documents\My Programs\Arduino\arduino-0018\hardware\arduino\cores\arduino\HardwareSerial.cpp that has a define for the buffer size:

# define SERIAL_BUFFER_SIZE 128

Just increase that and you should be good to go?

Lefty

I looked into that. I don't think it works that way. I think the 128 byte serial buffer is a hardware limiation. All the documentation I can find about it says that the buffer size can be reduced below 128, but that's the maximum.

You might be able to get a larger buffer on NewSoftSerial, but that only supports bauds up to 57,600 (7,200 bytes / sec) and I need slightly faster than that.

I looked at the ATMEGA328p datasheet... I actually can't find anything in there about a 128 byte buffer. Maybe it can be changed.