11

I'm using a serial device for a project, and what I'm trying to accomplish PC side, is listening for a command sent by the serial device, interpreting the query, running some code depending on the query, and transmitting back the result.

To be honest I tried out using PHP as the listener, and it works, unfortunately the infinite loop required to make the script act as a receiver, loads the CPU to 25%. So it's not really the best option.

I'm using cygwin right now, I'd like to create a bash script using linux native commands.

I can receive data by using:

cat /dev/ttyS2

And send a response with:

echo "command to send" > /dev/ttyS2

My question is, how do I make an automated listener to be able to receive and send data? The main issue I have, is actually how do I stop the cat /dev/ttyS2 command once information was received, put it into a variable which then I could compare with a switch, or a series of if else then blocks. Afterwards send back a response and start the cycle all over again?

Thanks

dsolimano
  • 8,870
  • 3
  • 48
  • 63
Matt
  • 1,139
  • 1
  • 11
  • 28
  • bash wouldn't be my first choice. Might be a good time to try a little python or perl - both have libraries and examples for this. – stark Oct 20 '12 at 17:22
  • Or try using C and linux system programming. Still. Are you trying to create a server-client kind of program in bash script? – askmish Oct 20 '12 at 18:13
  • I've tried using C++, however I've never laid my hands on that kind of a programming language, so I failed .. Using bash makes more sense to me, as I can also easily use both Windows and Linux applications with Cygwin. One command may use a Windows executable or bat file to save the output to a variable which I pass on to the serial device, and I can use Linux commands (such as wget) on the other line without having to write an entire function to do so in C++. – Matt Oct 21 '12 at 10:20

4 Answers4

23

Is this not what you're looking for?

while read -r line < /dev/ttyS2; do
  # $line is the line read, do something with it
  # which produces $result
  echo $result > /dev/ttyS2
done

It's possible that reopening the serial device on every line has some side-effect, in which case you could try:

while read -r line; do
  # $line is the line read, do something with it
  # which produces $result
  echo $result > /dev/ttyS2
done < /dev/ttyS2

You could also move the output redirection, but I suspect you will have to turn off stdout buffering.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Is is just for me but in my case the Arduino is reset every time "< /dev/ttyS2" is called – bubblebath Dec 21 '14 at 19:42
  • 2
    @bubblebath: That's certainly possible, and the solution would be to move `< /dev/ttyS2` from where it is to after the `done`, which would avoid opening the `ttyS` device for every `read`. – rici Dec 21 '14 at 19:47
  • This worked well for me too. Small syntax error in your code - $result should be $line – user37309 Jul 02 '20 at 07:02
  • @user37309: the idea is that the code which replaces the comment *consumes* `$line` and *produces* `$result` as a response. Not that it simply echoes the line read. – rici Jul 02 '20 at 07:20
  • That may be the idea, but that should be in the comment. Why have an undefined variable in an example? – user37309 Jul 02 '20 at 23:50
  • @user37309: Ok. – rici Jul 03 '20 at 00:21
  • 1
    This solution worked for me in some cases, but a better answer is here: https://stackoverflow.com/a/28766475/847201 – ACK_stoverflow Mar 22 '22 at 17:49
1

To remain fairly system independent, use a cross platform programming language: like Python, use a cross platform serial library like : pySerial and do the processing inside a script. I have used pySerial and I could run the script cross platform with almost no changes in source code. By using BASH you're limiting yourself a fair little.

Aniket Inge
  • 25,375
  • 5
  • 50
  • 78
  • In a way I am limiting myself, but on the other hand bash is native in Linux as well as in Mac and available on Windows via Cygwin. Also I'm not using any additional software/packages/libraries that would not be available cross platform in the native state. By using simple commands such as `while read -r line < /dev/ttyS2; do` and `echo "No new emails" > /dev/ttyS2` I'm removing the dependance of required libraries or software that may not be ported cross platform or that interact differently with the OS in question. Your method is also more logical I guess, but I'm not familiar with Python :) – Matt Oct 21 '12 at 10:14
  • @Matt also if you're using cygwin you ARE getting libraries with it :) infact a whole UNIXY layer on top of windows – Aniket Inge Oct 21 '12 at 10:16
  • It works perfectly. I didn't have to install or tweak anything. I just opened Cygwin, ran `echo "led on" > /dev/ttyS2` and my serial device turned on the led instantly. For receiving data I had to initiate it with the stty command. I agree I am adding libraries TO Windows, but in a way, having a solution with Python would require almost all OSes to install Python as it isn't native, rather than just with Windows having to install Cygwin. It is a workaround I agree, but it's much more easier for myself personally. – Matt Oct 21 '12 at 10:22
0

If you use right tools, it is possible to actually have your CPU usage to be exactly 0 when your device does not have any output.

To accomplish this, you should use some higher level language (Perl, Python, C/C++ would do, but not bash) and use select loop on top of file handle of your serial device. This is an example for Perl http://perldoc.perl.org/IO/Select.html, but you can use any other language as long as it has support for select() syscall.

mvp
  • 111,019
  • 13
  • 122
  • 148
  • Using Cygwin, the listener process, created with the loop given by rici, (as shown in the Task Manager) uses 0% CPU and only 100k of memory. However once a request is received from the serial device, the CPU spikes up depending on the complexity of the required output (such as regexp matching, curl to get info from webpages etc), it ranges from 0% to 25%. I guess using a higher level language this would be smaller, but I'm happy with the results, as it's much better than having PHP loop infinitely with 25% CPU usage :) – Matt Oct 21 '12 at 10:08
-3

I would recommend to use C/C++ with Qt 5.1.1, it's really easy and if you are familiar with the framework it'll be a piece of cake!!! Here you can find more information and here more helpful examples, give it a try, it's really pain free!! Also you can develop on win and then port your code to linux...straight forward.

Declare an object like this:

QSerialPort mPort; //remember to #include <QtSerialPort/QSerialPort>
//also add QT += serialport to your .pro file

Then add this code:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    setupUi(this);


    connect(this->pushButton,SIGNAL(clicked()),this,SLOT(sendData()));

    mPort.setPortName("ttyS0");
    mPort.setBaudRate(QSerialPort::Baud115200);
    mPort.setParity(QSerialPort::EvenParity);

    if(!mPort.open(QSerialPort::ReadWrite))
    {
        this->label->setText(tr("unable to open port, %1").arg(mPort.error()));
    }

    connect(&(this->mPort),SIGNAL(readyRead()),this,SLOT(readData()));
}   

void MainWindow::sendData()
{

    QByteArray data = lineEdit->text().toLatin1();
    if(mPort.isOpen())
    {
        mPort.write(data);
    }
    else
    {
        this->label->setText(tr("port closed %1").arg( mPort.error()));

    }
}


void MainWindow::readData()
{

    QString newData;
    int bread=0;
    while(bread < mPort.bytesAvailable() ){
        newData += mPort.readAll();
        bread++;
    }
  this->textEdit->insertPlainText("\n" + newData);
}
ulitosCoder
  • 1,919
  • 1
  • 17
  • 22