0

I have to develop a C++ program for an embedded FriendlyARM-based processor system. I use Qt Creator 3.0.0 (based on Qt 5.2.0) for desktop computer. My program should be able to read from serial port at Mini2440 FriendlyARM processor.

Before going to target system (embedded system), I tried to read and write from/to a serial port on my Laptop. My main problem is how to read from serial port. As you know, new computers and laptops don't have serial port so I try to simulate serial port programming using hand-made USB-to-serial adapter cable. When the USB serial cable is plugged in, it is recognized as "/dev/ttyUSB0" on Ubuntu. It seems to work well. Please note, the other end of cable (the serial port) isn't connected to anything.

My first question is: Is it OK to configure cable like this, or I have to connect it to other device? I try to wrote to /dev/ttyUSB0 each 10 seconds and read the data. I ended up the following code:

void MainWindow::refreshNotificationArea()
{
    generateNotifAreaData(); // a typical random data-generator
    QList<QSerialPortInfo> L = QSerialPortInfo::availablePorts();
    for (auto e : L)
        qDebug() << e.portName() << '\n'; // it prints 1 serial port: :ttyUSB0
    // write to the port
    QSerialPort notifAreaPort;
    // 1. set properties
    notifAreaPort.setBaudRate(QSerialPort::Baud9600); 
    notifAreaPort.setStopBits(QSerialPort::OneStop);
    notifAreaPort.setParity(QSerialPort::NoParity); 
    notifAreaPort.setDataBits(QSerialPort::Data8);
    notifAreaPort.setFlowControl(QSerialPort::NoFlowControl); 
    QObject::connect(&notifAreaPort,SIGNAL(error(QSerialPort::SerialPortError)),
    this, SLOT(errorReport(QSerialPort::SerialPortError)));
    notifAreaPort.setPortName(serial_device.c_str());
    // 2. open port
    notifAreaPort.open(QIODevice::ReadWrite);
    if (!notifAreaPort.isOpen())
        qDebug() << "Open failed"; // open is OK, no error message printed

    string s  = convertNotifAreadData2Str();
    qDebug() << "Generated data " << s.c_str(); // OK
    int a = notifAreaPort.write(s.c_str()); // write done
    qDebug() << "Write count" << a; // OK

    // now read the info

    QByteArray ba = notifAreaPort.readLine(3); // read failed
    QSerialPort::SerialPortError err = notifAreaPort.error();
    qDebug() << "Error code" << err;
    qDebug() << "What? " << notifAreaPort.errorString();
    qDebug() << "Read count " << ba.size(); // 0

    notifAreaPort.close();
}

void MainWindow::errorReport(QSerialPort::SerialPortError error)
{
    if(error!=0)
        qDebug()<<"ERROR:"<<endl<<error; // nothing printed
}

Writing to serial port is OK. but Reading issues sometimes "No such file or directory"! sometimes "File temporarily unavalable! The strange thing is notifAreaPort.error() returns 0, and it means no error occured!

Thoughts?

-- Saeed Amrollahi Boyouki

László Papp
  • 51,870
  • 39
  • 111
  • 135

3 Answers3

1

There are a couple of issues in your code, but I will highlight the most important of those:

  • You are setting the parameters before opening. This should happen after opening. That is how the API was sadly designed, but we are in the process of revamping it.

  • You should command line examples for reading that I added in 5.2? It seems that you do not know how to read and those would give you a simple example. In short: you are basically trying to read before the write potentially even finished.

László Papp
  • 51,870
  • 39
  • 111
  • 135
1

You cannot (well, SHOULD not) write then read from a QSerialPort in the same function.

There are two methods which I use for QSerialPort processing:

METHOD ONE Create and open your QSerialPort object. Set up a QTimer with a timeout of around 50 ms or so (depends on hardware).

Connect the readyRead() signal to a slot which basically just reads all data into your buffer (QByteArray is ideal for this). The slot stops the QTimer, reads all data available with readAll() and then restarts the QTimer and returns.

Connect the timeout signal of the QTimer to a function to process the read bytes of input.

The premise here is that eventually all data will have arrived and the QTimer will timeout, at which point you will have had all of your data in the buffer to process.

