QSerialPort buffers more bytes than readBufferSize - qt

I have a microcontroller that sends stuff to an embedded Linux device over RS485 that shows up as a /dev/ttysomething. I use the QSerialPort class (from Qt 5.15.2) to read received data.
This basically works.
As a test, I send a block of 100 bytes, approximately once a second (in practice probably slightly slower than that). The value of the first byte of the first block is 0, then the value of each successive byte increases by 1. The receiving side expects that and checks whether something unexpected is received. It does this also once per second.
// In the header:
QTimer timer;
QSerialPort serialPort;
MyValues* myValues; // Contains some Q_PROPERTYs for displaying some values on the screen
// In the cpp:
void MyStuff::setup()
{
serialPort.setPortName("/dev/ttymxc2");
serialPort.setBaudRate(QSerialPort::Baud57600);
serialPort.setReadBufferSize(1);
if (!serialPort.open(QIODevice::ReadWrite))
{
qDebug() << "Cannot serialPort.open: " << serialPort.error();
return;
}
timer.setInterval(1000);
timer.setSingleShot(false);
QObject::connect(&timer, &QTimer::timeout, &timer, [=]()
{
// Display how many times the timer elapsed
myValues->setSendCount(myValues->sendCount() + 1);
{
auto info = qDebug().nospace();
info << "Available = " << serialPort.bytesAvailable() << ", ";
info << "Expected = ";
printHexByte(info, myValues->expectedValue());
}
QByteArray readData = serialPort.readAll();
if (serialPort.error() == QSerialPort::ReadError)
{
qDebug() << "serialPort.error is ReadError: " << serialPort.errorString();
}
else
{
auto line = qDebug().nospace();
line << "Read " << readData.length() << " bytes: ";
for (int i = 0; i < readData.length(); i++)
{
unsigned char actualValue = readData[i];
if (actualValue == myValues->expectedValue())
{
printHexByte(line, actualValue);
line << " ";
}
else
{
myValues->setMismatchCount(myValues->mismatchCount() + 1);
line << "\nMismatch: Expected = ";
printHexByte(line, myValues->expectedValue());
line << ", Actual = ";
printHexByte(line, actualValue);
line << "\n";
myValues->setExpectedValue(actualValue);
}
myValues->setExpectedValue(myValues->expectedValue() + 1);
}
}
});
timer.start();
}
What I would expect:
Assuming that the microcontroller sending the block and the reading happen sufficiently out-of-phase so that we don't read in the middle of a block.
Since the read buffer size is set to 1, I would expect that there is only ever one byte up for grabs, with the 99 other bytes of the block being discarded. So each time the timer elapses, one byte is available, serialPort.readAll() reads 1 byte and it's either the first or last byte of the block.
What actually happens:
Each time the timer elapses, one byte is available, serialPort.readAll() reads 1 byte, but its value is always the previous byte's value + 1, so no bytes are discarded.
So I was wondering: Where are the bytes buffered? And how do I prevent it?
Because if my program reading the data happens to go slower than the microcontroller sending it, I don't want everything to get out of sync and blow when the system eventually runs out of memory after running for hours, I'd rather detect dropped bytes through checksums and be notified of the problem immediately when it happens.

Related

How can the HC128 streamcipher be used to decrypt a ciphered message in smaller parts?

