Can the Arduino be used to "spy" on a UART connection between two devices?

If I understand correctly you have 2 devices connected via UART. I assume just TX, RX and GND lines connected between the devices? (i.e., no DTS/CTS/DTR/RTS control lines used - this is typical).

In this scenario, device 1's TX (transmit) is connected to device 2's RX (receive), and vice-versa. Their grounds are connected to each other. Thus, each device can be transmitting and receiving at the same time (each transmits on a separate wire, the communication is full-duplex).

The reason I mention all this is because it becomes clear that to "sniff" or "listen", you'll actually need 2 UARTs to listen to both sides of the conversation.

Basically, all you'd do is ensure that all 3 devices' UART GNDs are shorted, and connect (really, "tee", as in a T-fitting, like plumbing) device 1 & device 2's TX lines to the 2 RX lines on 2 UARTs. Make sure that the baud rates are all configured identically.

There are a lot of Arduino boards / designs. The most common one nowadays, the Duemilanove, uses the ATMega328P, which I think has only 1 UART (well, USART). So you'd either have to wire up a 2nd UART IC, or resort to "bit banging" on the second receiver.

Async UART communications is well-defined, with start & stop bits (and sometimes parity bits), so if your processor is fast enough, you can simply connect one of the device's UART TX lines to a GPIO configured as input, and poll the line fast enough with oversampling to detect START & STOP and sample bits. The article "Bit Banging" by Jack Ganssle will give you plenty to chew on.

A decent description of the RS232 waveform can be found at BeyondLogic.

Note that there are other issues such as voltage levels (0/+5, -10V/+10V, etc.) that you'll have to take into consideration (see the Beyond Logic section on "RS232 Level Converters"). I don't have enough information on your system to discuss hardware interfacing besides the "connect the lines" approach discussed above. Assuming voltage levels are matched, usually it's not a problem to "tee" the TX line into a second receiver (the sniffer), but if the TX doesn't have enough drive, you might need to insert a buffer/driver to prevent the signal from degrading.


There's a neat trick you can do if the communication is in one direction only at a time (i.e. half-duplex communication). It won't work if both sides talk to each other at the same time (full duplex) but if it's your typical "do this" "ok here's the response" "now do this" "ok here's the new response" type of communications it works quite well.

Since the UART link uses an idle condition of the transmitter at a logic high (1) level you would use a 2-input AND gate and connect the TX from each side to an AND input. The output of the AND gate is your input to your sniffer's UART (it's RX pin). Now take device B's TX line and also bring it to an I/O port on the sniffer. You will configure the sniffer to generate an interrupt when this pin goes from high to low.

To recap: device A UART TX -> AND gate input. Device B UART TX -> other AND gate input AND sniffer GPIO pin. Output of AND gate -> sniffer UART RX line.

UART communications consist of a start bit, some number of data bits, an optional parity bit, and one or more stop bits. Since the idle state is a logic high (1), the start of EVERY byte will be a logic low (0) and the interrupt on the sniffer will fire. While your sniffer is executing the I/O interrupt the UART hardware will be collecting bits from the AND gate. By the time the UART has received the stop bit, the I/O interrupt will be long done and the UART RX interrupt will fire.

The interrupt-on-IO-change routine will set a "direction" variable to indicate that communications are in the "B->A" direction. The sniffer's UART receive interrupt would look at this "direction" variable and write the just-received byte to the appropriate buffer. The UART RX interrupt would then set the "direction" variable back to the default "A->B" state:

volatile int direction = 0;           /* 0 = A -> B */

void io_interrupt(void)
{
    direction = 1;                    /* switch direction, now B -> A */
}

void uart_interrupt(void)
{
    unsigned char b;

    b = UART_RX_REG;
    if(direction) {
        store_byte_to_device_b_sniff_buffer(b);
    } else {
        store_byte_to_device_a_sniff_buffer(b);
    }

    direction = 0;                   /* reset direction to default A -> B */
}

This code is written for clarity and not necessarily what you'd write in a real-world situation. Personally I'd make "direction" a pointer to the appropriate FIFO structure, but that's another exercise entirely. :-)

When device A is talking the I/O line does not move (it remains at a logic '1' since device B's UART transmitter is idle), and the UART RX interrupt will receive a byte, see that the direction is A->B, and store the data to that buffer. When device B is talking the I/O line will go low as soon as device B starts shifting data out, and the I/O interrupt routine will set the direction to indicate that device B is talking. The UART RX interrupt will eventually fire after all the bits have been collected and since I/O interrupt has taken care of setting the direction register appropriately, the received byte will be stored in the correct buffer.

Presto: half-duplex communications between two devices captured with a single UART and I/O line on the sniffer, with no bit-banged UART communications.


You don't have to hook the AVR's transmit-data pin into your circuit. Just connect the receive line to the half of the the existing link you want to eavesdrop on. If your particular AVR has two serial ports, you should be able to spy on both halves of the existing link simultaneously. You just have to make the port settings match the existing baud rate, stop bits, etc.