LinuxQuestions.org
Visit Jeremy's Blog.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-14-2021, 03:41 PM   #1
Atoss
LQ Newbie
 
Registered: Jul 2021
Posts: 7

Rep: Reputation: Disabled
Unable to echo to serial port from a bash script?


I am working on a small bash script, which involves going through devices on several serial ports, sending them a message and listening for their response. What I am using so far is:
Code:
for d in $(ls /dev | grep -E 'ttyUSB|ttyACM') ; 
do
   echo testing $d ;
   device="/dev/$d ;
   stty 9600 --file=$device; echo "" > $device ;
   (read -n32 -t5 resp < $device ; echo $resp)&
   read -p "" -t 0.5
   echo "id" > $device ;
   wait ;
done;
The device on the serial port responds with an affirmative message if it receives "id" (terminated either by newline or \0), or a negative message if it receives anything else.
Unfortunately, when I launch the script, it seems like nothing is returned from the device - the "testing /dev/ttyUSB0" gets echoed, but after that, I just get an empty line, and get thrown back to the prompt. When I try to execute the same commands manually - namely the (read -n32 -t5 resp < /dev/ttyUSB0 ; echo $resp)& and then echo "id" > /dev/ttyUSB0, I do get a response, however, it is not always the affirmative one. So far it seems like the first attempt gets an affirmative, then the next one doesn't unless I echo anything else to the serial port before running read.
Some help in getting the script to work would be appreciated.
 
Old 08-14-2021, 05:20 PM   #2
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,137
Blog Entries: 6

Rep: Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826
Comments on your script

Code:
#This is ok
for d in $(ls /dev | grep -E 'ttyUSB|ttyACM'); do

    #echo testing $d
    echo "testing "$d""

    #device="/dev/$d
    device=/dev/"$d"

    #stty 9600 --file=$device
    stty 9600 --file="$device"

    #You can't echo into a variable
    echo "" > $device 

    #You can redirect from a variable
    #read -n32 -t5 resp < $device
    read -n32 -t5 resp <<< "$device"
    
    #echo $resp
    echo "$resp"
    
    #read -p "" -t 0.5
    sleep .5
    
    #You can't echo into a variable
    echo "id" > $device
    
    wait
done
Code:
d="sda"
device="/dev/"$d""
echo "$device"
/dev/sda

read -n32 -t5 resp <<< "$device"
echo "$resp"
/dev/sda

a=5
echo "cat" > "$a"
echo "$a"
5

device="/dev"
device=""$device"/sda"
echo "$device"
/dev/sda
 
1 members found this post helpful.
Old 08-14-2021, 08:32 PM   #3
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,700

Rep: Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895
There are a bunch of syntax errors in your script.
I am not a big fan of using bash for serial communications but I found something that works fairly well using file descriptors for a simple test.
Untested...

Code:
#!/bin/bash

readbuf() {
    while IFS= read -d '' -t .1 -u 6 -r -n 1 char;do
      echo -n "$char"
    done
}

for device in $(ls /dev/tty* | grep -E 'ttyUSB|ttyACM') ; 
do
  stty -F $device 9600
  exec 5>$device
  exec 6<$device

  echo "id" >&5
  readbuf
  
  exec 6<&-
  exec 5<&-
done
 
1 members found this post helpful.
Old 08-15-2021, 04:11 AM   #4
Atoss
LQ Newbie
 
Registered: Jul 2021
Posts: 7

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by michaelk View Post
There are a bunch of syntax errors in your script.
I am not a big fan of using bash for serial communications but I found something that works fairly well using file descriptors for a simple test.
Untested...

Code:
#!/bin/bash

readbuf() {
    while IFS= read -d '' -t .1 -u 6 -r -n 1 char;do
      echo -n "$char"
    done
}

for device in $(ls /dev/tty* | grep -E 'ttyUSB|ttyACM') ; 
do
  stty -F $device 9600
  exec 5>$device
  exec 6<$device

  echo "id" >&5
  readbuf
  
  exec 6<&-
  exec 5<&-
done
"bunch of syntax errors" is putting it gently, I'd expect the script to be packed to the brim with them - it is my first attepmt, after all . I tried this, and it seems not to work - nothing gets echoed. I'm a bit puzzled by the read -d '' part - does that imply reading until encountering a \0 terminator? Also, what would you use instead of bash? I did not have any specific motivation for picking bash. Minicom is capable of running scripts, but I had very limited success with that - when running the script by the -S parameter, I get no response. Running it afterwards from the menu works, but that's not very helpful. Thank you for the help so far, though.

I do suspect there might be some problems with the termination of what gets sent, TBF, since sending the chars manually from a serial monitor works just fine (i, d, enter - the device on the other end will consider either \n, \r or \0 to signify an end of a message)
 
Old 08-15-2021, 07:34 AM   #5
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,700