I use the HC128 streamcipher in an application where the sender
applies the HC128 cipher on the message, slices the ciphered data
into (fixed size) smaller parts and sends them to the receiver.
The receiver gets these encrypted data chunks but it cannot wait until all parts
arrive to decrypt the whole message. Instead it should decrypt
the individual message parts and use them before all parts arrive.
I tried to realize this concept but it did not work: when I decrypted
the message parts I did not get the original message. Do I misuse
the HC128 streamcipher or is there a missing step? How can I
achieve the expected behavior?
The HC128 source.
#include <iostream>
#include <ecrypt-sync.h>
int main()
{
ECRYPT_ctx sender_ctx;
ECRYPT_ctx receiver_ctx;
unsigned char key[32];
unsigned char iv[32];
unsigned char plaintext[100];
unsigned char ciphertext[100];
unsigned char plaintext_receiver[100];
// SENDER
// ------
// Key and initial value for the example.
for (int i = 0; i < 16; i++)
key[i] = iv[i] = i;
// Initialize sender's encryptor.
ECRYPT_keysetup(&sender_ctx, key, 128, 128);
ECRYPT_ivsetup(&sender_ctx, iv);
// The message to send.
for (int i = 0; i < 24; i++)
plaintext[i] = i;
// Encrypt the message. The ciphertext will be sent in two parts for this example.
ECRYPT_encrypt_bytes(&sender_ctx, plaintext, ciphertext, 24);
// RECEIVER
// --------
// Initialize receiver's decryptor.
ECRYPT_keysetup(&receiver_ctx, key, 128, 128);
ECRYPT_ivsetup(&receiver_ctx, iv);
// First data chunk has received.
ECRYPT_decrypt_bytes(&receiver_ctx, ciphertext, plaintext_receiver, 12);
// Second data chunk has received.
ECRYPT_decrypt_bytes(&receiver_ctx, ciphertext + 12, plaintext_receiver + 12, 12);
for (int i = 0; i < 24; i += 1)
{
if (plaintext[i] != plaintext_receiver[i])
{
std::cout << "Error at index: " << i << "\n";
return -1;
}
}
std::cout << "Everything OK!" << "\n";
return 0;
}

QSerialPort detecting lost connection

