Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QtSerialPort weird behaviours on MacOSX



  • Hi, I'm trying to make an application that uses the serial port and I can't get it to work. To try to see where the problem comes from, I am testing with this simple application.

    What I observed are behaviors that do not make much sense or I am not able to understand.

    The application most of the time detects the serial port as occupied by another application when no other application is using it and the few times that it manages to detect it as free it does not produce any output, it seems that it sends data but does not send anything.

    Does anyone know what he could be doing wrong or how he can debug the cause of the error?

    Greetings and thanks in advance for the help.

    #include <QDebug>
    #include <unistd.h>
    #include <QtSerialPort/QSerialPort>
    
    int main(int argc, char *argv[])
    {
          QSerialPort* serial = new QSerialPort();
          serial->setPortName("/dev/cu.usbserial-14130");
    
           serial->open(QSerialPort::ReadOnly);
           serial->setBaudRate(QSerialPort::Baud9600);
           serial->setDataBits(QSerialPort::Data8);
           serial->setFlowControl(QSerialPort::NoFlowControl);
           serial->setParity(QSerialPort::NoParity);
           serial->setStopBits(QSerialPort::OneStop);
    
          if (serial->open(QIODevice::ReadWrite))
           {
               QString hex = "command\r";
               QByteArray test = QByteArray::fromHex(hex.toLatin1());
    
               while(true){
                   if (serial->isWritable()){
                       serial->write(test.data());
                       qDebug() << "Sending data";
                   }
                   else{
                       qDebug() << "I can not write ";
                   }
    
                  sleep(10/1000);
                  qDebug("Another Loop") ;
              }
           }
           else
           {
              qDebug("Failed to open Serial Port") ;
              qDebug() << "Port Name : " << serial->portName();
              qDebug() << "Serial port eror: " << serial->errorString() ;
              serial->close();
              return -1;
           }
    }
    

    Obtained output

    Failed to open Serial Port
    Port Name :  "cu.usbserial-14130"
    Serial port eror:  "Device is already open"
    

  • Lifetime Qt Champion

    @JorgeMaker said in QtSerialPort weird behaviours on MacOSX:

    May be I do not understand how QtSerialPort works

    It's not just about QtSerialport it is how Qt (or any other event driven framework) works.
    Simple rule: never block Qt event loop with long lasting loops, or even worse using sleep().
    Use a QTimer with appropriate timeout and send what you need in the slot connected to the https://doc.qt.io/qt-5/qtimer.html#timeout slot.


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Which version of macOS are you running ?
    Which type of serial port is it ? FTDI ?
    Which version of Qt are you using ?



  • Hi @SGaist thank you very much for your quick answer :)

    Here there is the information you requested:

    Which version of macOS are you running ? :

    • MacOS BigSur version11.4

    Which type of serial port is it ? FTDI ?

    • Is a USB to TTL converter based on CH340C . I scoped both Tx and RX pins with an oscilloscope and no activity is detected ..... both lines remain pulled up and the oscilloscope never triggers. I have tested this device using other applications and works without any problem using the same /dev/cu.usbserial-14130 serial port .

    Which version of Qt are you using ?

    • Qt 5.12.0 (x86_64-little_endian-lp64 shared (dynamic) release build; by Clang 10.0.0 (clang-1000.11.45.2) (Apple)) on "cocoa"
      OS: macOS 10.16 [darwin version 20.5.0]

    Observations:

    • 1.- Something that I do not understand is why serial->setPortName is printed as "cu.usbserial-14130" while I am initializing it with "/dev/cu.usbserial-14130" ... Does it could be a sign that something is going wrong?
    • 2.- I installed QtCreator a while time ago, .... I don't know if it makes sense to uninstall it and install a newer version to see if this solves my issue.

    Regards :)


  • Moderators

    @JorgeMaker said in QtSerialPort weird behaviours on MacOSX:

    while(true){
    if (serial->isWritable()){
    serial->write(test.data());
    qDebug() << "Sending data";
    }
    else{
    qDebug() << "I can not write ";
    }

              sleep(10/1000);
              qDebug("Another Loop") ;
          }
    

    It doesn't write, when you're connected because of your while loop.
    And its also the reason why you get the port not open error. It switches internally from attemotToConnect state to connected state once the EventLoop is running, it's a queued connection.

    The data will be passed to the OS when the event queue is processed, that doesn't happen while looping.



  • @J-Hilk said in QtSerialPort weird behaviours on MacOSX:

    t doesn't wri

    Hi @J-Hilk thanks for your answer ,

    May be I do not understand how QtSerialPort works. Could you suggest an alternative implementation on how to send a ""command\r" each N milliseconds to a devices connected to, in this case, "/dev/cu.usbserial-14130" ?

    Best regards and thanks in advance :)


  • Lifetime Qt Champion

    @JorgeMaker said in QtSerialPort weird behaviours on MacOSX:

    May be I do not understand how QtSerialPort works

    It's not just about QtSerialport it is how Qt (or any other event driven framework) works.
    Simple rule: never block Qt event loop with long lasting loops, or even worse using sleep().
    Use a QTimer with appropriate timeout and send what you need in the slot connected to the https://doc.qt.io/qt-5/qtimer.html#timeout slot.


  • Moderators

    @JorgeMaker

    this should get you started, hopefully

    #include <QCoreApplication>
    #include <QSerialPort>
    #include <QTimer>
    int main(int argc, char *argv[])
    {
    
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QCoreApplication::setOrganizationName("MyCompany");
        QCoreApplication::setOrganizationDomain("MyApplication");
        QCoreApplication app(argc, argv);
    
        QSerialPort* serial = new QSerialPort();
        serial->setPortName("/dev/cu.usbserial-14130");
    
        serial->open(QSerialPort::ReadOnly);
        serial->setBaudRate(QSerialPort::Baud9600);
        serial->setDataBits(QSerialPort::Data8);
        serial->setFlowControl(QSerialPort::NoFlowControl);
        serial->setParity(QSerialPort::NoParity);
        serial->setStopBits(QSerialPort::OneStop);
    
    
        QObject::connect(serial, &QSerialPort::errorOccurred, [serial](QSerialPort::SerialPortError error)->void{
            qDebug() << "Serialport had an error" << error << serial->errorString();
        });
    
        QObject::connect(serial, &QSerialPort::readyRead, [serial]()->void{
            QByteArray ba = serial->readAll();
            qDebug() << "Response over serial" << ba.toHex((' '));
        });
    
        QTimer timer;
        QObject::connect(&timer, &QTimer::timeout, serial, [serial]()->void{
            qDebug() << "Attempt to write";
            QString hex = "command\r";
            QByteArray test = QByteArray::fromHex(hex.toLatin1());
            serial->write(test);
        });
    
    
        if (serial->open(QIODevice::ReadWrite))
        {
            timer.start(100); // 10/1000 is inteneger division -> results in 0, 100 = 100 ms
        }
    
        return app.exec();
    }
    
    #include "main.moc"
    


  • Hi @J-Hilk , @jsulm , @SGaist

    Issue solved :) It works perfectly, The solution proposed by @J-Hilk worked perfectly ... finally data on the screen after a couple of afternoons hitting my head against the screen.

    DS1Z_QuickPrint1.png

    I also implemented a solution based on the use of QTimer as @jsulm suggested. I copy it here just in case It can help anyone else having the same issue/misunderstanding.

    • main.cpp:
    #include <QCoreApplication>
    #include "mytimer.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        // Create MyTimer instance
        // QTimer object will be created in the MyTimer constructor
        MyTimer timer;
    
        return a.exec();
    }
    
    • timer.h
    #ifndef MYTIMER_H
     #define MYTIMER_H
    
    #include <QTimer>
    #include <QtSerialPort/QSerialPort>
    
      class MyTimer : public QObject
      {
          Q_OBJECT
      public:
          MyTimer();
          QTimer *timer;
          QSerialPort* serialPort;
    
    
      public slots:
          void MyTimerSlot();
      };
    
      #endif // MYTIME
    
    • timer.cpp
    #include "mytimer.h"
    #include <QDebug>
    MyTimer::MyTimer()
    {
        // create a timer
        timer = new QTimer(this);
        serialPort = new QSerialPort(this);
    
         serialPort->setPortName("/dev/cu.usbserial-14130");
    
         serialPort->setBaudRate(QSerialPort::Baud9600);
         serialPort->setDataBits(QSerialPort::Data8);
         serialPort->setFlowControl(QSerialPort::NoFlowControl);
         serialPort->setParity(QSerialPort::NoParity);
         serialPort->setStopBits(QSerialPort::OneStop);
    
         if (!serialPort->open(QIODevice::ReadWrite)){
             qDebug("Failed to open Serial Port") ;
             qDebug() << "Port Name : " << serialPort->portName();
             qDebug() << "Serial port eror: " << serialPort->errorString() ;
         }
        // setup signal and slot
        connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot()));
        // msec
        timer->start(100);
    }
    
    void MyTimer::MyTimerSlot()
    {
        qDebug() << "One Loop...";
        if (serialPort->isWritable()){
            serialPort->write("Command");
            qDebug() << "Sending data";
        }
        else{
            qDebug() << "I can not write ";
        }
    }
    
    

    Thanks again for your help ;))


Log in to reply