Serial with socat
Introduction
I do a lot of work with network devices, which are often headless and require the use of serial consoles for out-of-band access. My go-to on MacOS and Linux has been screen, but various little issues over the years have led me to consider other options. I tried minicom on the recommendation of a colleague, but that didn’t work for me either.
I’ve struggled to pinpoint my problem with the aforementioned tools; I think I just want something less featurful, something that literally only gives me a tty via serial and doesn’t have anything unnecessary between my terminal and the remote shell. Over the weekend, I randomly happened upon an article about using socat for serial on Hacker News, and thought it might be just what I have been searching for. So I tried out the example and it failed miserably with the socat from Homebrew (MacOS being the OS on my daily driver).
What went wrong
I took the example command from the article and made the minimal changes for my environment (the name of my adapter’s serial device), but right off the bat I got an error:
socat -,rawer,escape=0x0f /dev/tty.usbserial-AI066OQC,b115200,rawer
# 2020/05/20 22:25:06 socat[72638] E parseopts(): unknown option "b115200"
A quick google search turned up a number similar reports, and led me toward the
ispeed
and ospeed
options. Reviewing the manpage for these parameters also
suggests that the b
option is not available on all operating systems, MacOS
presumably among those. The manpage also explains these are used to set the
baud rates for incoming and outgoing data on the line, respectively (anyone
ever run into asymmetric serial speeds in the wild?). Anyway, that’s an easy
change:
socat -,rawer,escape=0x0f /dev/tty.usbserial-AI066OQC,ispeed=115200,ospeed=115200,rawer
# and nothing at all happens, nor does the ctrl+o escape work
# eventually I had to resort to a kill -9 in another window
Bummer. I guess it’s not so easy after all.
Not all hope is lost
After far too long playing with permutations of options and searching the ends of the internet for suggestions, I finally found the magic incantation (with some hints from the sample command in this stack overflow question)
socat -,rawer,escape=0x0f /dev/tty.usbserial-AI066OQC,clocal=1,nonblock=1,ispeed=115200,ospeed=115200
The end solution
I am really excited about this new way to connect via serial. If nothing else, ctrl+a goes to the beginning of the line with no other escape sequence, and I am not at the mercy of any scrollback buffer beyond that of iterm. Alone, those are two killer features and make it so a serial connection really isn’t any different than any other terminal session I use throughout the day (besides perhaps speed).
That said, the above solution isn’t very pretty. But we can wrap it up as a shell function to make it easier to use:
# socat-serial --> social
social() {
local DEV=${1?must specify the serial device}; shift
local BAUD=${1:-9600}
socat -,rawer,escape=0x0f ${DEV},clocal=1,nonblock=1,ispeed=${BAUD},ospeed=${BAUD}
}
# now you can invoke like
social /dev/tty.usbserial-AI066OQC 115200
Drop that in your .bashrc or equivalent and off you go. Kill the connection with ctrl+o per the escape code.
I don’t have a need for more options, but ixon/ixoff
turn flow control on and
off, respectively, and csX
where `X is number of bit can change the length of
chars as needed. I am sure it supports other obscure parameters too; check the
manpage. For heavy-duty uses a function alone might not suffice. I could
totally see this evolving into a whole wapper script around socat, where
needed.
Happy console-ing!