LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Serial port I/O with Bash? (http://www.linuxquestions.org/questions/programming-9/serial-port-i-o-with-bash-730482/)

01000111 06-03-2009 02:55 PM

Serial port I/O with Bash?
 
Greetings all,
I am attempting to write a script to communicate with a photovoltaic inverter via a serial port on a Damn Small Linux PC. The inverter reads/writes binary data, and I've figured out how to handle that via od and awk, but I can't seem to actually communicate with the inverter. I know it works 'cause if I reboot my DSL PC to WinXP, I have a VBScript that works just fine... so communication and hardware is not the issue. Please take a look at the following and let me know if you have any suggestions. Right now, whenever I try to read from /dev/ttyS0, the script just hangs. The lines below that start with ####### are all my attempts so far, and they all hang. The last ###### is what I expect to get back from the inverter.

Thanks in advance,
01000111


#!/bin/bash
stty -F /dev/ttyS0 raw ispeed 2400 ospeed 2400 cs8 -ignpar -cstopb -echo

#Get total watt-hours generated
echo $'\x80\x80\x80\x01\x01\x01\x11\x14' > /dev/ttyS0
######read -n 1 InStr < /dev/ttyS0
######InStr=`dd if=/dev/ttyS0 count=1 bs=1`
######(cat > InStr) < /dev/ttyS0
######InStr=$'\x80\x80\x80\x03\x01\x01\x02\x03\x04\xff'
InStrLen=${#InStr}
for ((x=0;x<$InStrLen;x++)); do
let z=0x$(echo -n ${InStr:x:1} | od -h | awk '{ print $2 }')
echo -n $z...
done
if [ ${InStr:0:6} = $'\x80\x80\x80\x03\x01\x01' ]; then
let a=0x$(echo -n ${InStr:6:1} | od -h | awk '{ print $2 }')
let b=0x$(echo -n ${InStr:7:1} | od -h | awk '{ print $2 }')
let c=0x$(echo -n ${InStr:8:1} | od -h | awk '{ print $2 }')
WattsTotal=$[(($a*256)+$b)*10**$c]
fi
echo TOTAL: $WattsTotal

harry edwards 06-03-2009 03:45 PM

Do you get the response with a simple test i.e.
Code:


stty -F /dev/ttyS0 raw ispeed 2400 ospeed 2400 cs8 -ignpar -cstopb -echo
cat < /dev/ttyS0 &
echo $'\x80\x80\x80\x01\x01\x01\x11\x14' > /dev/ttyS0


01000111 06-04-2009 07:23 AM

Quote:

Originally Posted by harry edwards (Post 3562093)
Do you get the response with a simple test i.e.
Code:


stty -F /dev/ttyS0 raw ispeed 2400 ospeed 2400 cs8 -ignpar -cstopb -echo
cat < /dev/ttyS0 &
echo $'\x80\x80\x80\x01\x01\x01\x11\x14' > /dev/ttyS0


I tried:
Code:

cat < /dev/ttyS0 `echo $'\x80\x80\x80\x01\x01\x01\x11\x14' > /dev/ttyS0`
but that hangs too.

jlinkels 06-04-2009 08:24 AM

I am not really familiar with reading and writing thru /dev/ttyS0, but I assume you simple echo to it and read from it and everything else is handled on a lower level by the device driver.

First question is of course if the serial port is configured correctly at works at all. You can connect pin 2 and 3 of the serial connector on the PC as to make a loopback plug. Everything you write to the port should be echoed and read again by your script.

If this does not happen, it is useless to try to talk to the inverter.

If this works, are you sure that there is no additional protocol being used to talk to the inverter? Often it is something like STX at the beginning, some DLE's and EOT's at the end of the transmission, often with checksums etc.

Also a great help is a device called break-out box or Interfaker which has LED's for all important serial signals, and you can force signals like DTR and DSR active or inactive. With the LED's you can see if anything is transmitted ar all and if the status of the handshake lines is satisfactory.

jlinkels

01000111 06-04-2009 11:09 AM

I've made some progress. I'm getting data back from the inverter now. Turns out that one of my \x01 needed to be \x00. This caused a problem with null characters, but printf solved that.... I think. The problem I'm having now is that I appear to be getting 'random' responses from the inverter. I get different length responses (anywhere between 1 and 5 bytes) and almost never the same response twice. The fact that I'm getting anything at all indicates that my message TO the inverter is working. If I change it to a badly formatted message (such as changing the last byte to a incorrect checksum) I get nothing. So, here's what I have (the commented line is what I expect):

Code:

#!/bin/bash
stty -F /dev/ttyS0 raw ispeed 2400 ospeed 2400 cs8 -ignpar -cstopb eol 255 eof 255

#Get total watt-hours generated
printf "\x80\x80\x80\x00\x01\x01\x11\x13" > /dev/ttyS0
eol=0
ff=$'\xff'
until [ $eol -eq 1 ]; do
    echo -n .
    read -r -d $ff -n 1 -t 2 OneByte < /dev/ttyS0
    eol=$?
    InStr=$InStr$OneByte
done
echo $InStr
######InStr=$'\x80\x80\x80\x03\x01\x01\x11\x01\x02\x03\x18'     
InStrLen=${#InStr}
for ((x=0;x<$InStrLen;x++)); do
    let z=0x$(echo -n ${InStr:x:1} | od -h | awk '{ print $2 }')
    echo -n $z...
done
if [ $InStrLen -gt 8 ]; then
    if [ ${InStr:0:7} = $'\x80\x80\x80\x03\x01\x01\x11' ]; then
        let a=0x$(echo -n ${InStr:7:1} | od -h | awk '{ print $2 }')
        let b=0x$(echo -n ${InStr:8:1} | od -h | awk '{ print $2 }')
        let c=0x$(echo -n ${InStr:9:1} | od -h | awk '{ print $2 }')
        let WattsTotal=$[ (($a*256)+$b)*10**$c ]
    fi
fi
echo TOTAL: $WattsTotal


Wim Sturkenboom 06-05-2009 10:31 AM

If I understand your code (I'm not a bash scripter),you stop when there's nothing to read (eol=$? and test $eol).

This however does not mean that you have received a complete message (you might be reading to early). So if you know that a message has a fixed length or a specific terminator, I would read till you have the full message (count bytes or set n to 8 or OneByte equals whatever) and ignore $eol for that purpose.

What probably happens now:
the second time you run the program, you still receive data from the previous run.

Hope this helps

wollik 07-23-2012 12:05 PM

Reading binary data via tty
 
Hi 01000111,
I got a similar situation in reading binary data with my embedded Linux environment. Since this sytem is ARM based I can't do the reading wit a C-program because of the missing cross toolchain for mipsel.

So I try to read via the shell but can't get any succsess.

My Inverter sends every 4,8 seconds the PV data without beeing polled like in your example. In the other times there are fill bytes send. My problem is that my inverter sends binary data and no linefeed or CR. I have to check every byte to see if this is the start of a PV-Record but can't get any byte read in via my shell.

My version of the read also don't supports the -d command so I can't use the startbyte as a line delimiter.
I just want to continious read from the tty-port one byte, but can't get any of it.

Did you get your environment to run ?

I there just someone here in the forum that can help me on this ?

Doing a:

cat /dev/ttyS0 | hd -c

(assuming there is permanent binary data with no LF or CR)


Regards
WolliK

pcpostar 10-29-2012 11:05 AM

Quote:

I just want to continious read from the tty-port one byte, but can't get any of it.
Did you make any progress on this subject. I have the same difficult. I used command:
READ -n 1 character < /dev/tts/1
The READ command reply with illegal option -n.

sundialsvcs 10-29-2012 12:07 PM

Have no idea under the sun why you are using Bash for this. Use a real programming language. Even on DamnSmall, you have literally a dozen or more to choose from. . .

theNbomr 10-29-2012 07:50 PM

sundialsvcs is correct. Serial IO invariably requires the use of the termios API, and shells lack that capability. Some things just shouldn't be done with shell scripts.

--- rod.

Wim Sturkenboom 10-29-2012 11:39 PM

Please realize that this thread is old (and revived in june 2012 by wollik).

@wollik
have you tried a standard program like minicom just to see if you get something in?

pcpostar 10-31-2012 04:56 PM

Thank you for your answers. OK, i know that with some other programing languages it could be done more easily.

But I'm not a programer, and I'm not very familiar with Linux programing.

So what would you suggest me to use? i need really a simple program, that would continuously read from serial byte by byte and write to text file. That's all.

all other work is already done.

I'm doing my thesis at faculty and this is just some part. I'm doing something completely different and this reading from serial is just a small sub-problem...

Would anyone help me with that?

pcpostar 10-31-2012 06:14 PM

Quote:

Originally Posted by Wim Sturkenboom (Post 4817834)
@wollik
have you tried a standard program like minicom just to see if you get something in?

I've tried reading from serial. I'm getting data in. The thing is, that all the commands are waitng for carriage-return. But for protocol that i use, it shouldn't wait for CR. It should read every byte by byte.

Programs such as minicom, microcm, picocom... can they read byte by byte? I tried to play arround with them, but i couldn't get any data to be read from serial?

theNbomr 11-01-2012 08:39 AM

With the port in raw mode, the port should not wait for line terminators. In that case, I think read will get however many bytes are waiting in the receive buffer. How are the replies from your device delimited? Generally, these kinds of devices either send some kind of terminator, or send chunks of fixed-size data. If I understand your code correctly, you terminate reading data byte-at-time and appending to $InStr as soon as read times out once. Are you sure that your timeout is not too short? Would it make more sense to compose your $InStr until it of the length you require?

--- rod.

EDIT: Crap! I just replied to the OP, which probably doesn't apply to the person who first necroposted to this thread.
The answer to his/her question cannot be given without more information, whihc should be provide by starting a new thread, with more details.


All times are GMT -5. The time now is 07:43 PM.