Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Qt Serial Port Write and Read Issues
QtWS25 Last Chance

Qt Serial Port Write and Read Issues

Scheduled Pinned Locked Moved Unsolved General and Desktop
4 Posts 3 Posters 413 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mahis
    wrote on last edited by
    #1

    I have two hardware communicating over serial ports. The two hardware have their own instance of Serial Port Class. I'm having trouble with WaitForLineAvailable() returning an empty string. And sometimes Segmentation error on WaitForBytesWriten() line of the Write() method. These issues are random. I don't understand what might cause this kind of behaviour. Looking for some suggestions here.

    SerialPortClass.cpp
    bool SerialPortClass::OpenCommunicationsPort(QString port, QString params)
    {
    QMutexLocker locker(&mutexOpenCommunicationsPort);

    if(SerialPort != nullptr)
    {
        if(SerialPort->isOpen()) SerialPort->close();
    
        SerialPort->setPortName(port);
    }
    else
        SerialPort = new QSerialPort(port);
    
    // split up params "230400 N 8 1"
    QStringList paramsList = params.split(" ");
    
    if(paramsList.size() == 4)
    {
        SerialPort->setBaudRate(paramsList.value(0).toInt());
        if (paramsList.value(1) == "N") { SerialPort->setParity(QSerialPort::NoParity); }
        if (paramsList.value(1) == "E") { SerialPort->setParity(QSerialPort::EvenParity); }
        if (paramsList.value(1) == "O") { SerialPort->setParity(QSerialPort::OddParity); }
        if (paramsList.value(2) == "8") { SerialPort->setDataBits(QSerialPort::Data8); }
        if (paramsList.value(2) == "7") { SerialPort->setDataBits(QSerialPort::Data7); }
        if (paramsList.value(3) == "1") { SerialPort->setStopBits(QSerialPort::OneStop); }
        if (paramsList.value(3) == "2") { SerialPort->setStopBits(QSerialPort::TwoStop); }
        if (mydebug == 1) { qDebug() << "Port Opened on " << port << " " << params; }
        return SerialPort->open(QIODevice::ReadWrite);
    }
    
    return false;
    

    }

    bool SerialPortClass::Write(QString Tx, bool bDelayPerChar)
    {
    QMutexLocker locker(&mutexSerialPort);

    QCoreApplication::processEvents();
    
    bool status = true;
    
    QByteArray baTxMsg = Tx.QString::toUtf8();
    
    foreach(char c, baTxMsg)
        {
            SerialPort->write(&c, 1);           
            SerialPort->waitForBytesWritten(200);
            if(bDelayPerChar) { QThread::msleep(5); }
        }
    
    qDebug() << "Tx:" << Tx;
    
    return status;
    

    }

    bool SerialPortClass::WaitForLineAvailable(QString& Rx, int iWaitTime, QString terminator)
    {
    QMutexLocker locker(&mutexSerialPort);

    Rx = "";
    int delay = 500;
    int retries = (iWaitTime *1000)/delay;
    /*int retries = 30;
    char buffer[128];*/
    
    while ( (!Rx.contains(terminator) ) && (retries > 0) )
    {            
        if (SerialPort->waitForReadyRead(delay))
            //if (SerialPort->waitForReadyRead(5000))
        {
            QByteArray responseData = SerialPort->readAll();
    
            while (SerialPort->waitForReadyRead(50))
            {
                responseData += SerialPort->readAll();
            }
            Rx = QString::fromUtf8(responseData);
        }
        retries--;
    }
    qDebug() << "Rx:" << Rx << "" << retries;
    
    if (retries > 0)
        return true;
    
    return false;
    

    }
    HardwareThread1.h

    #ifndef HARDWARETHREAD1_H
    #define HARDWARETHREAD1_H

    #include <QObject>
    #include <QThread>
    #include <QMessageBox>

    #include "serialportclass.h"

    class HardwareThread1 : public QThread
    {
    Q_OBJECT

    public:
    explicit HardwareThread1 ( QObject *parent = nullptr );
    ~HardwareThread1 ();

    void go();
    void run();
    void stop();
    void CloseSerialComms();
    

    private:
    SerialPortClass serialPort;

    bool    GetStopControllerTestFlag();
    bool    GetIsShutdownSequenceRunningFlag();
    bool    EstablishComms();
    bool    m_bCommsEstablished = false;    
    int     m_iPSUStatus;
    int     m_iIEEEAddress;
    QString m_sComPort = "";
    QString m_sPortParams = "";
    QString m_sRxMsg = "";    
    

    signals:
    void signalDisplayPowered(int status, QString sHardware);
    void signalDisplayErrorMessage(QString sMessageBoxWindowTitle, QString sMessageBoxWindowText);
    void signalUserMessageBox(QString sText1, QString sText2, int iIcon, int iBtn = QMessageBox::Close);
    void signalToRunShutdownSequence();
    };

    #endif // HARDWARETHREAD1_H

    HardwareThread1.cpp
    #include "hardwarethread1.h"
    #include "globals.h"
    #include "programsettingsclass.h"

    #include <QDebug>
    #include <QMessageBox>

    //extern variables//
    extern ProgramSettingsClass *pProgramSettingsClassObj;

    HardwareThread1::HardwareThread1(QObject *parent) :
    QThread(parent)
    {
    bool bStatus;
    m_bCommsEstablished = false;

    bStatus = EstablishComms();   
    

    }

    /*!

    • @brief Destructor for the thread.
      */
      HardwareThread1::~HardwareThread1()
      {
      this->terminate();
      this->wait();
      }

    /*!

    • @brief Attempt to open serial port.

    • @param in port COM port number to open.

    • @param in params Serial port parameters.

    • @return Port open successfully
      */
      bool HardwareThread1::EstablishComms()
      {
      if (m_bCommsEstablished == false)
      {
      this->m_sComPort = pProgramSettingsClassObj->GetCOMPortSetting("ARDUINO_UNO_HW"); // Get port number for this PSU from settings file list.

           this->m_sPortParams = pProgramSettingsClassObj->GetCOMPortParameters("ARDUINO_UNO_HW");
      
           if (serialPort.OpenCommunicationsPort(this->m_sComPort, m_sPortParams)) // Attempt to open COM port.
               {
                   m_bCommsEstablished =  true;
               }
           else // Could not open COM port.
               {                    
                   m_bCommsEstablished =  false;
               }
       }
      
       return m_bCommsEstablished;
      

    }

    /*!

    • @brief Close Serial Port Comms
      */
      void HardwareThread1::CloseSerialComms()
      {
      serialPort.Close();
      }

    void HardwareThread1::go()
    {
    this->start();
    }

    /*!

    • @brief Retrieve 'change in Stop Controller Flag'.
    • @return Flag
      */
      bool HardwareThread1::GetStopControllerTestFlag()
      {
      bool bGet;
      mutexGlobalVariable.lock();
      bGet = g_bStopControllerTestFlag;
      mutexGlobalVariable.unlock();
      return bGet;
      }

    bool HardwareThread1::GetIsShutdownSequenceRunningFlag()
    {
    bool bGet;
    mutexShutdownSequenceVariable.lock();
    bGet = g_bIsShutdownSequenceRunning;
    mutexShutdownSequenceVariable.unlock();
    return bGet;
    }

    /*!

    • @brief Timed PSU monitoring.

    • Checks and sets voltage and current.
      */
      void HardwareThread1::run()
      {
      while(true)
      {
      bool status = serialPort.IsOpen();

       if ((status) && (!g_bManualValveBeingOperatedFlag) && (!GetStopControllerTestFlag()))
      {           
          serialPort.ClearReadBuffer();
                    
          serialPort.Write("Clear\n",true);
                    
          serialPort.Write("HOOD?\n",true);
                    
          serialPort.WaitForLineAvailable(m_sRxMsg); // Wait for response.
                     
          m_sRxMsg.remove("\r");
          m_sRxMsg.remove("\n");
          int iHoodStatus = m_sRxMsg.toInt();
      
          //qDebug() << "HOOD_STAT=" << m_sRxMsg << "\n";
      
          if((iHoodStatus == 0) && (m_sRxMsg != "")) //The hood is open, hence cut power supply and vent all pressure //
          {
              //Set the global flag//
              mutexGlobalVariable.lock();
              g_bStopControllerTestFlag = true;
              mutexGlobalVariable.unlock();
      
              //qDebug() << "HOOD_STAT=" << iHoodStatus << "\n";
              emit signalDisplayErrorMessage("Hood Status", "Hood Open Detected!\n \nThe Program will Quit Now.");
               
              CloseArduinoSerialComms();                            
          }
      }
      QThread::msleep(500);
      

      }
      }

    void HardwareThread1::stop()
    {
    this->terminate();
    this->wait();
    }

    Christian EhrlicherC 1 Reply Last reply
    0
    • M Mahis

      I have two hardware communicating over serial ports. The two hardware have their own instance of Serial Port Class. I'm having trouble with WaitForLineAvailable() returning an empty string. And sometimes Segmentation error on WaitForBytesWriten() line of the Write() method. These issues are random. I don't understand what might cause this kind of behaviour. Looking for some suggestions here.

      SerialPortClass.cpp
      bool SerialPortClass::OpenCommunicationsPort(QString port, QString params)
      {
      QMutexLocker locker(&mutexOpenCommunicationsPort);

      if(SerialPort != nullptr)
      {
          if(SerialPort->isOpen()) SerialPort->close();
      
          SerialPort->setPortName(port);
      }
      else
          SerialPort = new QSerialPort(port);
      
      // split up params "230400 N 8 1"
      QStringList paramsList = params.split(" ");
      
      if(paramsList.size() == 4)
      {
          SerialPort->setBaudRate(paramsList.value(0).toInt());
          if (paramsList.value(1) == "N") { SerialPort->setParity(QSerialPort::NoParity); }
          if (paramsList.value(1) == "E") { SerialPort->setParity(QSerialPort::EvenParity); }
          if (paramsList.value(1) == "O") { SerialPort->setParity(QSerialPort::OddParity); }
          if (paramsList.value(2) == "8") { SerialPort->setDataBits(QSerialPort::Data8); }
          if (paramsList.value(2) == "7") { SerialPort->setDataBits(QSerialPort::Data7); }
          if (paramsList.value(3) == "1") { SerialPort->setStopBits(QSerialPort::OneStop); }
          if (paramsList.value(3) == "2") { SerialPort->setStopBits(QSerialPort::TwoStop); }
          if (mydebug == 1) { qDebug() << "Port Opened on " << port << " " << params; }
          return SerialPort->open(QIODevice::ReadWrite);
      }
      
      return false;
      

      }

      bool SerialPortClass::Write(QString Tx, bool bDelayPerChar)
      {
      QMutexLocker locker(&mutexSerialPort);

      QCoreApplication::processEvents();
      
      bool status = true;
      
      QByteArray baTxMsg = Tx.QString::toUtf8();
      
      foreach(char c, baTxMsg)
          {
              SerialPort->write(&c, 1);           
              SerialPort->waitForBytesWritten(200);
              if(bDelayPerChar) { QThread::msleep(5); }
          }
      
      qDebug() << "Tx:" << Tx;
      
      return status;
      

      }

      bool SerialPortClass::WaitForLineAvailable(QString& Rx, int iWaitTime, QString terminator)
      {
      QMutexLocker locker(&mutexSerialPort);

      Rx = "";
      int delay = 500;
      int retries = (iWaitTime *1000)/delay;
      /*int retries = 30;
      char buffer[128];*/
      
      while ( (!Rx.contains(terminator) ) && (retries > 0) )
      {            
          if (SerialPort->waitForReadyRead(delay))
              //if (SerialPort->waitForReadyRead(5000))
          {
              QByteArray responseData = SerialPort->readAll();
      
              while (SerialPort->waitForReadyRead(50))
              {
                  responseData += SerialPort->readAll();
              }
              Rx = QString::fromUtf8(responseData);
          }
          retries--;
      }
      qDebug() << "Rx:" << Rx << "" << retries;
      
      if (retries > 0)
          return true;
      
      return false;
      

      }
      HardwareThread1.h

      #ifndef HARDWARETHREAD1_H
      #define HARDWARETHREAD1_H

      #include <QObject>
      #include <QThread>
      #include <QMessageBox>

      #include "serialportclass.h"

      class HardwareThread1 : public QThread
      {
      Q_OBJECT

      public:
      explicit HardwareThread1 ( QObject *parent = nullptr );
      ~HardwareThread1 ();

      void go();
      void run();
      void stop();
      void CloseSerialComms();
      

      private:
      SerialPortClass serialPort;

      bool    GetStopControllerTestFlag();
      bool    GetIsShutdownSequenceRunningFlag();
      bool    EstablishComms();
      bool    m_bCommsEstablished = false;    
      int     m_iPSUStatus;
      int     m_iIEEEAddress;
      QString m_sComPort = "";
      QString m_sPortParams = "";
      QString m_sRxMsg = "";    
      

      signals:
      void signalDisplayPowered(int status, QString sHardware);
      void signalDisplayErrorMessage(QString sMessageBoxWindowTitle, QString sMessageBoxWindowText);
      void signalUserMessageBox(QString sText1, QString sText2, int iIcon, int iBtn = QMessageBox::Close);
      void signalToRunShutdownSequence();
      };

      #endif // HARDWARETHREAD1_H

      HardwareThread1.cpp
      #include "hardwarethread1.h"
      #include "globals.h"
      #include "programsettingsclass.h"

      #include <QDebug>
      #include <QMessageBox>

      //extern variables//
      extern ProgramSettingsClass *pProgramSettingsClassObj;

      HardwareThread1::HardwareThread1(QObject *parent) :
      QThread(parent)
      {
      bool bStatus;
      m_bCommsEstablished = false;

      bStatus = EstablishComms();   
      

      }

      /*!

      • @brief Destructor for the thread.
        */
        HardwareThread1::~HardwareThread1()
        {
        this->terminate();
        this->wait();
        }

      /*!

      • @brief Attempt to open serial port.

      • @param in port COM port number to open.

      • @param in params Serial port parameters.

      • @return Port open successfully
        */
        bool HardwareThread1::EstablishComms()
        {
        if (m_bCommsEstablished == false)
        {
        this->m_sComPort = pProgramSettingsClassObj->GetCOMPortSetting("ARDUINO_UNO_HW"); // Get port number for this PSU from settings file list.

             this->m_sPortParams = pProgramSettingsClassObj->GetCOMPortParameters("ARDUINO_UNO_HW");
        
             if (serialPort.OpenCommunicationsPort(this->m_sComPort, m_sPortParams)) // Attempt to open COM port.
                 {
                     m_bCommsEstablished =  true;
                 }
             else // Could not open COM port.
                 {                    
                     m_bCommsEstablished =  false;
                 }
         }
        
         return m_bCommsEstablished;
        

      }

      /*!

      • @brief Close Serial Port Comms
        */
        void HardwareThread1::CloseSerialComms()
        {
        serialPort.Close();
        }

      void HardwareThread1::go()
      {
      this->start();
      }

      /*!

      • @brief Retrieve 'change in Stop Controller Flag'.
      • @return Flag
        */
        bool HardwareThread1::GetStopControllerTestFlag()
        {
        bool bGet;
        mutexGlobalVariable.lock();
        bGet = g_bStopControllerTestFlag;
        mutexGlobalVariable.unlock();
        return bGet;
        }

      bool HardwareThread1::GetIsShutdownSequenceRunningFlag()
      {
      bool bGet;
      mutexShutdownSequenceVariable.lock();
      bGet = g_bIsShutdownSequenceRunning;
      mutexShutdownSequenceVariable.unlock();
      return bGet;
      }

      /*!

      • @brief Timed PSU monitoring.

      • Checks and sets voltage and current.
        */
        void HardwareThread1::run()
        {
        while(true)
        {
        bool status = serialPort.IsOpen();

         if ((status) && (!g_bManualValveBeingOperatedFlag) && (!GetStopControllerTestFlag()))
        {           
            serialPort.ClearReadBuffer();
                      
            serialPort.Write("Clear\n",true);
                      
            serialPort.Write("HOOD?\n",true);
                      
            serialPort.WaitForLineAvailable(m_sRxMsg); // Wait for response.
                       
            m_sRxMsg.remove("\r");
            m_sRxMsg.remove("\n");
            int iHoodStatus = m_sRxMsg.toInt();
        
            //qDebug() << "HOOD_STAT=" << m_sRxMsg << "\n";
        
            if((iHoodStatus == 0) && (m_sRxMsg != "")) //The hood is open, hence cut power supply and vent all pressure //
            {
                //Set the global flag//
                mutexGlobalVariable.lock();
                g_bStopControllerTestFlag = true;
                mutexGlobalVariable.unlock();
        
                //qDebug() << "HOOD_STAT=" << iHoodStatus << "\n";
                emit signalDisplayErrorMessage("Hood Status", "Hood Open Detected!\n \nThe Program will Quit Now.");
                 
                CloseArduinoSerialComms();                            
            }
        }
        QThread::msleep(500);
        

        }
        }

      void HardwareThread1::stop()
      {
      this->terminate();
      this->wait();
      }

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      As always. You must not access an Qiodevice from outside the thread it was created. In most cases it's not needed. And there is also no need for using the waitFoo() functions. Use proper signals and slot (readyRead() signal).

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      M 1 Reply Last reply
      4
      • Christian EhrlicherC Christian Ehrlicher

        As always. You must not access an Qiodevice from outside the thread it was created. In most cases it's not needed. And there is also no need for using the waitFoo() functions. Use proper signals and slot (readyRead() signal).

        M Offline
        M Offline
        Mahis
        wrote on last edited by
        #3

        @Christian-Ehrlicher What exactly should I do in my code to not access QIODevice from outside the thread?

        jsulmJ 1 Reply Last reply
        0
        • M Mahis

          @Christian-Ehrlicher What exactly should I do in my code to not access QIODevice from outside the thread?

          jsulmJ Offline
          jsulmJ Offline
          jsulm
          Lifetime Qt Champion
          wrote on last edited by
          #4

          @Mahis said in Qt Serial Port Write and Read Issues:

          What exactly should I do in my code to not access QIODevice from outside the thread?

          Don't use threads: Qt is an asynchronous framework, most of the time there is no need to use threads.

          https://forum.qt.io/topic/113070/qt-code-of-conduct

          1 Reply Last reply
          3

          • Login

          • Login or register to search.
          • First post
            Last post
          0
          • Categories
          • Recent
          • Tags
          • Popular
          • Users
          • Groups
          • Search
          • Get Qt Extensions
          • Unsolved