c++linuxqtserial-portvirtual-serial-port

Using Linux pseudoterminal to test QSerialPort


I would like to test an application using Qt serial ports with a pseudoterminal. According to the man pages I implemented:

// open master
QSerialPort master("/dev/ptmx");
master.open(QSerialPort::ReadWrite);
int master_fd = master.handle();

// get device name of slave pseudoterminal
constexpr size_t PTSNAME_BUFFER_LENGTH = 128;
char ptsname_buffer[PTSNAME_BUFFER_LENGTH];
if (ptsname_r(master_fd, ptsname_buffer, PTSNAME_BUFFER_LENGTH) != 0)
    return 0;

// grant access to the slave
if (grantpt(master_fd) != 0)
    return 0;

// unlock the slave
if (unlockpt(master_fd) != 0)
    return 0;

// open slave
std::cout << "Slave pseudoterminal: " << ptsname_buffer << std::endl;
QSerialPort slave(ptsname_buffer);
slave.open(QSerialPort::ReadWrite);

// test communication
master.write("Hello World");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::cout << "Received: " << slave.readAll().toStdString() << std::endl;

Creating the slave device seems to work (in my case it is created at /dev/pts/2). However, the slave.readAll() command always returns an empty string.

Is it possible to test the QSerialPort with a pseudoterminal?


Solution

  • Qt is executed inside an event loop so you need to wait for the communication to be processed, in Qt you should work asynchronously using the signals:

    main.cpp

    #include <QCoreApplication>
    #include <QSerialPort>
    
    #include <iostream>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        QSerialPort master("/dev/ptmx");
        if(!master.open(QSerialPort::ReadWrite))
            std::cout<<"The master port was not opened";
    
        int master_fd = master.handle();
    
        // get device name of slave pseudoterminal
        constexpr size_t PTSNAME_BUFFER_LENGTH = 128;
        char ptsname_buffer[PTSNAME_BUFFER_LENGTH];
        if (ptsname_r(master_fd, ptsname_buffer, PTSNAME_BUFFER_LENGTH) != 0)
            return -1;
    
        // grant access to the slave
        if (grantpt(master_fd) != 0)
            return -1;
    
        // unlock the slave
        if (unlockpt(master_fd) != 0)
            return -1;
    
        // open slave
        std::cout << "Slave pseudoterminal: " << ptsname_buffer << std::endl;
        QSerialPort slave(ptsname_buffer);
        if(!slave.open(QSerialPort::ReadWrite))
            std::cout<<"The slave port was not opened";
    
        QObject::connect(&slave, &QSerialPort::readyRead, [&](){
            std::cout << "Received: " << slave.readAll().toStdString() << std::endl;
            a.quit();
        });
        // test communication
        master.write("Hello World");
        return a.exec();
    }
    

    Output:

    Slave pseudoterminal: /dev/pts/3
    Received: Hello World
    

    Note: Do not use std::this_thread::sleep_for since it is a blocking task that prevents the event loop from executing.