LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Linux Forums > Linux - Hardware
User Name
Password
Linux - Hardware This forum is for Hardware issues.
Having trouble installing a piece of hardware? Want to know if that peripheral is compatible with Linux?

Notices



Reply
 
Search this Thread
Old 12-15-2007, 04:20 PM   #1
Elv13
Member
 
Registered: Apr 2006
Location: Montreal,Quebec
Distribution: Gentoo
Posts: 825

Rep: Reputation: 128Reputation: 128
Creating a bash script with serial port input


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?
 
Old 12-16-2007, 05:43 AM   #2
blackhole54
Senior Member
 
Registered: Mar 2006
Posts: 1,896

Rep: Reputation: 61
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.

I hope this can at least get you started.
 
Old 12-16-2007, 03:08 PM   #3
Elv13
Member
 
Registered: Apr 2006
Location: Montreal,Quebec
Distribution: Gentoo
Posts: 825

Original Poster
Rep: Reputation: 128Reputation: 128
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?
 
Old 12-16-2007, 04:17 PM   #4
Elv13
Member
 
Registered: Apr 2006
Location: Montreal,Quebec
Distribution: Gentoo
Posts: 825

Original Poster
Rep: Reputation: 128Reputation: 128
Sorry for double posting, but i found something. This script -do- break after each letter, but i dont know how i can use data from it.

#/bin/bash
cpt=1
while [ $cpt -ne 10 ];
do

sudo cat /dev/ttyS0 2>/dev/null| xvkbd -xsendevent -text "it almost work!"
echo "did break"

done

____
i did try

var1=`sudo cat /dev/ttyS0 2>/dev/null| break`
xvkbd -xsendevent -text $var1

but the break return nothing...

EDIT: also tested:
sudo cat /dev/ttyS0 2>/dev/null| > test22.txt
dont work, the file is created, but empty

Last edited by Elv13; 12-16-2007 at 04:21 PM.
 
Old 12-17-2007, 04:13 AM   #5
blackhole54
Senior Member
 
Registered: Mar 2006
Posts: 1,896

Rep: Reputation: 61
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.
 
Old 12-17-2007, 07:13 PM   #6
Elv13
Member
 
Registered: Apr 2006
Location: Montreal,Quebec
Distribution: Gentoo
Posts: 825

Original Poster
Rep: Reputation: 128Reputation: 128
xvkbd is a keyboard emulator. It translate input into keypress, it is exactly what i want

The only thing to find is how to put sudo cat /dev/ttyS0 2>/dev/null is a variable.
 
Old 12-18-2007, 03:20 AM   #7
blackhole54
Senior Member
 
Registered: Mar 2006
Posts: 1,896

Rep: Reputation: 61
Quote:
Originally Posted by Elv13 View Post
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.
 
Old 06-03-2010, 12:47 PM   #8
bizonik
LQ Newbie
 
Registered: Jun 2010
Posts: 2

Rep: Reputation: 0
Quote:
Originally Posted by blackhole54 View Post
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.
 
Old 06-03-2010, 02:33 PM   #9
bizonik
LQ Newbie
 
Registered: Jun 2010
Posts: 2

Rep: Reputation: 0
More simplified code:
Code:
#!/bin/bash
P=1
echo abc | while read -n1 CHAR; do
  P=$P$CHAR
  echo -n $P,
done
echo;echo $P
Writes:
1a,1ab,1abc,1abc,
1
Should writes:
1a,1ab,1abc,1abc,
1abc

Note: In real code is "cat /dev/ttyS0" instead "echo abc".
It is only for test.
 
Old 06-04-2010, 12:23 AM   #10
catkin
LQ 5k Club
 
Registered: Dec 2008
Location: Tamil Nadu, India
Distribution: Servers: Debian Squeeze and Wheezy. Desktop: Slackware64 14.0. Netbook: Slackware 13.37
Posts: 8,563
Blog Entries: 29

Rep: Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179Reputation: 1179
Quote:
Originally Posted by bizonik View Post
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.
 
Old 06-08-2010, 08:01 AM   #11
blackhole54
Senior Member
 
Registered: Mar 2006
Posts: 1,896

Rep: Reputation: 61
Quote:
Originally Posted by bizonik View Post
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 08:06 AM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Bash Sleep vs crontab and bash serial port nutthick Programming 4 06-01-2006 03:42 AM
Why can't I read in data from the serial port using a bash script? tjt Linux - Newbie 1 06-17-2004 01:21 AM
send automatic input to a script called by another script in bash programming jorgecab Programming 2 04-01-2004 01:20 AM
bash-script input aizkorri Programming 7 07-08-2003 07:15 AM
How to Redirect serial port input to a file Legaviu Linux - General 1 07-31-2001 08:23 PM


All times are GMT -5. The time now is 11:20 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration