Posts Tagged ‘Arduino’

Notes on FTDI latency with Arduino

Sunday, October 16th, 2011

Today I Learned how to minimise latency when sending data to a computer from an Arduino (or any other FTDI-based device.) I learned it specifically for Windows, Linux and OS X.

Well, actually I learned this a few weeks ago while developing the Hairless MIDI<->Serial Bridge. But the blog post had to wait until today.

The Problems

  • By default, serial latency with FTDI chips (including Arduino Duemilanove/Mega) on Windows & Linux can be quite high (>16ms) and unpredictable.
  • In audio applications (like sending MIDI data), this can add enough latency to create audible artifacts.
  • In Java-based applications that use it, librxtx introduces an additional 20ms of latency. I wasn’t using Java or librxtx, but you’ll want to read that if you are.

The good news is that you can reduce FTDI latency substantially with a simple tweak.

What’s Latency?

In this case, latency is the amount of time between when some data gets sent from one side (the Arduino), and received on the other side (the computer.)

In lots of cases latency doesn’t matter, or you accept higher latency in exchange for higher throughput. However, for real-time applications like MIDI controllers, you don’t want a noticeable delay between pressing a button and hearing the sound that it makes.

The consensus seems to be that for acceptable MIDI audio responses, you need to keep MIDI message latency under about 20ms.

FTDI Latency Timer

The problem stems from the Arduino’s “Serial to USB converter” chip, the FTDI FT232R. The FTDI can’t send a USB packet to the computer for every byte that comes from the Arduino’s microcontroller. Instead, it stores the serial data in an internal buffer and only sends a USB packet when the buffer is full, or after a period of time has elapsed. This period of time is determined by the FTDI Latency Timer, which is the reason why FTDI chips can give bad latency characteristics.

On Linux & Windows, the default latency timer setting is 16ms. For example, say you send a 3 byte MIDI message from your Arduino at 115200bps. As serial data, it takes 0.3ms for the MIDI message to go from the Arduino’s microcontroller to the FTDI chip. However, the FTDI holds the message in its buffer for a further 15.8ms (16ms after the first byte arrived), before the latency timer expires and it sends a USB packet to the computer.

Thankfully, the latency timer can be tweaked. The tweaking method varies between operating systems.

Linux

In proper Linux style, the kernel’s FTDI driver exposes a nice sysfs interface that lets you get and set the latency timer. For example, if your serial port is ttyUSB0:

# cat /sys/bus/usb-serial/devices/ttyUSB0/latency_timer
16
# echo 1 > /sys/bus/usb-serial/devices/ttyUSB0/latency_timer
# cat /sys/bus/usb-serial/devices/ttyUSB0/latency_timer
1

… that will lower the timer from 16ms to 1ms (the minimum), to reduce latency.

In my experience, the timer value won’t change immediately on an open serial port. If an application is using it then you’ll need to close and reopen it before the new value takes effect.

If you’re writing code, there is also a Linux-specific serial flag ASYNC_LOW_LATENCY that programmatically sets the latency timer down to 1ms. This is how Hairless MIDI<->Serial Bridge does it. You can see a succinct C code example in this patch I submitted to the ttyMIDI project.

In testing, I found that ASYNC_LOW_LATENCY also only works if you subsequently close the serial port and then reopen it (annoying, because setting the flag requires you have open()ed it already.)

Windows

FTDI’s own driver for Windows has a combo box in the Port Settings dialog that lets you choose the latency timer value. This Instructable has some screen shots showing how to find the setting in the Windows Device Manager control panel.

Programmatically, setting the timer is a bit hackier on Windows but not impossible. The FTDI driver saves the current latency setting for each device in the registry, so you can use Microsoft’s Registry API to write a new value, then reopen the serial port. The registry key is

SYSTEM\CurrentControlSet\Enum\FTDIBUS\-device id-\0000\Device Parameters

There is a code example for this hack in the Hairless MIDI<->Serial source code.

OS X

OS X does things differently. The driver bundle contains a file, /System/Library/Extensions/FTDIUSBSerialDriver.kext/Contents/Info.plist. This XML plist file describes different profiles for the serial port, including different LatencyTimer values, depending on how the FTDI identifies itself on the USB bus. FTDI’s own Technical Note on the subject [PDF] explains how to edit that value to change the latency.

The good news is that on OS X the latency timer defaults to 2ms for any FTDI FT232 that uses the default vendor & device USB IDs (0403:6001). This includes Arduino & clone FTDIs, so there is no real need to change anything.