My setup is as follows: Arduino Mega UART3 <--> Voltage Level shifter <--> Rpi4
All the code can be found here
Data is being sent synchronously from the Arduino to the Rpi at 100ms and 1000ms (depending on the importance of the data). And also data is being sent from the Rpi back to the Arduino asynchronously on GUI input (There is also a sync sent at 1000ms in case some frames are lost/corrupted).
Coms are handled in a separate thread than the GUI to provide the best user experience.
Well, all is working fine but I have been trying unsuccessfully to detect if the Rpi has lost connection with the Arduino. (In the other direction is covered with a serial watchdog.)
I wanted to avoid the use of another watchdog in the Qt part but I cannot find a suitable signal to detect the lost serial connection as the QSerialPort keeps reporting a Timeout error randomly... and there are no other methods that detect successfully the disconnection.
Here below is the workaround that I am trying to use without much success: (Probably it needs a major rewrite... but I need to be pointed in the right direction first)
// Serial Port Initialization
m_Serial = new QSerialPort();
m_Serial->setPortName("ttyS0");
//m_Serial->setPortName("ttyUSB0");
m_Serial->setBaudRate(QSerialPort::Baud115200);
m_Serial->setDataBits(QSerialPort::Data8);
m_Serial->setParity(QSerialPort::NoParity);
m_Serial->setStopBits(QSerialPort::OneStop);
m_Serial->setFlowControl(QSerialPort::NoFlowControl);
m_Serial->open(QIODevice::ReadWrite);
qDebug() << "SerialPort Status: " << m_Serial->isOpen();
emit serialConnected(m_Serial->isReadable());
while(!abort)
{
QThread::msleep(5);
while (!isConected(m_Serial)){
emit serialConnected(false);
isSerialConected = false;
QThread::sleep(1);
qDebug() << "Serial Error: ";
//qDebug() << "Is readable?: " << m_Serial->isReadable();
//qDebug() << "Bytes availiable?: " << m_Serial->bytesAvailable();
mutex.lock();
abort = _abort;
mutex.unlock();
}
if (!isSerialConected){
emit serialConnected(true);
isSerialConected=true;
}
mutex.lock();
abort = _abort;
mutex.unlock();
if(!m_outFrameQueue->isEmpty())
{
//qDebug() << "Frame empty";
{ *sendData*}
} else
{
if (m_Serial->waitForReadyRead(10) )
{ *readData*}
And the function that I am trying to use to detect if is actually connected:
bool SerialWorker::isConected(QSerialPort *m_Serial){
// qDebug() << "SerialPort Error: " << m_Serial->error();
// qDebug() << "SerialPort isReadable: " << m_Serial->isReadable();
// qDebug() << "SerialPort isReadable: " << m_Serial->bytesAvailable();
if (m_Serial->error() == QSerialPort::SerialPortError::NoError){
return true;
}
else if (m_Serial->error() == QSerialPort::SerialPortError::TimeoutError){
m_Serial->clearError();
//m_Serial->reset();
return true;
}else{
m_Serial->reset();
m_Serial->close();
m_Serial->clearError();
QThread::sleep(1);
m_Serial->open(QIODevice::ReadWrite);
return false;
}
}

How to connect using UDP to my pic32 and get an answer from it

I want to connet with Qt on windows to my PIC32 UDP server. With a test program in C, I can connect and get the answer, but with Qt it is impossible. What am I doing wrong ?
As you can see I use an ACTIVE WAITING with the while, and my slot doesn't seems to be triggered. Maybe you can see my mistake here ? I hate active waiting....
Here is my code on Qt :
void MuTweezer::run()
{
QHostAddress sender;
quint16 senderPort;
QByteArray datagram;
qint64 pendingBytes;
int cpt = 0;
// Message to send
QByteArray message("Hello that's charly");
// m_imuRcvSocket.bind(10000); why is it for in a client side !?
// SEEMS to NOT BE TRIGGERED
connect(&m_imuRcvSocket, SIGNAL(readyRead()), this, SLOT(recu()));
while(m_isRunning && cpt++ < 10)
{
// send the initial message
qint64 bytesSent= m_imuRcvSocket.writeDatagram(message, QHostAddress("192.168.0.15"), 10000);
cout << "Bytes sent : " << bytesSent << endl;
// We wait the answer from the server
while(!m_imuRcvSocket.hasPendingDatagrams());
// If there is no datagram available, this function returns -1
if((pendingBytes = m_imuRcvSocket.pendingDatagramSize()) == -1)
continue;
datagram.resize(pendingBytes);
m_imuRcvSocket.readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
cout << "================="
<< "\nMessage from <" << sender.toString().toStdString().substr(7) << "> on port " << senderPort
<< "\nString : " << datagram.data()
<< "\nSize: " << pendingBytes << " Bytes (characters)\n"
<< "=================" <<
endl;
}
}
Here is my code on the PIC32, as you can see, once I receive a message, I send the answer, it allows me to make a bidirectionnal communication :
if(!UDPIsOpened(mySocket)){
DBPRINTF("Socket CLOSED");
continue; // go back to loop beginning
}
DBPRINTF("Socket OPEN");
if(!(lengthToGet = UDPIsGetReady(mySocket)))
continue;
// get the string
// UDPGetArray : returns the number of bytes successfully read from the UDP buffer.
if((lengthWeGot = UDPGetArray(message, lengthToGet)))
UDPDiscard(); // Discards any remaining RX data from a UDP socket.
/* Modifie it and send it back */
if(UDPIsPutReady(mySocket)){
message[20]= 'Z';
message[21]= 'i';
message[22]= 'b';
message[23]= 'o';
UDPPutArray(message, lengthWeGot);
UDPFlush();
}
Any idea ?
Try to use waitForBytesWritten and waitForReadyRead:
// to receive datagrams, the socket needs to be bound to an address and port
m_imuRcvSocket.bind();
// send the initial message
QByteArray message("Hi it's Charly");
qint64 bytesSent= m_imuRcvSocket.writeDatagram(message,
QHostAddress("200.0.0.3"),
10000);
bool datagramWritten = m_imuRcvSocket.waitForBytesWritten();
// add code to check datagramWritten
datagram.resize(50); // random size for testing
bool datagramReady = m_imuRcvSocket.waitForReadyRead() && m_imuRcvSocket.hasPendingDatagrams();
// add code to check datagramReady
m_imuRcvSocket.readDatagram(datagram.data(),
datagram.size(),
&sender,
&senderPort);
cout << "================="
<< "\nMessage from <" << sender << "> on port " << senderPort
<< "\nString : " << datagram
<< "\nSize: " << pendingBytes << " Bytes (characters)\n"
<< "=================" <<
endl;
A better alternative would be to use signals and slots as described in the documentation of QUdpSocket
if you plan to use the microprocessor as a client with UDP you need the MAC address of the destination machine otherwise it will not work. This one took me 4 hours to figure out.

Qt - H.264 video streaming using FFmpeg libraries

I am trying to get my IP camera stream in my Qt Widget application. First, I connect to UDP port of IP camera. IP camera is streaming H.264 encoded video. After socket is bound, on each readyRead() signal, I am filling the buffer with received datagrams in order to get a full frame.
Variable initialization:
AVCodec *codec;
AVCodecContext *codecCtx;
AVFrame *frame;
AVPacket packet;
this->buffer.clear();
this->socket = new QUdpSocket(this);
QObject::connect(this->socket, &QUdpSocket::connected, this, &H264VideoStreamer::connected);
QObject::connect(this->socket, &QUdpSocket::disconnected, this, &H264VideoStreamer::disconnected);
QObject::connect(this->socket, &QUdpSocket::readyRead, this, &H264VideoStreamer::readyRead);
QObject::connect(this->socket, &QUdpSocket::hostFound, this, &H264VideoStreamer::hostFound);
QObject::connect(this->socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
QObject::connect(this->socket, &QUdpSocket::stateChanged, this, &H264VideoStreamer::stateChanged);
avcodec_register_all();
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec){
qDebug() << "Codec not found";
return;
}
codecCtx = avcodec_alloc_context3(codec);
if (!codecCtx){
qDebug() << "Could not allocate video codec context";
return;
}
if (codec->capabilities & CODEC_CAP_TRUNCATED)
codecCtx->flags |= CODEC_FLAG_TRUNCATED;
codecCtx->flags2 |= CODEC_FLAG2_CHUNKS;
AVDictionary *dictionary = nullptr;
if (avcodec_open2(codecCtx, codec, &dictionary) < 0) {
qDebug() << "Could not open codec";
return;
}
Algorithm is as follows:
void H264VideoImageProvider::readyRead() {
QByteArray datagram;
datagram.resize(this->socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
this->socket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
QByteArray rtpHeader = datagram.left(12);
datagram.remove(0, 12);
int nal_unit_type = datagram[0] & 0x1F;
bool start = (datagram[1] & 0x80) != 0;
int seqNo = rtpHeader[3] & 0xFF;
qDebug() << "H264 video decoder::readyRead()"
<< "from: " << sender.toString() << ":" << QString::number(senderPort)
<< "\n\tDatagram size: " << QString::number(datagram.size())
<< "\n\tH264 RTP header (hex): " << rtpHeader.toHex()
<< "\n\tH264 VIDEO data (hex): " << datagram.toHex();
qDebug() << "nal_unit_type = " << nal_unit_type << " - " << getNalUnitTypeStr(nal_unit_type);
if (start)
qDebug() << "START";
if (nal_unit_type == 7){
this->sps = datagram;
qDebug() << "Sequence parameter found = " << this->sps.toHex();
return;
} else if (nal_unit_type == 8){
this->pps = datagram;
qDebug() << "Picture parameter found = " << this->pps.toHex();
return;
}
//VIDEO_FRAME
if (start){
if (!this->buffer.isEmpty())
decodeBuf();
this->buffer.clear();
qDebug() << "Initializing new buffer...";
this->buffer.append(char(0x00));
this->buffer.append(char(0x00));
this->buffer.append(char(0x00));
this->buffer.append(char(0x01));
this->buffer.append(this->sps);
this->buffer.append(char(0x00));
this->buffer.append(char(0x00));
this->buffer.append(char(0x00));
this->buffer.append(char(0x01));
this->buffer.append(this->pps);
this->buffer.append(char(0x00));
this->buffer.append(char(0x00));
this->buffer.append(char(0x00));
this->buffer.append(char(0x01));
}
qDebug() << "Appending buffer data...";
this->buffer.append(datagram);
}
first 12 bytes of datagram is RTP header
everything else is VIDEO DATA
last 5 bits of first VIDEO DATA byte, says which NAL unit type it is. I always get one of the following 4 values (1 - coded non-IDR slice, 5 code IDR slice, 7 SPS, 8 PPS)
5th bit in 2nd VIDEO DATA byte says if this datagram is START data in frame
all VIDEO DATA is stored in buffer starting with START
once new frame arrives - START is set, it is decoded and new buffer is generated
frame for decoding is generated like this:
00 00 00 01
SPS
00 00 00 01
PPS
00 00 00 01
concatenated VIDEO DATA
decoding is made using avcodec_decode_video2() function from FFmpeg library
void H264VideoStreamer::decode() {
av_init_packet(&packet);
av_new_packet(&packet, this->buffer.size());
memcpy(packet.data, this->buffer.data_ptr(), this->buffer.size());
packet.size = this->buffer.size();
frame = av_frame_alloc();
if(!frame){
qDebug() << "Could not allocate video frame";
return;
}
int got_frame = 1;
int len = avcodec_decode_video2(codecCtx, frame, &got_frame, &packet);
if (len < 0){
qDebug() << "Error while encoding frame.";
return;
}
//if(got_frame > 0){ // got_frame is always 0
// qDebug() << "Data decoded: " << frame->data[0];
//}
char * frameData = (char *) frame->data[0];
QByteArray decodedFrame;
decodedFrame.setRawData(frameData, len);
qDebug() << "Data decoded: " << decodedFrame;
av_frame_unref(frame);
av_free_packet(&packet);
emit imageReceived(decodedFrame);
}
My idea is in UI thread which receives imageReceived signal, convert decodedFrame directly in QImage and refresh it once new frame is decoded and sent to UI.
Is this good approach for decoding H.264 stream? I am facing following problems:
avcodec_decode_video2() returns value that is the same like encoded buffer size. Is it possible that encoded and decoded date are always same size?
got_frame is always 0, so it means that I never really received full frame in the result. What can be the reason? Video frame incorrectly created? Or video frame incorrectly converted from QByteArray to AVframe?
How can I convert decoded AVframe back to QByteArray, and can it just be simply converted to QImage?
The whole process of manually rendering the frames can be left to another library. If the only purpose is a Qt GUI with live feed from the IP camera you can use libvlc library. You can find an example here: https://wiki.videolan.org/LibVLC_SampleCode_Qt

Arduino Serial.println is printing two lines

I am doing some simple arduino projects in an effort to learn some of the basics.
For this project I am trying to print a line sent through the serial monitor. When I print the line, my leading text prints along with the first character of the user input, and then a new line starts and the leading text prints again along with the rest of the user data. I'm not sure why this is happening.
Here is my code:
char data[30];
void setup()
{
Serial.begin(9600);
}
void loop()
{
if (Serial.available())
{
//reset the data array
for( int i = 0; i < sizeof(data); ++i )
{
data[i] = (char)0;
}
int count = 0;
//collect the message
while (Serial.available())
{
char character = Serial.read();
data[count] = character;
count++;
}
//Report the received message
Serial.print("Command received: ");
Serial.println(data);
delay(1000);
}
}
When I upload the code to my Arduino Uno, and open the serial monitor, I can type in a string like: "Test Message"
When I hit enter, I get the following result:
Command received: T
Command received: est Message
When what I was expecting was:
Command received: Test Message
Can someone point me in the right direction?
Thanks in advance for the help.
Serial.available() doesn't return a boolean it returns how many bytes are in the Arduino's serial buffer. Because you are moving that buffer into a list of 30 chars you should check that the serial buffer is 30 chars long with the condition Serial.available() > 30.
This could be causing the code to execute once as soon as the serial buffer has any data, hence it running for the first letter then again realising more has been written to the buffer.
I'd recommend also completely removing your data buffer and using the data direct from the serial's buffer. e.g
Serial.print("Command received: ");
while (Serial.available()) {
Serial.print((char)Serial.read());
}
Edit: How to wait until serial data finishes being sent
if (Serial.available() > 0) { // Serial has started sending
int lastsize = Serial.available(); // Make a note of the size
do {
lastsize = Serial.available(); // Make a note again so we know if it has changed
delay(100); // Give the sender chance to send more
} while (Serial.available() != lastsize) // Has more been received?
}
// Serial has stopped sending

Resources