METHOD TWO The slot which handles readyRead() signal can check all data in the buffer for some "marker" which denotes that some chunk of data has fully arrived. Many devices use 0x0D or 0x0d0x0A as the delmiter. Others use NULL 0x00 or some other byte.

Evaluate the buffer at each iteration of the readyRead() handler slot.

This example shows the second choice and it works well for small reads.

r_port = new QSerialPort(this);
r_port->setPortName("COM3");
r_port->setBaudRate(QSerialPort::Baud9600);
r_port->setDataBits(QSerialPort::Data8);
r_port->setParity(QSerialPort::NoParity);
r_port->setStopBits(QSerialPort::OneStop);
r_port->setFlowControl(QSerialPort::NoFlowControl);
if (r_port->open(QSerialPort::ReadWrite))
{
    connect(r_port, &QSerialPort::readyRead, this, &MYPROG::on_readyRead);
    connect(r_port, &QSerialPort::errorOccurred, this, &MYPROG::breakCaught);
}
else
{
    QMessageBox::critical(this, "SERIAL PORT NOT CONNECTED", "Unable to connect to the radio.\n\nPlease check your connections\nand configuration and try again.");
    return;
}

void MYPROG::on_readyRead()
{
 // keep reading until we get a \r\n delimiter
    rxBytes.append(r_port->readAll());
    qDebug()<<"raw rxBtes"<<rxBytes;
    if(!rxBytes.contains("\r\n"))
    {
        return;
    }
    int end = rxBytes.lastIndexOf("\r\n") + 2;
    QStringList cmds = QString(rxBytes.mid(0, end)).split("\r\n", QString::SkipEmptyParts);
    rxBytes = rxBytes.mid(end);
    foreach(QString cmd, cmds){
        qDebug()<<"serial read"<<cmd;
    }
}

This allows the QSerialPort to be read when data arrives and then the program can return to the event loop in order to keep a GUI from becoming unresponsive. Typical serial reads are small and of short time duration so UI freezing rarely happens using these methods.

guitarpicva
  • 432
  • 3
  • 10
0

„Hand-made USB to serial adapter“ - sounds interesting. Are you sure that this works correctly?. I think it’s a good idea to connect PIN 2(Rx) and 3(Tx), so you getting data. Now you can test your device with any other terminal software. I use for serial ports always the readyRead() signal and I check before reading with port->bytesAvailable(). And I open my port with port->open(QIODevice::ReadWrite | QIODevice::Unbuffered).

Matthias
  • 51
  • 3
  • Thank you for your feedback Matthias. I'll check the cable later. Please answer to my first question. Just to test serial port reading and writing, is it OK, one end of serial cable connected to Laptop and another end unconnected to device (whatever serial device)? – Saeed Amrollahi Boyouki Feb 08 '14 at 11:37
  • Matthias. From QSerialPort::open() doc: – Saeed Amrollahi Boyouki Feb 08 '14 at 11:49
  • Warning: The mode has to be QIODevice::ReadOnly, QIODevice::WriteOnly, or QIODevice::ReadWrite. Other modes are unsupported. – Saeed Amrollahi Boyouki Feb 08 '14 at 11:49
  • You wrote „qDebug() << "Read count " << ba.size(); // 0“ - zero is the size you read from the port. You didn’t have any loop or a device connected, so this value is fine. But this is not a real test, because you only know that Qt didn’t reports an error. You didn’t know that it’s sends correctly to a connected device. When exactly do you got the „No such file or directory“ error? On open, close, read or write? – Matthias Feb 08 '14 at 12:37
  • I took your source and checked your situation (no device connected). I added port.clearError() before reading the port and now I get this „Socket operation on non-socket“ error (error code=0). And when I additional add port.waitForReadyRead(1); I get same error but the error code 12, which means timeout and this is true ;-) – Matthias Feb 09 '14 at 13:51
  • @Matthias: I am not sure where you got the Unbuffered idea from, but we have not made that work. It should also be documented as aforementioned. If the documentation is not clear about it, we need to improve it until we add support for that. Currently, everything is buffered, unfortunately. – László Papp Feb 12 '14 at 12:58
  • @Laszlo: No, the document is pretty clear. I only had a (too short) look into my application and I use dependent on settings different QIODevice’s, but I always open with the unbuffered mode… so my fault. – Matthias Feb 13 '14 at 16:17