Rep: Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895
Any language that has a serial port library like c or python. In this case it does not matter but the d '' change the end of line character from a newline to nothing.
try:
Code:
  echo -e "id\n\r" >&5
I do have a serial port with a loopback adapter but it can not simulate a real device. I assume your devices all use 9600,8,n,1 for serial port settings?
 
Old 08-15-2021, 08:01 AM   #6
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,137
Blog Entries: 6

Rep: Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826Reputation: 1826
Quote:
I'm a bit puzzled by the read -d '' part
Code:
help read
Quote:
I don't know exactly what you are doing. I looked at your script and don't have any of those device nodes. I also don't want to change speeds. Here is a nothing example.
Code:
for d in $(ls /dev | grep 'tty'); do

    echo "testing "$d""
    
    stty -F /dev/tty1 -a | head -n 1
    
    device="hello "$d""
    
    read -n20 -t5 resp <<< "$device"
    
    echo -e ""$resp"\n"
    
    sleep .5
done

testing tty
speed 38400 baud; rows 67; columns 240; line = 0;
hello tty

testing tty0
speed 38400 baud; rows 67; columns 240; line = 0;
hello tty0

testing tty1
speed 38400 baud; rows 67; columns 240; line = 0;
hello tty1

testing tty10
speed 38400 baud; rows 67; columns 240; line = 0;
hello tty10
...
 
Old 08-15-2021, 08:38 AM   #7
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,700

Rep: Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895
By the way you can echo/redirect to a variable if it is a file or a character device etc.
Code:
var=test.txt
echo "hello" > $var
The OP is trying to send a command to a device connect by serial port (RS-232) and receive a response. /dev/ttyUSBX or /dev/ttyACMX are device IDs from some device connected to a USB port like a USB serial port adapter. To communicate successfully the serial port speed (baud rate), number of bits. number of stop bits and parity must be the same as the connected device or all you get is garbage.
 
1 members found this post helpful.
Old 08-15-2021, 11:43 AM   #8
Atoss
LQ Newbie
 
Registered: Jul 2021
Posts: 7

Original Poster
Rep: Reputation: Disabled
Ok, so I managed to cobble something together, shamelessly stealing from several examples and possibly following every bad practice under the sun.
This is what I managed:

find_thing.sh (run as . find_thing.sh):
Code:
#!/bin/bash

target="thing"
baudrate=9600

if [ -f ~/minicom.out ]; then rm ~/minicom.out; fi;

for device in $(ls /dev/tty* | grep -E "ttyUSB|ttyACM");
do
   if [ -f ~/minicom.out ]; then rm ~/minicom.out; fi;
   touch minicom.out

   nohup bash -c "minicom -b $baudrate -D $device -S minicom_script.txt -C minicom.out" & > nohup.txt ;
   wait

   lastline=$(tail ~/minicom.out)
   echo $lastline
   if [ "$lastline" = "$target" ]
   then
      echo "match"
      export THING="$device"
   fi

done;
minicom_script.txt:
Code:
send ""    # didn't work if I removed either of these
sleep 1.5  # I understand the sleep, but what the empty send does, no idea

send "id"
expect {
   "thing" ! killall minicom
   timeout 3 ! killall minicom
}
I suppose I could've explained my situation better: I have a system with several devices connected over USB serial, and the OS tends to assign the device files on bootup at random, even going as far as registering the same device as ttyUSB* or ttyACM* at different times. The device works either way, but the programs running on the system assume a known location for each device. The idea is that I will create a single script for each device, and that script will put the location of said device in an environment variable. Kind of messy, but works, and also solves the problem with the one device that has a different baudrate. Sure, a generalised approach would be much nicer, but perfectionism is the worst enemy of completion. This works.
I do suppose though that I could at least implement some kind of checking for accepted outputs - maybe in the minicom script, although that was...interesting to work with.
 
Old 08-15-2021, 12:28 PM   #9
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,700

Rep: Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895
There are ways via udev to automatically assign a device ID to the same adapter.
If what you have works for you good.

I had to do something similar with some test equipment many years ago using Labview using windows.
 
  


Reply



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
How to Communicate Virtual Serial Port on Host(windows) to Communicate with Virtual Serial Port on Guest System(Linux) aquamarine Linux - Newbie 2 09-16-2016 02:48 PM
echo 0:$(echo 8*35*37*47 | bc) | xxd -r && echo $(id -un) Linuxanity LinuxQuestions.org Member Intro 1 08-15-2012 06:30 PM
Is a USBtty (USB serial port) treated the same as tty (normal serial port) in C? spudgunner Programming 1 11-12-2010 01:19 PM
BASH: How to NOT echo to screen with "if echo $x | grep ".*"; then" eur0dad Programming 9 07-27-2006 02:14 PM
Kphone echo (echo echo) scabies Linux - Software 0 10-18-2004 02:59 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 12:42 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
Open Source Consulting | Domain Registration