Tuesday, October 21, 2014

Serial Ports Revisited

We were trying to test a pair of radios with a data loop-back and were again making the same old mistakes and then wondering what the heck is going on. It is just amazing how many times I have sat down and scratched my head with these things - it feels like I never learn or remember!

The mistake we make over and over again is to expect the wrong read/write behaviour from a serial port utility:
  • A serial port is a bidirectional device.
  • A program will grab the device file handle and open the device file either as 'Read', 'Write' or 'Read/Write'.
  • Simple programs like 'cat', 'echo', 'bash' (read, write), 'head', 'tail' and 'of', all open the device file as either 'Read' or 'Write', never 'Read/Write'.
  • Complex programs like 'cutecom', 'minicom', 'screen' and 'netcat', open the device file as 'Read/Write'.
So, one cannot run 'cat' twice on the same port in order to send and receive data, you have to use 'netcat' or 'screen' to do that in one convoluted operation, or you have to make a T cable and run the Rx and Tx wires to two separate serial ports and then you can run 'cat' twice (on the different ports).


Ensure that you are a member of the dialout group:
# usermod -a -G dialout username

To configure a serial port you can use either 'screen', 'minicom' or 'stty':
$ screen /dev/ttyUSB0 115200
$ minicom -b 115200 -o -D /dev/ttyUSB0
$ stty -F /dev/ttyUSB0 raw
$ stty -F /dev/ttyUSB0 115200

To exit screen, type Control-A k and exit minicom with Control-A x.

Tx and Rx

Then, to send and receive data through the port:

This will not work concurrently.  You cannot open two terminals and run these commands together to see what is transceived in real time:
$ cat txfile > /dev/ttyUSB0
$ cat /dev/ttyUSB0 > rxfile

After the first instance of 'cat' grabbed the file handle to send the file, the second instance cannot open it again to receive the file at the same time - oops...

T Cable Solution

If you make a T cable then you can do this in two terminals:
$ cat txfile > /dev/ttyUSB0
$ cat /dev/ttyUSB1 > rxfile

That will work because each instance of 'cat' uses a separate serial port, but then you got to find three 9 pin connectors and two serial port adaptors, which may not all be available, but the commands are easy to understand.

Echo and Read

The easier way, is to use screen with exec, or minicom with expect, or do character I/O one at a time in bash with echo and read.

If you use read, be sure to set a timeout or it may hang forever.  It is also usually needed to flush any junk from the receive buffer before you try to look for a specific response, and use a sleep statement to allow the response to come back after a command:

echo "Set serial port USB0 to 9600N81"
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 9600

echo Wait for the dust to settle
sleep 1

# Flush
for i in 1 2 3 4 5; do
  read -r -t1 JUNK < /dev/ttyUSB0

  sleep 0.1

# Send LFCR and Read a line
echo -en "/n/r" > /dev/ttyUSB0

sleep 0.1
read -r -t1 LINE < /dev/ttyUSB0

# Flush
for i in 1 2 3 4 5; do
  read -r -t1 JUNK < /dev/ttyUSB0

  sleep 0.1

if [ "$LINE" != "NO CARRIER" ]; then
  echo Communication failure
  exit 1

echo Communication OK

exit 0

Hex and Octal Dump

While I am at it, sometimes it is better to output data in hexadecimal. This can be done with the 'od' (octal dump) program instead of the venerable 'cat':
$ cat txfile | nc < /dev/ttyUSB0 > /dev/ttyUSB0 | od -x

$ od -x < /dev/ttyUSB1

$ $ od -x < /dev/ttyUSB1 > rxfile

and so on.

A Real Computer

Obviously, you need a real computer to do all this:

The joke comes from here:

No comments:

Post a Comment

On topic comments are welcome. Junk will be deleted.