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. Reconnecting to another port in QSerialPort
Forum Updated to NodeBB v4.3 + New Features

Reconnecting to another port in QSerialPort

Scheduled Pinned Locked Moved Unsolved General and Desktop
1 Posts 1 Posters 322 Views 1 Watching
  • 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.
  • Dummie1138D Offline
    Dummie1138D Offline
    Dummie1138
    wrote on last edited by
    #1

    Hi. I have two devices connected to my PC. In my code, I keep a connection open to only one of them. I have the following code, which is meant to change the port connection when called.

    //Function to change the connected port
    void DeviceConnection::changePort(QString name){
        toSendQueue.clear();
        qDebug() << "changing port to " << name;
        serialPort_Controller->close(); //This is triggering a handleError::ResourceError. Find out how the rest of the code overrides it.
        connectingTimer->stop();
        emit lostConnection(Reconnect);
        setPort(name);
        sendConnectionVerification();
    }
    
    //Configure port
    void DeviceConnection::setPort(QString port){
        serialPort_Controller->setPortName(port);     //My computer
        serialPort_Controller->setBaudRate(115200);
        serialPort_Controller->setDataBits(QSerialPort::Data8);
        serialPort_Controller->setParity(QSerialPort::NoParity);
        serialPort_Controller->setStopBits(QSerialPort::OneStop);
        serialPort_Controller->setFlowControl(QSerialPort::SoftwareControl);
    }
    
    //Send connection verification
    void DeviceConnection::sendConnectionVerification(){
        qDebug() << serialPort_Controller->isDataTerminalReady();
        if(serialPort_Controller->open(QIODevice::ReadWrite)){
            qDebug() << "Connected";
            QByteArray validationMessage = "TOARM_VALCN_00000000_4C";
            validationMessage.append('\r');
            validationMessage.append('\n');
            serialPort_Controller->clear(QSerialPort::Output);
            serialPort_Controller->write(validationMessage);
            qDebug() << "Sending message: " + validationMessage;
        }
        else{
            qDebug() << "Connection error";
        }
    }
    

    My issues are:
    1: When closing and reopening the port, I encounter a QSerialPort error. Specifically, a Resource Error. My understanding is that this is called when a device is unexpectedly removed from the system, which is why I'm confused why this would be called when I call close() on the port.

    2: It takes a bit of time for the port to fully switch. When changing to, say, COM Port 2, and sending a message through the QSerialPort (which is done pretty much instantly after the ports are switched), I receive a message back from COM Port 1 before I receive messages back from COM Port 2. Personally, I'm looking for something that can provide a signal back to Qt that the port has fully switched from COM Port 1 to COM Port 2 that isn't blocking if possible, but if there are better ways of implementing this, I am all ears.

    Please let me know if more information is required.

    The full code, if it helps:
    deviceconnection.h

    #ifndef DEVICECONNECTION_H
    #define DEVICECONNECTION_H
    
    #include <QObject>
    #include <QQueue>
    #include <QElapsedTimer>
    #include <QThread>
    
    #include <QSerialPort>
    #include <QSerialPortInfo>
    
    class QTimer;
    class QSerialPort;
    class QSerialPortInfo;
    
    #include "controller.h"
    
    class DeviceConnection : public QObject
    {
        Q_OBJECT
    public:
        DeviceConnection();
        ~DeviceConnection();
    
        QString portName;  //Internal holder of current port name
        QSerialPortInfo portInfo;
    
        struct PortList{
            QString name; //holder of connectable ports
            QSerialPortInfo info;
            QList<QString> deviceSpec;
        };
        QList<PortList> portList;
    
        QSerialPort* serialPort_Controller; //Need more research into this
    
        void addToQueue(QString message);
        void removeOneFromQueue();
        void closeConnection();
        bool isQueueEmpty();
        void emptyQueue();
        enum DisconMode{Disconnect, Reconnect}; //Reconnect is for reconnecting to another USB port
    
    signals:
    
        //message recieved, sent to get processed
        void receivedMessage(QString);
    
        //connection is lost, disable all relevant sections of the program and reset the info on main
        void lostConnection(int);
    
    public slots:
        void serialReady();
        void sendFromQueue();
        void connectDevice();
        void restartConnection();
        void finishConnectAttempts(); //upon a valid connecion occuring it waits to finish the current connection attempts
        void changePort(QString); //change the connected port
    
    private slots:
        void handleError(QSerialPort::SerialPortError error);
        void stopConnectAttempts(); //After current connection attempts finish, stop connecting
    
    private:
        bool alreadySetup;
    
        QString incomingRemainder;
        QStringList messagesList;
        void sendConnectionVerification();    //Sends VALCN
    
        QTimer* sendingTimer;
        QTimer* connectingTimer;
        QTimer* checkerTimer;
        QQueue<QString> toSendQueue;
        int waitingForResponse;
        void setPort(QString); //Configures port for sending
        int sendingAttempts;    //Lists the amount of attempts at sending a single message. Currently, tops out at 10
    
        QString calculateChecksum(QString message);
    
    };
    
    #endif // DEVICECONNECTION_H
    
    

    deviceconnection.cpp

    #include "deviceconnection.h"
    #include <QtSerialPort/QSerialPort>
    #include <QtSerialPort/QSerialPortInfo>
    #include <QTimer>
    #include <QTime>
    #include <QFile>
    #include <QDebug>
    #include <QWidget>
    
    
    #include "statusbar.h"
    #include "controller.h"
    
    int numPortsAvailable;
    QList<QSerialPortInfo> portsAvailable = QSerialPortInfo::availablePorts();
    int currentPosInList;
    //setting up the connections to start the device connection p1ocedures
    DeviceConnection::DeviceConnection(){
        currentPosInList = 0;
        connectingTimer = new QTimer(this);
        connectingTimer->start(1000);
        serialPort_Controller = new QSerialPort(this);
        connectDevice();
        connect(connectingTimer, SIGNAL(timeout()), this, SLOT(connectDevice()));
        connect(serialPort_Controller, SIGNAL(readyRead()), this, SLOT(serialReady()));
        sendingTimer = new QTimer(this);
        connect(sendingTimer, SIGNAL(timeout()), this, SLOT(sendFromQueue()));
        sendingTimer->start(50);
        connect(serialPort_Controller, SIGNAL(error(QSerialPort::SerialPortError)), this,
                SLOT(handleError(QSerialPort::SerialPortError)));
        //sendingAttempts = 0;
    }
    
    //attempts a connection with the next device in the list of available ports
    void DeviceConnection::connectDevice(){
        serialPort_Controller->close();
        portsAvailable = QSerialPortInfo::availablePorts();
        qDebug() << "connecting";
        qDebug() << portsAvailable.count();
        QSerialPortInfo port;
        if (currentPosInList < portsAvailable.count()){
            portName = portsAvailable.at(currentPosInList).portName();
            qDebug() << portName;
            serialPort_Controller->setPortName(portName);     //My computer
            serialPort_Controller->setBaudRate(115200);
            serialPort_Controller->setDataBits(QSerialPort::Data8);
            serialPort_Controller->setParity(QSerialPort::NoParity);
            serialPort_Controller->setStopBits(QSerialPort::OneStop);
            serialPort_Controller->setFlowControl(QSerialPort::SoftwareControl);
            currentPosInList++;
            //sendConnectionVerification();
            if(serialPort_Controller->open(QIODevice::ReadWrite)){
                qDebug() << "Connected";
                QByteArray validationMessage = "TOARM_VALCN_00000000_4C";
                validationMessage.append('\r');
                validationMessage.append('\n');
                serialPort_Controller->clear(QSerialPort::Output);
                serialPort_Controller->write(validationMessage);
                qDebug() << "Sending message: " + validationMessage;
            }
            else{
                qDebug() << "Connection error";
            }
            //FIXIT Arduino only returns information when in debug mode
            if (!serialPort_Controller->isOpen()){
                qDebug() << "Cannot open controller serial port";
            }
        }
        else{
            currentPosInList = 0;
        }
    }
    
    //Configure port
    void DeviceConnection::setPort(QString port){
        serialPort_Controller->setPortName(port);     //My computer
        serialPort_Controller->setBaudRate(115200);
        serialPort_Controller->setDataBits(QSerialPort::Data8);
        serialPort_Controller->setParity(QSerialPort::NoParity);
        serialPort_Controller->setStopBits(QSerialPort::OneStop);
        serialPort_Controller->setFlowControl(QSerialPort::SoftwareControl);
    }
    
    //Send connection verification
    void DeviceConnection::sendConnectionVerification(){
        qDebug() << serialPort_Controller->isDataTerminalReady();
        if(serialPort_Controller->open(QIODevice::ReadWrite)){
            qDebug() << "Connected";
            QByteArray validationMessage = "TOARM_VALCN_00000000_4C";
            validationMessage.append('\r');
            validationMessage.append('\n');
            serialPort_Controller->clear(QSerialPort::Output);
            serialPort_Controller->write(validationMessage);
            qDebug() << "Sending message: " + validationMessage;
        }
        else{
            qDebug() << "Connection error";
        }
    }
    
    //upon a valid connecion occuring it finishes up the connection attempts
    void DeviceConnection::finishConnectAttempts(){
        int delayCount = portsAvailable.count() * connectingTimer->interval() + 500;  //How much more time does the connectingTimer need to run before we turn it off
        //NOTE There is no significance to the +500, just as a buffer
        if (delayCount < 1){
            delayCount = 1;
        }
        QTimer::singleShot(delayCount, this, &DeviceConnection::stopConnectAttempts);
        PortList portListBuf;
        portListBuf.name = portsAvailable.at(currentPosInList - 1).portName();
        portListBuf.info = portsAvailable.at(currentPosInList - 1);
        portList.append(portListBuf);
        if(serialPort_Controller->open(QIODevice::ReadWrite)){
            qDebug() << "connected with device";
        }
    }
    
    //After current connection attempts finish, stop connecting
    void DeviceConnection::stopConnectAttempts(){
        qDebug() << "stopping connect attempts";
        connectingTimer->stop();
        waitingForResponse = 0;
    }
    
    DeviceConnection::~DeviceConnection(){
        serialPort_Controller->close();
        delete serialPort_Controller;
    }
    
    //closes connection to device & purges records of ports
    void DeviceConnection::closeConnection(){
        serialPort_Controller->close();
        portList.clear();
    }
    
    //received messages get processed here into the correct format
    void DeviceConnection::serialReady(){
        QString allReceived = QString(serialPort_Controller->readAll());
    
        qDebug() << "recieved messages: " << allReceived;
        allReceived = incomingRemainder + allReceived;
    
        QStringList fullMessages = allReceived.split("\r\n");
        incomingRemainder = fullMessages.last();
        fullMessages.removeLast();
    
        foreach (QString message, fullMessages) {
            //qDebug() << message; For seeing the message on debugs.
            emit receivedMessage(message);
        }
    }
    
    //adds the massage to the back of the queue
    void DeviceConnection::addToQueue(QString message){
        serialPort_Controller->thread();
        qDebug() << "Pushing " + message + " back";
        toSendQueue.push_back(message.toUtf8());
    }
    
    //removes a message from the queue, triggered when a returning message is received.
    void DeviceConnection::removeOneFromQueue(){
        //qDebug() << "removeOneFromQueue";
        sendingAttempts = 0;
        waitingForResponse = 0;
        if (toSendQueue.count() != 0) {
            toSendQueue.pop_front();
        }
    }
    
    //sends the message at the front of the queue to the device
    //this is tied to a qtimer that triggers once every 50ms
    void DeviceConnection::sendFromQueue(){
        if (waitingForResponse) //waitingForResponse is a value decrementing from 20.
            waitingForResponse--;
        else {
            if (toSendQueue.count() > 0) {
                sendingAttempts++;
                qDebug() << "sending attempt: " << sendingAttempts;
                // don't continue sending if we are getting no response after sending more than 10 times
                if (sendingAttempts > 10) {
                    // log the error
                    qDebug() << "Attempting to send queued message to main board failed:" << toSendQueue.front();
                    removeOneFromQueue();
                } else {
                    // send the item at the top of the queue
                    serialPort_Controller->write(toSendQueue.front().toUtf8());
                    qDebug() << "Sending message:" << toSendQueue.front();
                    waitingForResponse = 20;
                }
            }
        }
    }
    
    //returns true if the queue is empty, false if not
    bool DeviceConnection::isQueueEmpty(){
    //    if (toSendQueue.count() > 0){ //Purge by 08/06/2023.
    //        return false;
    //    }
    //    else{
    //        return true;
    //    }
        return toSendQueue.isEmpty();
    }
    
    //empties the queue
    void DeviceConnection::emptyQueue(){
        toSendQueue.clear();
    }
    
    //ends the connection to the device and restarts the connection procedure with the next port
    void DeviceConnection::restartConnection(){
        closeConnection();
        qDebug() << "restartConnection";
        connectingTimer->start(3000);
        emit lostConnection(Disconnect);
    }
    
    //informs the program that the connection was lost and restarts the connection procedure
    void DeviceConnection::handleError(QSerialPort::SerialPortError error){
        if (error == QSerialPort::ResourceError) {
            qDebug() << "handleError";
            //serialPort_Controller->clearError();
            emptyQueue();
            restartConnection();
        }
    }
    
    //Function to change the connected port
    void DeviceConnection::changePort(QString name){
        toSendQueue.clear();
        qDebug() << "changing port to " << name;
        serialPort_Controller->close(); //This is triggering a handleError::ResourceError. Find out how the rest of the code overrides it.
        connectingTimer->stop();
        emit lostConnection(Reconnect);
        setPort(name);
        sendConnectionVerification();
    }
    
    
    1 Reply Last reply
    0

    • Login

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