Skip to content

Instantly share code, notes, and snippets.

@valgur
Last active March 12, 2025 13:18
Show Gist options
  • Save valgur/0970e3ddaf148fbc7f8d8832b2d61659 to your computer and use it in GitHub Desktop.
Save valgur/0970e3ddaf148fbc7f8d8832b2d61659 to your computer and use it in GitHub Desktop.
Create a virtual TTY device to log traffic on a physical serial port
#!/bin/bash
# Default device path
DEFAULT_DEVICE="/dev/ttyUSB0"
DEFAULT_VIRT_DEVICE="/tmp/virtual_tty"
# Function to display usage information
usage() {
echo "Usage: $0 [OPTIONS]"
echo "Creates a virtual TTY connected to a real serial device with bidirectional logging."
echo ""
echo "Options:"
echo " -d, --device PATH Serial device to connect to (default: $DEFAULT_DEVICE)"
echo " -v, --virtdev PATH Virtual TTY device path (default: $DEFAULT_VIRT_DEVICE)"
echo " -b, --baud RATE Baud rate (default: auto-detect from device)"
echo " -l, --logdir DIR Directory to store logs (default: /tmp/tty_logs)"
echo " -h, --help Display this help and exit"
echo ""
echo "Example: $0 --device /dev/ttyACM0 --baud 115200"
exit 1
}
# Parse command line arguments
DEVICE=$DEFAULT_DEVICE
VIRT_DEVICE=$DEFAULT_VIRT_DEVICE
BAUD_RATE=115200
LOG_DIR="/tmp/tty_logs"
while [[ $# -gt 0 ]]; do
case $1 in
-d|--device)
DEVICE="$2"
shift 2
;;
-v|--virtdev)
VIRT_DEVICE="$2"
shift 2
;;
-b|--baud)
BAUD_RATE="$2"
shift 2
;;
-l|--logdir)
LOG_DIR="$2"
shift 2
;;
-h|--help)
usage
;;
*)
echo "Unknown option: $1"
usage
;;
esac
done
# Check if device exists
if [ ! -c "$DEVICE" ]; then
echo "Error: $DEVICE does not exist or is not a character device"
exit 1
fi
# Create log directory
mkdir -p "$LOG_DIR"
timestamp=$(date +"%Y%m%d_%H%M%S")
# Auto-detect baud rate if not specified
if [ -z "$BAUD_RATE" ]; then
BAUD_RATE=$(stty -F "$DEVICE" speed)
echo "Detected baud rate: $BAUD_RATE"
else
echo "Using specified baud rate: $BAUD_RATE"
fi
echo "Taking ownership of $DEVICE..."
sudo chown $USER $DEVICE
# Create FIFOs for logging
TX_FIFO="/tmp/tx_fifo_$$"
RX_FIFO="/tmp/rx_fifo_$$"
mkfifo $TX_FIFO
mkfifo $RX_FIFO
# Function to clean up on exit
cleanup() {
echo -e "\nCleaning up and exiting..."
# Kill background processes
[[ -n $SOCAT_PID ]] && kill $SOCAT_PID 2>/dev/null
[[ -n $TX_LOGGER_PID ]] && kill $TX_LOGGER_PID 2>/dev/null
[[ -n $RX_LOGGER_PID ]] && kill $RX_LOGGER_PID 2>/dev/null
# Remove temporary files
rm -f $TX_FIFO $RX_FIFO
echo "Logs saved to:"
echo "TX log: $LOG_DIR/${timestamp}_tx.log"
echo "RX log: $LOG_DIR/${timestamp}_rx.log"
exit 0
}
# Set up trap for Ctrl+C and other signals
trap cleanup SIGINT SIGTERM EXIT
# Start TX and RX loggers
echo "Setting up bidirectional TTY with logging..."
# Use a simpler socat approach - one instance for the virtual TTY
socat -d PTY,raw,echo=0,link=$VIRT_DEVICE,mode=777 \
OPEN:$DEVICE,raw,echo=0,crnl,b$BAUD_RATE &
SOCAT_PID=$!
# Wait for the virtual device to be ready
sleep 1
# Check if socat started successfully
if ! ps -p $SOCAT_PID > /dev/null; then
echo "Error: Failed to create virtual TTY. Check permissions and device availability."
cleanup
exit 1
fi
# Set up TX logging (data to device)
socat -u OPEN:$VIRT_DEVICE,rdonly OPEN:$TX_FIFO,creat,append &
TX_SOCAT_PID=$!
# Set up RX logging (data from device)
socat -u OPEN:$DEVICE,rdonly OPEN:$RX_FIFO,creat,append &
RX_SOCAT_PID=$!
# Start log file writers
cat $TX_FIFO > "$LOG_DIR/${timestamp}_tx.log" &
cat $RX_FIFO > "$LOG_DIR/${timestamp}_rx.log" &
echo -e "\nSetup complete!"
echo "Virtual TTY: $VIRT_DEVICE → $DEVICE"
echo "Baud rate: $BAUD_RATE"
echo "TX log (to device): $LOG_DIR/${timestamp}_tx.log"
echo "RX log (from device): $LOG_DIR/${timestamp}_rx.log"
echo -e "\nWaiting for communication (press Ctrl+C to exit)...\n"
# Keep script running until Ctrl+C
while true; do
sleep 1
# Check if socat is still running
if ! ps -p $SOCAT_PID > /dev/null; then
echo "Error: socat process terminated unexpectedly"
exit 1
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment