LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Unable to echo to serial port from a bash script? (https://www.linuxquestions.org/questions/programming-9/unable-to-echo-to-serial-port-from-a-bash-script-4175699159/)

Atoss 08-14-2021 03:41 PM

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.

teckk 08-14-2021 05:20 PM

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


michaelk 08-14-2021 08:32 PM

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


Atoss 08-15-2021 04:11 AM

Quote:

Originally Posted by michaelk (Post 6275292)
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)

michaelk 08-15-2021 07:34 AM

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?

teckk 08-15-2021 08:01 AM

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
...


michaelk 08-15-2021 08:38 AM

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.

Atoss 08-15-2021 11:43 AM

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.

michaelk 08-15-2021 12:28 PM

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.


All times are GMT -5. The time now is 04:18 PM.