Bash – send data to serial (rs232) port and wait for response
Sending data to a serial port is quite easy in Bash, for example:
echo "my packet data" > /dev/ttyS0
And you can read from a serial port using cat:
cat /dev/ttyS0
However cat must typically be run from a different shell instance as it blocks waiting for data. So is it possible to write and then read the response from a single shell instance?
Well, it is, but it requires a bit of sleight-of-hand. For example, if we start cat in the background and then send the command, cat will report the response as follows:
# Run cat in the background cat /dev/ttyS0& # Send the command, cat should print the response echo "my packet data" > /dev/ttyS0
Which works but it a bit of a mouth-full! cat continues to run in the background, and will print more responses as they arrive.
But what if you want to just send one packet and then wait for a single response?
This is a bit harder, but if your response ends with an end of line character, or another known character then we can use read to help with this…
First we setup a read command in the background, unlike cat, this command will end when a response is received or when the timeout time arrives, then we can send our command:
(read -n60 -t20 RESP < /dev/ttyS0; echo $RESP)& echo "my packet data" > /dev/ttyS0
This gets read to wait for up to 20 seconds (-t20) for a line of data (max size, 60 characters -n60) from /dev/ttyS0, which it reads into RESP, it then echos $RESP – all of this happens in the background. echo then sends the packet which will result in a response.
If your response packed ends with a character other than an EOL character then you can specify a delimiter to read using the -d command-line option.
Again it’s all a bit long winded, so we can wrap it all up in a bash script (send_tty.sh) as follows:
#!/bin/sh # # Send a packet to the specified serial port # and wait for, and output the response, it is assumed # that the response will end with an EOL character. # # usage: send_tty.sh <data-to-send> <port> # # [backgound] Wait for, and read a line from the serial port into RESP, # max 128 characters, timeout=10s, then output $RESP # # (c) 2019 Kevin Godden # (read -n128 -t10 RESP < $2; echo $RESP)& # Hack - use read to pause for 200ms to give previous # command a chance to get started.. read -p "" -t 0.2 # Send command printf "$1\r" > $2 # Wait for background read to complete wait
An example of using the script:
./send_tty.sh "my-command" /dev/ttyPS0./send_tty.sh "my-command" /dev/ttyPS0
Github repo is here