How Fast?

I ran a test of the tweaked latency timers. The test sketch sends a MIDI note (3 bytes), then waits to see that same note echoed back. It does this many times and calculates the average delay.

With the latency timer set to 1-2ms, the entire round trip averages 18-19ms. This includes at least 1ms spent in the MIDI framework on the computer. So, based on those results, I estimate the one-way latency to be under 10ms. Excellent!

With a 16ms latency timer, the one-way latency would have been 25ms or more.

I don’t know how much of the 10ms latency is now coming from FTDI/USB layer, or from higher layers in the host operating system. It’s good enough for MIDI use, so I stopped investigating!

Which Arduinos?

Arduinos with FTDI chips include the Arduino Duemilanove & Mega, and some clones like the Seeeduino.

The newer Arduino Uno & Mega 2560 have a different AtMegaU8 chip, programmed to behave as a USB/Serial converter. According to tests I’ve seen, these have good latency characteristics.

Been busy…

Saturday, September 24th, 2011

Too much going on lately:

Still, it’s nice to have things that are (almost) coming to fruition.

Structured EEPROM access with Arduino/AVRs

Thursday, July 1st, 2010

On an Arduino or other AVR, EEPROM access is a bit fiddly if you want to store different types of data. In this blog post, I’ll show you a quick trick to use when you have lots of structured data to store in EEPROM.

Alternatives

First, the existing alternatives:

  • Arduino has EEPROM, which is simple but it only reads/writes single bytes.
  • Arduino Playground has two templated functions that let you read/write anything. However, you need to know the offset of whatever you are accessing. Also, I find C++ templates a bit icky for this use case.
  • One level deeper, avr-libc has a more complex API for other kinds of integers, and buffers. However, you still need to remember offsets & sizes.
  • The EEMEM keyword works like PROGMEM to let you flag things as stored in the EEPROM. Which is nice, but you still need to pass size and offset whenever you read a value.


Technique

This technique uses a single ‘struct’ to represent the entire contents of your EEPROM, so you can then use macros to read and write fields.

You define a struct called __eeprom_data that describes your EEPROM:

struct __eeprom_data {
  int first;
  int second[64];
  boolean third;
  char fourth[buf_len];
}

Then use macros eeprom_read & eeprom_write to read and write each field:

int x;
eeprom_write(-7, first);
eeprom_read(x, first);


Full Example


/*
 * Copy and paste this block of #include & #defines into your code to use
 * this technique.
 *
 * (Don't worry too much about reading the macros, read through the
 * examples below instead.)
 */

#include <avr/eeprom.h>
#define eeprom_read_to(dst_p, eeprom_field, dst_size) eeprom_read_block(dst_p, (void *)offsetof(__eeprom_data, eeprom_field), MIN(dst_size, sizeof((__eeprom_data*)0)->eeprom_field))
#define eeprom_read(dst, eeprom_field) eeprom_read_to(&dst, eeprom_field, sizeof(dst))
#define eeprom_write_from(src_p, eeprom_field, src_size) eeprom_write_block(src_p, (void *)offsetof(__eeprom_data, eeprom_field), MIN(src_size, sizeof((__eeprom_data*)0)->eeprom_field))
#define eeprom_write(src, eeprom_field) { typeof(src) x = src; eeprom_write_from(&x, eeprom_field, sizeof(x)); }
#define MIN(x,y) ( x > y ? y : x )

const int buflen = 32;

/*
 * __eeprom_data is the magic name that maps all of the data we are
 * storing in our EEPROM
 */
struct __eeprom_data {
  int first;
  int second;
  boolean third;
  char fourth[buflen];
  char fifth[buflen];
};

