concerning a collaboration with Reilly Grant.
The following are some brief notes on asynchronous serial traffic, how to decipher it, and how to identify a baud rate when a scope is unavailable.
RS232, TTL UART Waveforms
Of the two waveforms above, the first is RS232 and the second is TTL-level serial. They are on opposite sides of a MAX232 level-converter, and the images have been scaled to show the same point in the transmission, which is inverted and amplified by the MAX232. Here, the TTL line is +5V for a 1 and 0V for a 0. The RS232 line is -10V for a 0 and +10V for a 1.
In decoding serial traffic, it is important to recognize that the least significant bit is transmitted first, and that it is preceded by a start bit of 0. Following the most significant bit will be an optional parity bit and stop bit of 1. Thus, 0xEE would be
111111110011101111111111
where the bold portion is the data byte and the surrounding ones are the idle state. For practice, draw arbitrary bytes on paper, both as RS232 and as TTL-level. Further, note that the exact end of the byte is not immediately clear, and can only be found by matching timing from the starting 0 bit.
Here is a photograph of a few bytes.
Double Timing
When decoding serial traffic, it is crucially important to do so at the proper rate. It is for this reason that the standard baud rates have been decided upon. The traffic above was received at roughly, but not quite exactly, twice its baud rate. Note--by clicking the image for a better view--how common bytes of 0xFF and 0x00 are, as well as how rare a byte containing just a single 1 or 0 in isolation is.
Close Timing
Once sniffing traffic on the proper order of magnitude, it is often the case that a smart card reader will not be using exactly the same timing. As timing is synchronized not to a clock, but to a start bit, the less significant bits will be correct with errors being found in the more significant bits. For example, "0x3B 0x02" might be mistakenly read as "0x9B 0x82" if the transmission is being sniffed at a slower rate than the actual. This is because the stop bits are being misinterpreted as most-significant bits. (10,400 baud being sniffed at 9600 baud, in this case.)
To determine the next guess from an incorrect read without a scope, draw the misinterpreted byte and the correct byte of graph paper, comparing them. Shown below are 0x3B and 0x9B, click on the image for annotations.
Unfortunately, Unix was not designed with odd baud rates in mind, and the Linux solution isn't terribly elegant either. In short, you must configure the port to run at 38,400 baud and override that baud rate with a manually chosen clock divider. For details, see the setserial documentation, looking at the spd_cust option. Further complicated matters, spd_cust doesn't seem to work with the FTDI usb/serial chip. If you can get the chip to work at an odd rate, please post a comment with details.
Automation
Automatically identifying the baud rate isn't terribly hard for a microcontroller, which can watch traffic to measure the bit width and adjust its reception appropriately. At some point, I'll write firmware for the MSP430 to do just that.