Unsolved Sending files over RS-485 using QtSerialPort/QSerial
-
@ivar I'm still wondering if half-duplex serial lines are optimal for transmitting HD pictures.
Ex: on 115 kBaud, you get approx. 10 kByte/sec.
Transmitting 600 kByte takes a full minute! Have you considered this?
-
@aha_1980 I have, and you are right they most likely are not optimal for the purpose but my circumstances are set.
Im just trying to make a program that works. Slow or not. -
@JKSH Hello sir!
So I have now got the program to send over the array of bytes and then put it together in the correct order. I now stand before the problem of closing my
QObject::connect(&serial ,&QSerialPort::readyRead, [&] { qDebug() << "New data Available: "; QByteArray datas; datas = serial.readAll(); imgBytes.append(datas); qDebug() << imgBytes; });
I'm not very familiar with lambda expressions but as I've understood it this expression keeps listening to the port even when there is no more data being available.
Any tips on how I can exit the loop? (I solved this temporarily by using a counter just so I could move forward)
I do however have some problems with creating a image from the arrays. I've used the serializing/deserializing suggestion you posted earlier.
this is my array before transmission
"\x00\x00\x00\x01\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00>\x00\x00\x00<\b\x06\x00\x00\x00>\t\tO\x00\x00\x00\tpHYs\x00\x00\x0E\xC4\x00\x00\x0E\xC4\x01\x95+\x0E\x1B\x00\x00\x00\x93IDATh\x81\xED\xD9""A\x11\x83P\x10\x04\xD1\xF9)\xB4\xA0\x06-\x89\x97hA\rf>\x07\xB8`\x80""9t?\x03S]{\xDC\x91u\xCE\x14\xCD\xE3W\xD9\xFDTVo\xAD\xE8$Yj\xCB\x0F\xFF\x97\xF7\xBE\xDD\x8B""7\x19Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\xD8\xF0\x91\xA4\xF2""B\xDA\xAA\x8F+\xF0\xC5k\xE1\xFBh-_\xBC""8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\xB5p\xDF\xC4%'p\xA7\fw\x0F\xF4 \xAD\x00\x00\x00\x00IEND\xAE""B`\x82"
and after recieval
"\x00\x00\x00\x01\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00>\x00\x00\x00<\b\x06\x00\x00\x00>\t\tO\x00\x00\x00\tpHYs\x00\x00\x0E\xC4\x00\x00\x0E\xC4\x01\x95+\x0E\x1B\x00\x00\x00\x93IDATh\x81\xED\xD9""A\x11\x83P\x10\x04\xD1\xF9)\xB4\xA0\x06-\x89\x97hA\rf>\x07\xB8`\x80""9t?\x03S]{\xDC\x91u\xCE\x14\xCD\xE3W\xD9\xFDTVo\xAD\xE8$Yj\xCB\x0F\xFF\x97\xF7\xBE\xDD\x8B""7\x19Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\xD8\xF0\x91\xA4\xF2""B\xDA\xAA\x8F+\xF0\xC5k\xE1\xFBh-_\xBC""8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\x86\xD3\x18Nc8\x8D\xE1""4\xB5p\xDF\xC4%'p\xA7\fw\x0F\xF4 \xAD\x00\x00\x00\x00IEND\xAE""B`\x82"
Any thoughts?
-
@JKSH I thought I would include my programs sending & recieving if you would take the time to help me out,
SerialportSend
#include "mainwindow.h" #include <QApplication> #include <QtSerialPort/QSerialPort> #include <QtDebug> #include <QByteArray> #include <QDataStream> QSerialPort serial; void sender() { QImage img(":/fourColors.png"); QByteArray imgBytes; QDataStream stream(&imgBytes, QIODevice::WriteOnly); stream << img; qDebug() << "Transmission starting"; qDebug() << imgBytes; serial.write(imgBytes); qDebug() << ".."; serial.flush(); serial.waitForReadyRead(1000); serial.waitForBytesWritten(1000); qDebug() << "Transmission finished"; } int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; //w.show(); serial.open(QSerialPort::WriteOnly); serial.setPortName("ttyUSB0"); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); serial.setFlowControl(QSerialPort::NoFlowControl); serial.setBaudRate(QSerialPort::Baud9600); serial.setDataBits(QSerialPort::Data8); if(serial.open(QSerialPort::ReadWrite)) { qDebug() << "Connected to send!"; sender(); } else { qDebug() << "Error: Not connected to send!"; } return a.exec(); }
SerialportRecieve
#include "mainwindow.h" #include <QApplication> #include <QtSerialPort/QSerialPort> #include <QDebug> #include <QByteArray> #include <QDataStream> #include <QLabel> #include <QPixmap> QSerialPort serial; QByteArray imgBytes; QImage img; int i = 0; void imgBuilder(QByteArray imgBytes) { qDebug() << imgBytes; qDebug() << "STARTING ARRAYBUILD"; QDataStream stream(imgBytes); stream >> img; qDebug() << "ARRAY BUILT!"; } void reciever () { qDebug() << "I read data"; serial.waitForBytesWritten(1000); QObject::connect(&serial ,&QSerialPort::readyRead, [&] { qDebug() << "New data Available: "; QByteArray datas; datas = serial.readAll(); imgBytes.append(datas); if (!serial.bytesAvailable()) { i++; qDebug() << i; } if (i == 16) { qDebug() << "Closing port..."; serial.close(); imgBuilder(imgBytes); } }); } int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); serial.open(QSerialPort::ReadOnly); serial.setPortName("ttyUSB1"); serial.setParity(QSerialPort::NoParity); serial.setStopBits(QSerialPort::OneStop); serial.setFlowControl(QSerialPort::NoFlowControl); serial.setBaudRate(QSerialPort::Baud9600); serial.setDataBits(QSerialPort::Data8); if(serial.open(QSerialPort::ReadOnly)) { qDebug() << "Connected to recieve!"; reciever(); } else { qDebug() << "Error: Not connected to recieve!"; } QLabel label; label.setPixmap( QPixmap::fromImage(img)); label.show(); QObject::connect(&serial, static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error), [&](QSerialPort::SerialPortError error) { qDebug() << "An error Occured: " << error; a.quit(); }); return a.exec(); }
So from this i've compared the arrays before and after and they are identical. My program does show a label but it is empty..
-
@ivar said in Sending files over RS-485 using QtSerialPort/QSerial:
So from this i've compared the arrays before and after and they are identical. My program does show a label but it is empty..
This is related to what I said earlier about asynchronous programming.
When you run your receiver code, this is the sequence of things that happen (Starting from
main()
):- Create a
QApplication
- Create and show a
MainWindow
- Open and configure serial port
- Open serial port again
- Print debug message: "Connected to recieve!"
- Call the
receiver()
function:
a. Print debug message: "I read data"
b. Wait for bytes to be written
c. Connect a lambda function toQSerialPort::readyRead()
d. Return - Create a
QLabel
- Convert the
QImage
toQPixmap
, and apply the pixmap to theQLabel
- Show the
QLabel
- Connect a lambda function to
QSerialPort::error()
- Start running the application's event loop (by calling
QApplication::exec()
) - (Start waiting for bytes to arrive on the serial port)
- (When bytes arrive) Emit the
QSerialPort::readyRead()
signal - (When the signal is emitted) Call the lambda function that was connected in Step #6c:
a. Print debug message: "New data Available: "
b. Read all available data from serial port
c. Check bytes and increment counter, i
d. (When i == 16) Close port and deserialize bytes
Read through your code again, and follow the list above. Make sure you understand and agree with this sequence. If something is not clear, ask for clarification.
The most important part is this: You converted the QImage to QPixmap at Step #8, but the QImage isn't complete until Step #14d!
You must understand: Step #6c does not run the lambda function to process the byte array. Step #6c only registers the lambda function, and then your code continues to the next line. The lambda function is only run when the signal
QSerialPort::readyRead()
is emitted.Your task now is to figure out how you should change the code so that it waits for all the bytes to be received before it creates the
QPixmap
. Do this first, then we'll discuss your other questions. - Create a
-
@JKSH Right. I did what you said and followed the code throughout and I understand what you mean. So I figured I was hindering myself by running everything from the main function and re-wrote my reciever program in the
mainwindow.cpp
instead of themain.cpp
. I discarded the counter and implemented a command function instead.
Instead of creating a label when I construct myQPixmap
I tried out creating my mainwindow and a label that I can update when needed. I tried using signals and slots to make this work and I think I got it right -
I included this (below) into my header file and can define and use the function in mymainwindow.cpp
file.private slots: void serialBuilder();
mainwindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" #include "mainwindow.h" #include <QApplication> #include <QtSerialPort/QSerialPort> #include <QDebug> #include <QByteArray> #include <QDataStream> #include <QLabel> #include <QPixmap> #include <QBuffer> QSerialPort *serial; QByteArray imgBytes; QImage img; int i = 0; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); serial = new QSerialPort(this); serial->open(QSerialPort::ReadOnly); serial->setPortName("/dev/ttymxc2"); serial->setParity(QSerialPort::NoParity); serial->setStopBits(QSerialPort::OneStop); serial->setFlowControl(QSerialPort::NoFlowControl); serial->setBaudRate(QSerialPort::Baud9600); serial->setDataBits(QSerialPort::Data8); connect(serial,SIGNAL(readyRead()),this,SLOT(serialBuilder())); if(serial->open(QSerialPort::ReadOnly)) { qDebug() << "Connected to recieve!"; } else { qDebug() << "Error: Not connected to recieve!"; } } void MainWindow::serialBuilder(){ serial->waitForBytesWritten(1000); qDebug()<< "New data available!"; QByteArray datas; datas = serial->readAll(); if (datas == "Build") { qDebug()<<imgBytes; QDataStream stream(imgBytes); stream >> img; qDebug()<< "Array built, constructing image..."; ui->label->setContentsMargins(0,0,0,0); ui->label->setScaledContents(true); ui->label->setPixmap(QPixmap::fromImage(img)); } else { imgBytes.append(datas); } } MainWindow::~MainWindow() { delete ui; }
And success! After I had tested it out, as you suggested, with a small 2x2 pixel image and got it to work I did the same thing with a large HD picture. I got the image to be constructed and display at my other device scaled down to about 1/4th of the original size.
However I still have some things that are unclear to me and I would like to fix.
-
I do not fully understand the difference of
connect(serial,SIGNAL(readyRead()),this,SLOT(serialBuilder()));
and my earlier expressionQObject::connect(&serial ,&QSerialPort::readyRead, [&] {...});
maybe this is due to me being a novice on C++... -
I would like to have a timer on the
connect(serial,SIGNAL(readyRead()),this,SLOT(serialBuilder()));
instead of it waiting for a command. Say for instance after 5 seconds, if there is no signal emitted to the port then it automatically proceeds into the construction of the array.
This way I could maybe (?) send one image - wait for it to update it to the device - send a new picture and get it to update my device. (I would of course have to clear my array appending the byteArray intermittently)
Perhaps I could use something likeif (!serial.bytesAvailable) { ... }
orìf (serial.atEnd) { ... }
I've tried it out and failed earlier but I'll give it a shot again..
Thanks again for all your help and support, couldn't have done it without you! :)
-
-
Hi,
- The second syntax triggers a compile time error and will fail the build.
-
@ivar said in Sending files over RS-485 using QtSerialPort/QSerial:
And success! After I had tested it out, as you suggested, with a small 2x2 pixel image and got it to work I did the same thing with a large HD picture.
Congrats! Great work.
However I still have some things that are unclear to me and I would like to fix.
- I do not fully understand the difference of
connect(serial,SIGNAL(readyRead()),this,SLOT(serialBuilder()));
and my earlier expressionQObject::connect(&serial ,&QSerialPort::readyRead, [&] {...});
maybe this is due to me being a novice on C++...
Read this: https://doc.qt.io/qt-5/signalsandslots-syntaxes.html
connect(serial, SIGNAL(readyRead()), this, SLOT(serialBuilder()));
is equivalent toconnect(serial, &QSerialPort::readyRead, this, &MainWindow::serialBuilder);
The first version is a string-based connection; the second version is a functor-based connection
If
serialBuilder()
is only called from one place, you can write it "inline" as a lambda function instead of creating a separate slot function.- I would like to have a timer on the
connect(serial,SIGNAL(readyRead()),this,SLOT(serialBuilder()));
instead of it waiting for a command. Say for instance after 5 seconds, if there is no signal emitted to the port then it automatically proceeds into the construction of the array.
- Create a
QTimer
and store it somewhere (the same way you're storing theQSerialPort
- Connect the
QTimer::timeout()
signal to the function that converts the byte array into an image. - Make the timer single-shot.
- Every time
serialBuilder())
is called,start()
the timer.
This way, the timer starts counting down when the first chunk of data is received. If a new chunk arrives within 5 s, then the timer gets reset and it starts counting from 5 s again. 5 s after the last chunk is received, the
timeout()
signal gets emitted.Thanks again for all your help and support, couldn't have done it without you! :)
You're most welcome. I hope you're having fun and learning lots. Here are a few more tips:
- You are trying to open the serial port twice. Don't call
open()
before you configure the port. - Your code only reads data from the serial port -- it does not write data into the serial port. Therefore, it makes no sense to call
waitForBytesWritten()
: https://doc.qt.io/qt-5/qserialport.html#waitForBytesWritten - You have quite a few global variables. Try to avoid these. Convert them into private member variables of your
MainWindow
instead. - The nicer/proper way to include
QSerialPort
is: AddQT += serialport
to your *.pro file, then write#include <QSerialPort>
instead of#include <QtSerialPort/QSerialPort>
- If you plan to scale down the image, then it could make sense to do it before sending it across the serial port: https://doc.qt.io/qt-5/qimage.html#scaled
Happy coding!
- I do not fully understand the difference of
-
@JKSH I'll look into the timer and make sure I understand everything we've done.
Thanks again and have a great summer! -
@ivar said in Sending files over RS-485 using QtSerialPort/QSerial:
@JKSH I'll look into the timer and make sure I understand everything we've done.
All the best with your project.
Thanks again and have a great summer!
You're welcome. Have a great summer!
(P.S. It's winter where I live ;) )
-
@ivar if your issue is solved, please don't forget to mark your post as such! Thanks