Question

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

Was it helpful?

Solution

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

OTHER TIPS

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.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top