Linux - HardwareThis forum is for Hardware issues.
Having trouble installing a piece of hardware? Want to know if that peripheral is compatible with Linux?
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Hi, i got a device that send information over serail port (clear ANCSI code). I am looking for a way to recive those information in a bash script and using them.
I can use sudo cat /dev/ttyS0 to see the iunformation, but i want it so stop after each transmition and run few command. But i cant stop cat like that so it is not the right solution.
My script will look like:
Code:
#/bin/bash
useless=1
while [ $useless -ne 10 ];
do
#Recive the information here (and wait for it if possible)
xvkbd -xsendevent -text "The content from serial"
done
How can i recive information from port when it send it and use it?
If your device sends "lines" that end in a newline character (a.k.a line feeds, a.k.a 0x0A), then I think the following would work:
Code:
sudo cat /dev/ttyS0 | while read var1 var2 var3; do
# Do stuff with $var1 $var2, $var3, etc here
done
See the bash man page for details on the read command and how input gets assigned to variables. You will execute the body of the do loop once for each line of input. When comming from a serial port, I am not sure what causes exit from the do loop -- perhaps an ASCII EOT character?
Depending on the format of your data stream, you may need to pipe the output of the serial device through some tr and or sed command(s) before piping to the while statement.
Also, be aware that prior to giving the commands, you may need to set options for the serial device use the stty command.
Thanks for answering my question. Your idea is good and may work, but the device never send 0A, it only send letter and number, one by one. I could edit the application in the device, but i never used this kind of language and i am not sure if GCC can compile the apps for this thing. There is any way to just react to the transmition without cat?
I am not familiar with xvkbd. I tried to do a quick review of it on the Internet, but it was not immediately obvious to me why it is relevant to this problem. (I'm not saying it isn't relevant, but with the time I put in I couldn't make the connection.) So unless you can point me to something to get me up to speed with it quickly, I don't think I can help you with it.
If there was a way to turn the breaks into some character (different than what your device puts out), you could use tr to turn those into new lines and you could use the construct i showed you. But I haven't been able to find anyway to do that. The stty command has provisions for having breaks cause an "interrupt signal" but I haven't figured out any way to use that. I am beginning to suspect that your needs are little bit more than can be handled with a bash script. Perhaps perl or python could be used to solve your problem, but I am not familiar with those. I am certain a (probably pretty simple) C program could solve the problem, but I am far to rusty to feel qualified to help you.
The only thing to find is how to put sudo cat /dev/ttyS0 2>/dev/null is a variable.
It seems to me the basic problem is the lack of newlines in your data stream. W/o that, I don't think the normal variable assignement is going to work. However, the bash read command can be told to read a certain number of characters only. However, when I tried
Code:
echo abcdefg | read -n2 var
echo $var
There was nothing in $var, for reasons I don't understand. However,
Code:
echo abcdefg | while read -n2 var; do
echo $var
done
produced the expected results. So I think something like the following might work for you
Code:
#!/bin/bash
PORT=/dev/ttyS0
MAX_CHARS=100
# There may be additional parameters you need to use with stty, and/or some of these may need to be deleted.
# But get the port set up appropriately ...
sudo stty -echo -brkint ignbrk -F $PORT
# Now read in data, pairing a letter with a number
CNT=0; SECOND=FALSE
sudo cat $PORT | while read -n1 CHAR; do
let "CNT += 1"
[ $CNT -gt $MAX_CHARS ] && break
case $CHAR in
[a-zA-Z]) PAIR=$CHAR; SECOND=TRUE; continue;;
[0-9]) [ $SECOND != TRUE ] && continue || PAIR=$PAIR$CHAR; SECOND=FALSE;;
*) SECOND=FALSE; continue;;
esac
# The $PAIR variable now contains a letter followed by a number, which you can
# use as you see fit.
done
I followed your example of accessing the serial port using sudo. It would probably be a little better to change the owner or group (and permissions) of the device so that you can access access it w/o sudo. For example, if the group of /dev/ttyS0 was serial, the group permission were rw and the user running this was a member of serial, then you would not need to use sudo.
It seems to me the basic problem is the lack of newlines in your data stream...
I want to read some data from /dev/ttyS0 and code from blackhole54 is inspirational.
But variables in while loop is only local and it is not usable in next code.
It is good to see in next simplified code:
Code:
MAX_CHARS=100
CNT=0; SECOND=FALSE; PAIR="abc"
echo dfghfhuguhyou235v | while read -n1 CHAR; do
let "CNT += 1"
[ $CNT -gt $MAX_CHARS ] && break
case $CHAR in
[a-zA-Z]) PAIR=$CHAR; SECOND=TRUE; continue;;
[0-9]) [ $SECOND != TRUE ] && continue || PAIR=$PAIR$CHAR; SECOND=FALSE;;
*) SECOND=FALSE; continue;;
esac
echo 1: $PAIR $CNT/$MAX_CHARS
# The $PAIR variable now contains a letter followed by a number, which you can
# use as you see fit.
done
echo 2: $PAIR $CNT/$MAX_CHARS
This script writes:
1: u2 14/100
2: abc 0/100
But it should writes:
1: u2 14/100
2: u2 14/100
I want to use data in variable $PAIR and $CNT "global", but I don't know what is need to change.
But variables in while loop is only local and it is not usable in next code.
The solution is to avoid having the while command in a pipe:
Code:
while read -n1 CHAR
do
<whatever you want to do>
done < /dev/ttyS0
That loop will hang waiting for input from /dev/ttyS0. There is a timeout option for the read command which you may find useful. AFAIK there is no way to implement a "non-blocking" read in shellscript so read with a short timeout is the closest approximation that is simply achievable.
I want to read some data from /dev/ttyS0 and code from blackhole54 is inspirational.
But variables in while loop is only local and it is not usable in next code.
It is good to see in next simplified code:
Code:
MAX_CHARS=100
CNT=0; SECOND=FALSE; PAIR="abc"
echo dfghfhuguhyou235v | while read -n1 CHAR; do
let "CNT += 1"
[ $CNT -gt $MAX_CHARS ] && break
case $CHAR in
[a-zA-Z]) PAIR=$CHAR; SECOND=TRUE; continue;;
[0-9]) [ $SECOND != TRUE ] && continue || PAIR=$PAIR$CHAR; SECOND=FALSE;;
*) SECOND=FALSE; continue;;
esac
echo 1: $PAIR $CNT/$MAX_CHARS
# The $PAIR variable now contains a letter followed by a number, which you can
# use as you see fit.
done
echo 2: $PAIR $CNT/$MAX_CHARS
This script writes:
1: u2 14/100
2: abc 0/100
But it should writes:
1: u2 14/100
2: u2 14/100
I want to use data in variable $PAIR and $CNT "global", but I don't know what is need to change.
catkin correctly identified your problem and the solution: if you use a pipe to get the information into the while loop, then the while loop runs in a subprocess and any changes to the variables occuring in the while loop cannot be seen outside it. If you bring the information into the while loop by redirecting it from the done statement, then everything is the same process and changes to variables will persist.
The following modification to your script illustrates this (I've highlighted modifications in red, put typed commands in bold and added a blank line for readability):
Code:
$ cat /tmp/test
#!/bin/bash
MAX_CHARS=100
CNT=0; SECOND=FALSE; PAIR="abc"
while read -n1 CHAR; do
let "CNT += 1"
[ $CNT -gt $MAX_CHARS ] && break
case $CHAR in
[a-zA-Z]) PAIR=$CHAR; SECOND=TRUE; continue;;
[0-9]) [ $SECOND != TRUE ] && continue || PAIR=$PAIR$CHAR; SECOND=FALSE;;
*) SECOND=FALSE; continue;;
esac
echo 1: $PAIR $CNT/$MAX_CHARS
break
done < <(echo dfghfhuguhyou235v)
echo 2: $PAIR $CNT/$MAX_CHARS
$ /tmp/test
1: u2 14/100
2: u2 14/100
Note that I used a bash feature called process substitution to redirect the information into the loop. Some (most?) other shells don't have this feature so if you are using a different shell you may have to find a different way to redirect into the loop. I also added a break statement after a pair was matched -- otherwise additional iterations of the loop would destroy the values of the variables you were looking for.
If you really want/need to pipe the information into the loop instead of redirect it, the only thing I know is to have the loop write the info to a file and then read it back out of the file when you are out of the loop. It's a bit ugly, but it works.
Last edited by blackhole54; 06-08-2010 at 07:06 AM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.