void setup()
{
   Serial.begin(57600);

   /*
    * Writing simple variables to the EEPROM becomes simple
    *
    * First argument is the value to write, second argument is which field
    * (in __eeprom_data) to write to.
    */
   int q = 132;
   eeprom_write(q, first);
   eeprom_write(5958, second);
   eeprom_write(false, third);
   eeprom_write("Hello from EEPROM!", fourth);

   /*
    * You can even write from a pointer address if need be
    *
    * First argument is the pointer to write from.
    * Second argument is the field (in __eeprom_data)
    * to write to.
    * Third argument is the buffer length
    */
    const char * buf = "Another hello looks like this";
    eeprom_write_from(buf, fifth, strlen(buf)+1);

    int a, b;
    boolean c;
    char d[buflen], e[buflen];
    char *e_p = e;

    /*
     * Reading back is just as simple. First argument is the variable to read
     * back to, the second argument is the field (in __eeprom_data) to read
     * from.
     */
    eeprom_read(a, first);
    eeprom_read(b, second);
    eeprom_read(c, third);
    eeprom_read(d, fourth);

    /*
     * You can read back to a pointer address, if you need to.
     */
    eeprom_read_to(e_p, fifth, buflen);

    Serial.println(a);
    Serial.println(b);
    Serial.println(c ? "TRUE" : "FALSE");
    Serial.println(d);
    Serial.println(e_p);

    /*
     * The eeprom_write macros do bounds checking,
     * so you can't overrun a buffer.
     *
     * In __eeprom_data, 'third' is a one-byte boolean, but
     * eeprom_write knows this so only the first char 'T' is written
     * to EEPROM
     */
    eeprom_write("This is a buffer overflow", third);

    /*
     * If you have an array, like char[], you can write & read a single
     * array entry from a particular constant index
     *
     * Unfortunately, it only works for constant indexes not variables.
     * eeprom_write('X', fourth[x]) does not work with these macros.
     */
    eeprom_write('X', fourth[3]);
    eeprom_read(d, fourth);
    char x;
    eeprom_read(x, fourth[3]);
    Serial.println(d);
    Serial.println(x);
}

void loop() { }

(This is Arduino code, obviously if you’re using avr-libc directly then you can rewrite it for that.)

Downsides

The downsides of this technique (as I see them) are:

  • Uses macro magic (so a bit icky.)
  • Overkill if you only need to store one type of data in EEPROM, but useful if you have lots of different types.


Initial Values

When you start up, you need to know if the data in EEPROM is data that your program saved, or something else. There are a few different ways to deal with this.

EEMEM

EEMEM provides a way for you to set default values easily in your code:

EEMEM struct __eeprom_data initial_data EEMEM = {
  1, // first
  2, // second
  false, // third
  "Initial fourth", // fourth
   "Initial fifth" // fifth
};

Via avr-objcopy & avrdude you can generate a .eep file and flash it to the AVR. This is nice, but it won’t work if you’re using the Arduino IDE because (as of version 0018) it doesn’t generate .eep files properly, and it also doesn’t support flashing them.

It’s a good option if you’re using your own Makefile, though.

“Magic” number

The other way is to check for and expect a “magic” value somewhere in the EEPROM data. Something like:

// Change this any time the EEPROM content changes
const long magic_number = 0x5432;

struct __eeprom_data {
  long magic; // should be set to magic_number
  int first;
  int second;
}

void setup() {
  long magic;
  eeprom_read(magic, magic);
  if(magic != magic_number)
    initialise_eeprom();
}

void initialise_eeprom() {
  eeprom_write(0, first);
  eeprom_write(0, second);
  eeprom_write(magic_number, magic);
}

Servo pulse width range with Arduino

Saturday, July 4th, 2009

I recently bought a couple of cheap MG996R high-torque servos on ebay, and I want to use them with my Arduino.

Arduino + MG996 + Dusty Breadboard

These have the “JR” servo pinout, so orange = signal, red = V+, brown = V-.

You control these servos by sending a 50Hz pulse width modulated signal. The pulse width determines the position of the servo. Arduino wraps this in a nice Servo library. So you can just use servo.write(<angle>) to set the servo to a certain angle. Cool!

The servo library defaults to pulsing 544ms to 2400ms for angles zero to 180. This is too wide for the MG996R, the servo only moves when you write angles through 20-150 or so. Setting the value outside this range stresses the servo and can wear it out!

I wrote a quick sketch to interactively find the real minimum and maximum values: . You just watch the serial port and follow simple prompts:

Download the sketch here.

Servo Range Sketch (running)

Using that sketch, I found my MG996R servos to have minimum pulse width around 771 and maximum around 2193 when running off 5v1. The full sweep is approximately 0-130 degrees.

So, to use the servo library with correct angles the sweep will be 771 to 17982. So I can call:

servo.attach(servopin, 771, 1798);
servo.write(0); // Min
delay(2000);
servo.write(65); // Midpoint
delay(2000);
servo.write(130); // Max

Yay Arduino!

  1. While the servo is unloaded I’m powering it from the Arduino via USB, but this won’t supply enough current for a loaded servo. []
  2. The adjusted max of 1798 is calculated as ( (MaxPW – MinPW) * (MaxAngle / 180) ) + MinPW, ie ( (2193 – 771) * (130 / 180) ) + 771 []