QSerialPort not reading from Arduino
-
wrote on 31 May 2023, 14:58 last edited by
Question as posted on Arduino forum: https://forum.arduino.cc/t/serial-println-not-sent-to-qt-application/1133089
Hi. I have the following code for a QSerialPort, meant to send and receive information from an Arduino.
//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++; if(serialPort_Controller->open(QIODevice::ReadWrite)) { qDebug() << "Connected"; int i = serialPort_Controller->write("TOPIC_VALCN_00000000_48\r\n"); //"TOPIC_VALCN_00000000_48\r\n" qDebug() << i; qDebug() << "TOPIC_VALCN_00000000_48\r\n"; } else { qDebug() << "Connection error"; } if (!serialPort_Controller->isOpen()) { qDebug() << "Cannot open controller serial port"; } } else { currentPosInList = 0; } } //received messages get processed here into the correct format void DeviceConnection::serialReady() { QString allReceived = QString(serialPort_Controller->readAll()); // if (SystemSettings::get()->getValue(PROTOCOL_DEBUG_MODE) == "1") // backSerialPort->write(allReceived.toUtf8()); 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); } }
The issue is simple: When I send a message through to the Arduino code from the Arduino serial monitor, the code does successfully process the message and return it on the Arduino serial monitor.
However, when sending messages through via QSerialPort, the code no longer processes the message and returns it to the Qt application at baud rates higher than 9600.
Things I've tried:
Shortening the message written:
serialPort_Controller->write("T\n");
This did not work.
Changing the data bits & parity, that did not work.
Running the code in debug mode and adding a breakpoint at this line:
serialPort_Controller->write("TOPIC_VALCN_00000000_48\r\n");
This worked, somehow. I have yet to determine why.
The part of the arduino code that directly reads and writes:
void setup() { // put your setup code here, to run once: Serial.begin(115200); // usb port serial pinMode(LED_BUILTIN, OUTPUT); } void loop() { //Collect data. bool readyRead = true; int i = 0; //Serial.println("Serial available."); if (Serial.available() > 0) { //Serial.println("Serial available."); i = msgReceiver('\n', readyRead); //msgReceiver is for turning the input to a char array. '\n' is end char. The output of msgReceiver is the char array position, so need to ++. digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //This turns an LED on and off. messageArray[27] = '\0'; // terminate the string int msgLength = i + 1; for (i = msgLength; i > 22; i--) { //messageArray[22] is the 2nd char of the checkSum. Should not be ablated. messageArray[i] = '\0'; } bool canSend = true; char checkSumCharacters[2] = {messageArray[i - 1], messageArray[i]}; //Values are ascii code of the base16 numbers. int receivedCheckSum = strtoul(checkSumCharacters, NULL, 16); //Converts the base16 characters to an int. int checkSum = checkSumGenerator(messageArray); for (i = 0; i < sizeof(cmdtarget); i++) { //Length of cmdtarget is 5. cmdtarget[i] = messageArray[i]; } cmdtarget[5] = '\0'; for (i = 0; i < sizeof(input_cmdtype) - 1; i++) { //Length of cmdtype is 5. input_cmdtype[i] = messageArray[i + 6]; } input_cmdtype[5] = '\0'; for (i = 0; i < sizeof(input_cmdparam) - 1; i++) { //Length of cmdparam is 8. input_cmdparam[i] = messageArray[i + 12]; } input_cmdparam[8] = '\0'; //An attempt at a function pointer to be called for rtnProtocols. Pointer used to decrease negative effects of conditionals. stringFunction *rtnpointer[] = { FIRMV_F, VALCN_F, RQSPC_F, CTFTC_F, LIDVM_F, LIDVC_F, PREVC_F, STPWM_F, CHPWM_F, STPER_F, CHPER_F, CHIGH_F, SHIGH_F, MOTST_F, MHIGH_F, MTRTT_F, RCTIC_F, RCTGM_F, RCTGC_F, SAVEA_F, PREST_F, PREND_F, RQPRE_F, CLPRE_F, RQDAT_F, CUPTC_F, LIDTC_F, CURPR_F, CUPTT_F, CDATE_F, CTIME_F, SDATE_F, RDTTM_F, SPROG_F, SLOAD_F, CUFAC_F, LIFAC_F, STFAC_F, MTFAC_F, PPFAC_F, PCOFF_F, CUPFM_F, CUPFC_F, LIDFM_F, LIDFC_F, PREFC_F, FCPWM_F, FCPER_F, FHIGH_F, }; input in = convert(input_cmdtype); //Using the char to enum converter. canSend = errorStack(cmdtarget, receivedCheckSum, checkSum, msgLength, in, canSend); if (canSend) { char verak[6] = "VERAK"; rtnProtocols(verak, input_cmdparam); rtnpointer[in](input_cmdtype, input_cmdparam); } } } //Reads message. int msgReceiver(char endMarker, bool readyRead) { char rc; int ndx = 0; while (readyRead == true) { if (Serial.available() > 0) { rc = Serial.read(); if (rc != endMarker) { messageArray[ndx] = rc; ndx++; } else if (rc == endMarker) { messageArray[ndx] = '\0'; // terminate the string //Serial.println("Received \n."); rc = 0; readyRead = false; } else { readyRead = true; } } } return ndx; } //Function write back to PC void rtnProtocols(char cmdtype[], char cmdparam[]) { char outputString[28] = "TOWST_"; strcat (outputString, cmdtype); strcat (outputString, "_"); strcat (outputString, cmdparam); strcat (outputString, "_"); int outputCheckSum = checkSumGenerator(outputString); char outputCheckSumHex[3] = "00"; itoa (outputCheckSum, outputCheckSumHex, 16); if (outputCheckSum < 16) { //Adds a 0 if CS has fewer than 2 numbers outputCheckSumHex[1] = outputCheckSumHex[0]; outputCheckSumHex[0] = '0'; } outputCheckSumHex[0] = toupper (outputCheckSumHex[0]); outputCheckSumHex[1] = toupper (outputCheckSumHex[1]); strcat (outputString, outputCheckSumHex); //Now displayed in base16 outputString[23] = {'\r'}; //Not displayed in port. This should be happening, I think. outputString[24] = {'\n'}; Serial.write(outputString); }
Please let me know if more information is required.
-
Question as posted on Arduino forum: https://forum.arduino.cc/t/serial-println-not-sent-to-qt-application/1133089
Hi. I have the following code for a QSerialPort, meant to send and receive information from an Arduino.
//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++; if(serialPort_Controller->open(QIODevice::ReadWrite)) { qDebug() << "Connected"; int i = serialPort_Controller->write("TOPIC_VALCN_00000000_48\r\n"); //"TOPIC_VALCN_00000000_48\r\n" qDebug() << i; qDebug() << "TOPIC_VALCN_00000000_48\r\n"; } else { qDebug() << "Connection error"; } if (!serialPort_Controller->isOpen()) { qDebug() << "Cannot open controller serial port"; } } else { currentPosInList = 0; } } //received messages get processed here into the correct format void DeviceConnection::serialReady() { QString allReceived = QString(serialPort_Controller->readAll()); // if (SystemSettings::get()->getValue(PROTOCOL_DEBUG_MODE) == "1") // backSerialPort->write(allReceived.toUtf8()); 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); } }
The issue is simple: When I send a message through to the Arduino code from the Arduino serial monitor, the code does successfully process the message and return it on the Arduino serial monitor.
However, when sending messages through via QSerialPort, the code no longer processes the message and returns it to the Qt application at baud rates higher than 9600.
Things I've tried:
Shortening the message written:
serialPort_Controller->write("T\n");
This did not work.
Changing the data bits & parity, that did not work.
Running the code in debug mode and adding a breakpoint at this line:
serialPort_Controller->write("TOPIC_VALCN_00000000_48\r\n");
This worked, somehow. I have yet to determine why.
The part of the arduino code that directly reads and writes:
void setup() { // put your setup code here, to run once: Serial.begin(115200); // usb port serial pinMode(LED_BUILTIN, OUTPUT); } void loop() { //Collect data. bool readyRead = true; int i = 0; //Serial.println("Serial available."); if (Serial.available() > 0) { //Serial.println("Serial available."); i = msgReceiver('\n', readyRead); //msgReceiver is for turning the input to a char array. '\n' is end char. The output of msgReceiver is the char array position, so need to ++. digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //This turns an LED on and off. messageArray[27] = '\0'; // terminate the string int msgLength = i + 1; for (i = msgLength; i > 22; i--) { //messageArray[22] is the 2nd char of the checkSum. Should not be ablated. messageArray[i] = '\0'; } bool canSend = true; char checkSumCharacters[2] = {messageArray[i - 1], messageArray[i]}; //Values are ascii code of the base16 numbers. int receivedCheckSum = strtoul(checkSumCharacters, NULL, 16); //Converts the base16 characters to an int. int checkSum = checkSumGenerator(messageArray); for (i = 0; i < sizeof(cmdtarget); i++) { //Length of cmdtarget is 5. cmdtarget[i] = messageArray[i]; } cmdtarget[5] = '\0'; for (i = 0; i < sizeof(input_cmdtype) - 1; i++) { //Length of cmdtype is 5. input_cmdtype[i] = messageArray[i + 6]; } input_cmdtype[5] = '\0'; for (i = 0; i < sizeof(input_cmdparam) - 1; i++) { //Length of cmdparam is 8. input_cmdparam[i] = messageArray[i + 12]; } input_cmdparam[8] = '\0'; //An attempt at a function pointer to be called for rtnProtocols. Pointer used to decrease negative effects of conditionals. stringFunction *rtnpointer[] = { FIRMV_F, VALCN_F, RQSPC_F, CTFTC_F, LIDVM_F, LIDVC_F, PREVC_F, STPWM_F, CHPWM_F, STPER_F, CHPER_F, CHIGH_F, SHIGH_F, MOTST_F, MHIGH_F, MTRTT_F, RCTIC_F, RCTGM_F, RCTGC_F, SAVEA_F, PREST_F, PREND_F, RQPRE_F, CLPRE_F, RQDAT_F, CUPTC_F, LIDTC_F, CURPR_F, CUPTT_F, CDATE_F, CTIME_F, SDATE_F, RDTTM_F, SPROG_F, SLOAD_F, CUFAC_F, LIFAC_F, STFAC_F, MTFAC_F, PPFAC_F, PCOFF_F, CUPFM_F, CUPFC_F, LIDFM_F, LIDFC_F, PREFC_F, FCPWM_F, FCPER_F, FHIGH_F, }; input in = convert(input_cmdtype); //Using the char to enum converter. canSend = errorStack(cmdtarget, receivedCheckSum, checkSum, msgLength, in, canSend); if (canSend) { char verak[6] = "VERAK"; rtnProtocols(verak, input_cmdparam); rtnpointer[in](input_cmdtype, input_cmdparam); } } } //Reads message. int msgReceiver(char endMarker, bool readyRead) { char rc; int ndx = 0; while (readyRead == true) { if (Serial.available() > 0) { rc = Serial.read(); if (rc != endMarker) { messageArray[ndx] = rc; ndx++; } else if (rc == endMarker) { messageArray[ndx] = '\0'; // terminate the string //Serial.println("Received \n."); rc = 0; readyRead = false; } else { readyRead = true; } } } return ndx; } //Function write back to PC void rtnProtocols(char cmdtype[], char cmdparam[]) { char outputString[28] = "TOWST_"; strcat (outputString, cmdtype); strcat (outputString, "_"); strcat (outputString, cmdparam); strcat (outputString, "_"); int outputCheckSum = checkSumGenerator(outputString); char outputCheckSumHex[3] = "00"; itoa (outputCheckSum, outputCheckSumHex, 16); if (outputCheckSum < 16) { //Adds a 0 if CS has fewer than 2 numbers outputCheckSumHex[1] = outputCheckSumHex[0]; outputCheckSumHex[0] = '0'; } outputCheckSumHex[0] = toupper (outputCheckSumHex[0]); outputCheckSumHex[1] = toupper (outputCheckSumHex[1]); strcat (outputString, outputCheckSumHex); //Now displayed in base16 outputString[23] = {'\r'}; //Not displayed in port. This should be happening, I think. outputString[24] = {'\n'}; Serial.write(outputString); }
Please let me know if more information is required.
Hi,
Are you sure about the flow control value as well ?
-
wrote on 1 Jun 2023, 08:13 last edited by
@SGaist By flow control value, do you mean the XON/XOFF? My Arduino works on XON/XOFF. I have also tested changing, to no success.
An additional predicament I have discovered is that the Arduino does receive, process, and return data from the Qt application the first time
Recent suggestions on the Arduino forum have suggested that:
When an application opens the Serial port to the Arduino, it causes the Arduino to reset so you need to wait for it to boot up and be ready to receive commands before you send them. For this reason, it is usually best to have your Arduino send a welcome string like "ready" inside of setup() that your application can detect before sending anything else.
I have added a message to the setup function of the Arduino, which is able to be sent every time the Arduino is reconnected to the serial port. However, the original message (which is processed in the loop part) is still unable to be received.
Addendum: the full code for the QSerialPort's file.
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; QSerialPort* serialPort_Controller; //Need more research into this void addToQueue(QString message); void removeOneFromQueue(); void closeConnection(); bool isQueueEmpty(); void emptyQueue(); 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(); 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 private slots: void handleError(QSerialPort::SerialPortError error); void stopConnectAttempts(); //After current connection attempts finish, stop connecting private: bool alreadySetup; QString incomingRemainder; QStringList messagesList; QTimer* sendingTimer; QTimer* connectingTimer; QTimer* checkerTimer; QQueue<QString> toSendQueue; int waitingForResponse; int sendingAttempts; //Lists the amount of attempts at sending a single message. Currently, tops out at 10 QString calculateChecksum(QString message); Controller* controller; }; #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(3000); 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 //This might need modification serialPort_Controller->setBaudRate(115200); serialPort_Controller->setDataBits(QSerialPort::Data8); serialPort_Controller->setParity(QSerialPort::NoParity); serialPort_Controller->setStopBits(QSerialPort::OneStop); serialPort_Controller->setFlowControl(QSerialPort::HardwareControl); currentPosInList++; if(serialPort_Controller->open(QIODevice::ReadWrite)){ qDebug() << "Connected"; QByteArray validationMessage = "TOARM_VALCN_00000000_4C"; validationMessage.append('\r'); validationMessage.append('\n'); serialPort_Controller->write(validationMessage); qDebug() << "Sending message: " + validationMessage; //FIXIT Arduino only returns information when in debug mode } else{ qDebug() << "Connection error"; } if (!serialPort_Controller->isOpen()){ qDebug() << "Cannot open controller serial port"; } } else{ currentPosInList = 0; } } //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); portName = portsAvailable.at(currentPosInList - 1).portName(); portInfo = portsAvailable.at(currentPosInList - 1); 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 void DeviceConnection::closeConnection() { serialPort_Controller->close(); } //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 VERAK 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 void DeviceConnection::sendFromQueue() //this is tied to a qtimer that triggers once every 50ms { 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) { return false; } else { return true; } } //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() { serialPort_Controller->close(); qDebug() << "restartConnection"; connectingTimer->start(3000); //qDebug() << "startedTimer"; emit lostConnection(); } //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"; closeConnection(); emptyQueue(); connectingTimer->start(3000); emit lostConnection(); } }
-
@SGaist By flow control value, do you mean the XON/XOFF? My Arduino works on XON/XOFF. I have also tested changing, to no success.
An additional predicament I have discovered is that the Arduino does receive, process, and return data from the Qt application the first time
Recent suggestions on the Arduino forum have suggested that:
When an application opens the Serial port to the Arduino, it causes the Arduino to reset so you need to wait for it to boot up and be ready to receive commands before you send them. For this reason, it is usually best to have your Arduino send a welcome string like "ready" inside of setup() that your application can detect before sending anything else.
I have added a message to the setup function of the Arduino, which is able to be sent every time the Arduino is reconnected to the serial port. However, the original message (which is processed in the loop part) is still unable to be received.
Addendum: the full code for the QSerialPort's file.
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; QSerialPort* serialPort_Controller; //Need more research into this void addToQueue(QString message); void removeOneFromQueue(); void closeConnection(); bool isQueueEmpty(); void emptyQueue(); 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(); 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 private slots: void handleError(QSerialPort::SerialPortError error); void stopConnectAttempts(); //After current connection attempts finish, stop connecting private: bool alreadySetup; QString incomingRemainder; QStringList messagesList; QTimer* sendingTimer; QTimer* connectingTimer; QTimer* checkerTimer; QQueue<QString> toSendQueue; int waitingForResponse; int sendingAttempts; //Lists the amount of attempts at sending a single message. Currently, tops out at 10 QString calculateChecksum(QString message); Controller* controller; }; #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(3000); 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 //This might need modification serialPort_Controller->setBaudRate(115200); serialPort_Controller->setDataBits(QSerialPort::Data8); serialPort_Controller->setParity(QSerialPort::NoParity); serialPort_Controller->setStopBits(QSerialPort::OneStop); serialPort_Controller->setFlowControl(QSerialPort::HardwareControl); currentPosInList++; if(serialPort_Controller->open(QIODevice::ReadWrite)){ qDebug() << "Connected"; QByteArray validationMessage = "TOARM_VALCN_00000000_4C"; validationMessage.append('\r'); validationMessage.append('\n'); serialPort_Controller->write(validationMessage); qDebug() << "Sending message: " + validationMessage; //FIXIT Arduino only returns information when in debug mode } else{ qDebug() << "Connection error"; } if (!serialPort_Controller->isOpen()){ qDebug() << "Cannot open controller serial port"; } } else{ currentPosInList = 0; } } //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); portName = portsAvailable.at(currentPosInList - 1).portName(); portInfo = portsAvailable.at(currentPosInList - 1); 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 void DeviceConnection::closeConnection() { serialPort_Controller->close(); } //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 VERAK 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 void DeviceConnection::sendFromQueue() //this is tied to a qtimer that triggers once every 50ms { 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) { return false; } else { return true; } } //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() { serialPort_Controller->close(); qDebug() << "restartConnection"; connectingTimer->start(3000); //qDebug() << "startedTimer"; emit lostConnection(); } //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"; closeConnection(); emptyQueue(); connectingTimer->start(3000); emit lostConnection(); } }
For debugging purposes, your class is really overly complicated.
You should create something simpler that does not try to do so many stuff through timers.
As a test helper, you could use the QSerialPort terminal. That way you can do tests manually.
-
For debugging purposes, your class is really overly complicated.
You should create something simpler that does not try to do so many stuff through timers.
As a test helper, you could use the QSerialPort terminal. That way you can do tests manually.
wrote on 2 Jun 2023, 08:54 last edited by@SGaist What are some other ways I could approach reconnecting in regular intervals, as well as waiting for a connection to complete, other than timers?
-
@SGaist What are some other ways I could approach reconnecting in regular intervals, as well as waiting for a connection to complete, other than timers?
A timer can be a possibility however I would rather first determine whether it's really needed.
Also, if the reconnection happens, does the serial port name change ?
1/6