Qt Serial Port Write and Read Issues
-
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_OBJECTpublic:
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();
} - @brief Destructor for the thread.
-
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_OBJECTpublic:
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();
}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).
- @brief Destructor for the thread.
-
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).
@Christian-Ehrlicher What exactly should I do in my code to not access QIODevice from outside the thread?
-
@Christian-Ehrlicher What exactly should I do in my code to not access QIODevice from outside the thread?
@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.