Shell script to write and read data from serial communication
I would expect that your cat
statement is blocking waiting for end-of-file. It may treat your input tty as a standard terminal device, in which case it will require an end-of-file character to terminate the input (e.g, Control-D) and it could apply other processing to the input and the output. You had best familiarize yourself with the stty
command and its options (see stty man page). Try doing stty -F /dev/ttyUSB0
to see what options are set.
The easiest way of reading the input might be to read a single character at a time. You could try with the following script. This will read a single character at a time from the input until a 'q' is entered.
stty -F /dev/ttyUSB0 raw
stty -F /dev/ttyUSB0 -echo
while read -rs -n 1 c && [[ $c != 'q' ]]
do
echo "read <$c>" # Replace this with code to handle the characters read
done < /dev/ttyUSB0
If -F does not work, redirect stdin to the device.
For your output problem, I think it is working but you are seeing the characters displayed as characters and not the hex-codes. To see the hex codes (for verification testing only - I don't think you want to send the hex codes to the terminal), try:
echo -en '\xAA\x04\xC2' | od -tx1
You may also want to have raw mode set when outputting to avoid the driver changing the output characters.
Python Shell: writing one byte and reading decimal data from a serial port
My suggestion:
- make sure that connection settings are really the same (I would compare them to Matlab)
- send correct commands: the first is b'\x80' and the second b'\x82' from your screenshots, although here you are writing b'\x81'
- add 5s timeout on both write and read, so you will definitely know if something is waiting
- reset in and out buffers as was suggested in the comments
- use logging to see the timings of each command
- add additional sleep to see if anything changes
- as a last resort you could try with a different library https://pyvisa.readthedocs.io/en/latest/index.html
My test script would be something like this (run with python -u
to avoid buffering of log output):
import logging
import serial
import time
from pathlib import Path
logging.basicConfig(level='INFO', format='%(asctime)s - %(message)s')
logging.info('open')
rs232 = serial.Serial(
port='COM5',
baudrate=115200,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=5,
write_timeout=5,
)
logging.info('reset buffers')
rs232.reset_input_buffer()
rs232.reset_output_buffer()
logging.info('write first')
rs232.write(b'\x80')
# time.sleep(1) # see if uncommenting changes anything
logging.info('read first')
reply = rs232.read(size=1)
logging.info(reply)
logging.info('write second')
rs232.write(b'\x82')
# time.sleep(1) # see if uncommenting changes anything
logging.info('read second')
# reply = rs232.read_until(expected=b'\x0a\x0d') # read single line
reply = rs232.read_until(expected=b'\x80')
Path('result.csv').write_text(reply.decode().replace('\n\r', '\r\n'))
logging.info(reply)
logging.info('close')
rs232.close()
Reading and writing to a serial port using shell and Java
Instead of screen you may use command cu from UUCP package.
To install UUCP package sudo apt-get install uucp
or sudo yum install uucp
.
Then use this command:static String command = "cu -l " + port + " -s " + baudRate;
Some explanation:
- screen -d detaches session (it runs in background) that's why you do not see any data.
- screen requires terminal which is not easy from java. See How to open a command terminal in Linux?
Linux serial port listener and interpreter?
Is this not what you're looking for?
while read -r line < /dev/ttyS2; do
# $line is the line read, do something with it
# which produces $result
echo $result > /dev/ttyS2
done
It's possible that reopening the serial device on every line has some side-effect, in which case you could try:
while read -r line; do
# $line is the line read, do something with it
# which produces $result
echo $result > /dev/ttyS2
done < /dev/ttyS2
You could also move the output redirection, but I suspect you will have to turn off stdout buffering.
Batch script to read/write data using COM port
Playing around with a few different things with batch scripts leads me to believe nothing is going to work in with a batch script and the standard Windows command line tools. set /P
returns immediately when reading from COM1
, and something like type COM1
will copy the serial data in large chunks line by line.
I had better luck using Cygwin bash. Here's a simple script that receives lines from COM1
and echoes them back. It exits when it receives a line starting with "quit". You can test it out by using a terminal emulator on the other end of the serial link or just using statements like echo quit > COM1
.
CR="$(echo -e '\r')"
exec 4<> /dev/com1
cat <&4 | while :
do
IFS="$CR" read -r line
case "$line" in
quit*)
echo "goodbye$CR" >&4
break
;;
*)
echo "recieved line: $line"
echo "recieved line: $line$CR" >&4
;;
esac
done
The CR
variable holds a carriage return character which in this example is used to strip it off the input lines and used to terminate lines with CR LF when outputting them over the serial line. Depending how exactly your BIOS behaves you may or may not need to do this in your own script.
The exec 4<> /dev/com1
line is crucial. This opens the COM port once for both reading and writing. Windows only allows a COM port to be open once, so if this wasn't done it wouldn't be possible to read and write to the COM port. The 4
means that is assigned to file descriptor 4 and the exec
statement keeps it open for the rest of the script.
The cat <&4 |
part is also important. Unfortunately there seems to be a bug in Cygwin bash where it will try to rewind the file descriptor if it reads past the end of a line. This works for files, but it doesn't for serial ports so data gets lost. To workaround this problem the script reads from a pipe instead, which bash is smart enough to not try to rewind.
The reason for setting IFS="$CR"
is to strip off the carriage return at the end of a line as mentioned before, and to not strip off anything while reading. The read
command uses the IFS string to break up the input line into words. You may be able to use this to your advantage and set it to a different value to make it easier to parse the BIOS output.
The rest of the details are pretty straightforward. The -r
option for read
causes it not to treat \
characters specially. Depending on what sort of line endings your BIOS expects you have three different ways you can write your echo statements:
echo "Both CR and LF line ending$CR" >&4
echo -n "CR only line ending$CR" >&4
echo "LF only line ending" >&4
One thing this script doesn't do it set the COM port parameters like baud rate and flow control. This is probably best done using the normal MODE COM1
command. Cygwin has an equivalent stty
command, but it doesn't appear to support all the parameters.
Another entirely different option is to use Expect. If you find that it's hard to get bash to parse and respond appropriately to your BIOS's output then you might consider using that instead. This sort of thing is what its designed for, though there's a bit of learning curve if you're not already familiar with TCL. It's available as a standard Cygwin package.
Related Topics
How to Run Multiple Programs in a Sequence
Run Shell Command and Don't Wait for Return
How to Change the Permissions in Openshift Container Platform
Using Sed Replace Line in File with Another File
How to Extract Patterns Form a Text Files in Shell Bash
Linux Randomly Deleted My File While Compiling What Do I Do
Wc -M in Unix Adds One Character
Segmentation Fault with a Variable in Section .Data
Reusing a Port Number in a Udp
Find Files in Multiple Directories Taken from List in a File
Why Does Script Not Recognize File Extension
Shell Script to Delete Files When Disk Is Full
Why Do I Get /Etc/Cups Conflicts Between Attempted Installs in Yocto
How to Make Linux Power Off When Halt Is Run
Elegant Way to Set Base Address of Elf Image with Linux Binutils