The current bottleneck for my Rainbowduino project is the serial latency of the Arduino FTDI USB-Serial chip (currently 20ms round trip time from Processing to Arduino and back). There are some OS specific driver tweaks to improve the latency – but not for Mac OSX. The new Arduino UNO should have better latency times, accoring to the Arduino Website:
…We replaced the aging FTDI chipset with a custom made usb-serial converter built with an Atmel ATmega8U2 this provides lower latency and doesn’t require to install any drivers on mac and linux…
As I don’t own a Arduinio UNO (yet) I asked in the Arduio Forum about UNO’s serial latency. Paul Stoffregen did some nice benchmarks about the Arduino boards (thanks again!), here are the results and the test-setup.
Arduino Firmware: neoLed
The results for sending 7 bytes and receive 4 bytes on Mac OSX:
| Teensy | ~1ms |
| Arduino UNO | ~4ms |
| Arduino Duemillanove | ~16ms |
So the new Arduino UNO’s serial latency is definitive better than the latency of the duemillanove.
Edit 10.2.2011:
However I use the Arduino from a Processing sketch – which uses the RXTX lib. And the RXTX lib use JNI to access the serial port – and after playing with the RXTX lib (changed some timeouts, changed JNI versions…) I guess the JNI interface adds some ms of latency. So the minimum latency using the RXTX lib was 20ms, period!
Peter also added a footnote about this measurement:
Something to keep in mind is this benchmark is only for the 7 byte TX, 4 byte RX case, and I only tested on Mac OS-X 10.5, and only for this specific C program. (I tried Linux briefly but had a lot of trouble getting Uno to work reliably). If you transmit 96 bytes, at 115200 baud between the chips, that’ll add about 8.3 ms. 96 bytes might change things for Teensy too, since that will become more than 1 USB packet.
How the software writes I/O operations also can matter greatly. In this test, I did a single 7 byte write, a single poll, and a single 4 byte read. In other testing I did with Puredata a few months ago, results varied quite a lot on each operating system. The size of the actual writes from the app to operating system matter greatly (only OS-X will combine smaller I/O operations together… Windows and Linux will happily pass single byte writes to the USB host controller, making horribly inefficient use of the bandwidth). Processing may or may not do things efficiently. It’s amazing how much difference it can make, especially on Windows due to what seems to be poor driver design. The point is differently designed software could attempt the same communication and get completely different results, especially if issues single-byte I/O operations.
Here are the test result details and test C program:
Arduino Duemilanove:
1 2 3 4 5 6 7 8 9 10 11 | port /dev/cu.usbserial-A800daD3 opened, waiting for board to boot up sending 7 bytes, read 4 bytes, elased: 11.75 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms sending 7 bytes, read 4 bytes, elased: 15.87 ms sending 7 bytes, read 4 bytes, elased: 16.02 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms sending 7 bytes, read 4 bytes, elased: 15.96 ms |
Arduino Uno:
1 2 3 4 5 6 7 8 9 10 11 | port /dev/cu.usbmodem411 opened, waiting for board to boot up sending 7 bytes, read 4 bytes, elased: 3.56 ms sending 7 bytes, read 4 bytes, elased: 3.96 ms sending 7 bytes, read 4 bytes, elased: 3.96 ms sending 7 bytes, read 4 bytes, elased: 3.97 ms sending 7 bytes, read 4 bytes, elased: 3.97 ms sending 7 bytes, read 4 bytes, elased: 3.96 ms sending 7 bytes, read 4 bytes, elased: 3.96 ms sending 7 bytes, read 4 bytes, elased: 4.98 ms sending 7 bytes, read 4 bytes, elased: 3.96 ms sending 7 bytes, read 4 bytes, elased: 3.96 ms |
For Teensy (with Serial.send_now() added)
1 2 3 4 5 6 7 8 9 10 11 | port /dev/cu.usbmodem12341 opened, waiting for board to boot up sending 7 bytes, read 4 bytes, elased: 0.85 ms sending 7 bytes, read 4 bytes, elased: 0.95 ms sending 7 bytes, read 4 bytes, elased: 0.87 ms sending 7 bytes, read 4 bytes, elased: 1.03 ms sending 7 bytes, read 4 bytes, elased: 0.98 ms sending 7 bytes, read 4 bytes, elased: 0.96 ms sending 7 bytes, read 4 bytes, elased: 0.96 ms sending 7 bytes, read 4 bytes, elased: 0.96 ms sending 7 bytes, read 4 bytes, elased: 0.96 ms sending 7 bytes, read 4 bytes, elased: 0.96 ms |
The C Source to measure the latency (also done by Paul Stoffregen):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | // compile with: gcc -O2 -Wall -o latency_test latency_test.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <fcntl.h> #include <poll.h> #include <termios.h> #include <unistd.h> #define PORT "/dev/cu.usbserial-A800daD3" // Duemilanove //#define PORT "/dev/cu.usbmodem411" // Uno //#define PORT "/dev/cu.usbmodem12341" // Teensy //#define PORT "/dev/ttyUSB0" // Duemilanove on Linux //#define PORT "/dev/ttyACM0" // Uno or Teensy on Linux #define BAUD B115200 void die(const char *format, ...) __attribute__ ((format (printf, 1, 2))); int main() { int r, fd, count; struct termios tinfo; unsigned char buf[7]; struct pollfd fds; struct timeval begin, end; double elapsed; fd = open(PORT, O_RDWR); if (fd < 0) die("unable to open port %sn", PORT); if (tcgetattr(fd, &tinfo) < 0) die("unable to get serial parmsn"); if (cfsetspeed(&tinfo, B115200) < 0) die("error in cfsetspeedn"); if (tcsetattr(fd, TCSANOW, &tinfo) < 0) die("unable to set baud raten"); printf("port %s opened, waiting for board to boot upn", PORT); sleep(3); for (count=0; count < 10; count++) { // send the ping request buf[0] = 1; buf[1] = 0; buf[2] = 1; buf[3] = 4; buf[4] = 0x10; buf[5] = 2; buf[6] = 0x20; printf("sending 7 bytes"); gettimeofday(&begin, NULL); r = write(fd, buf, 7); if (r != 7) die("unable to write, r = %dn", r); // wait for a responds fds.fd = fd; fds.events = POLLIN; poll(&fds, 1, 500); r = read(fd, buf, 4); gettimeofday(&end, NULL); printf(", read %d bytes", r); if (r != 4) die ("unable to read 4 bytesn"); elapsed = (double)(end.tv_sec - begin.tv_sec) * 1000.0; elapsed += (double)(end.tv_usec - begin.tv_usec) / 1000.0; printf(", elased: %.2f msn", elapsed); } close(fd); return 0; } void die(const char *format, ...) { va_list args; va_start(args, format); vfprintf(stderr, format, args); exit(1); } |




[...] Arduino Serial latency [...]
Hi, I figured out why the FTDI has 16ms latency. It’s just the default setting in its driver. I’ve written an article of how to tune the latency down to 1ms in this article: http://blog.lincomatic.com/?p=201
[...] the Teensy (with direct USB to the chip, not a USB-to-serial onboard) gets about 1ms serial latency. The same page reported the Duemillanove at 16ms minimum [...]