Arduino doesn't receive data after reconnecting to USB - c++

So, I've built a basic QT GUI where I want to establish communication with an Arduino Nano through USB. I send a number through the GUI and the Arduino receives the number and processes it.
The communication works fine when I upload the code to Arduino and right afterwards open the GUI and start the process. However, when I disconnect the Arduino from the USB (or restart my PC - I've tried both) and reconnect it to use it with the GUI, the Arduino behaves like it received nothing.
More specifically, in the first case Serial.available() returns "1" as it receives the number properly, but in the latter case it returns "0", so it does nothing.
I made the code as simple as I could trying to track down the issue and the problem continues.
So here is the main QT GUI code:
depth_ = insertDepthEdit->text().toInt(); // user input from GUI
myThread *mThread;
mThread = new myThread(this, depth_);
connect(mThread, SIGNAL(valueRead(QString)), this, SLOT(onTextChange(QString)));
//valueRead is the signal emitted from Arduino
//onTextChange the function that processes the received string
mThread->start();
mThread->wait(100);
mThread->quit();
The Arduino thread code (also QT):
void myThread::run() {
QSerialPort serial;
serial.setPortName("COM3");
serial.setBaudRate(QSerialPort::Baud9600);
serial.setDataBits(QSerialPort::Data8);
serial.setParity(QSerialPort::NoParity);
serial.setStopBits(QSerialPort::OneStop);
serial.setFlowControl(QSerialPort::NoFlowControl);
serial.open(QIODevice::ReadWrite);
if (serial.isOpen() && serial.isWritable()) {
qDebug() << "Ready to write..." << endl;
QByteArray ba(QString::number(depth_).toStdString().c_str());
qDebug() << ba << endl;
serial.write(ba);
if (serial.bytesToWrite() > 0) {
serial.flush();
if (serial.waitForBytesWritten(1000)) {
qDebug() << "data has been sent" << endl;
}
}
if (serial.flush()) {
qDebug() << "ok" << endl;
}
}
else {
qDebug() << "Error";
}
if (serial.isOpen() && serial.isReadable()) {
qDebug() << "Ready to read..." <<endl;
while (serial.waitForReadyRead(5000)) {
QByteArray inByteArray = serial.readLine();
input_ = QString(inByteArray);
qDebug() << input_;
qDebug() << "ok" << endl;
emit valueRead(input_);
}
}
serial.close();
}
And finally the Arduino code:
int c = 0;
const int ledPin = 13;
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.print(Serial.available());
while (Serial.available() > 0) {
digitalWrite(ledPin, HIGH);
delay(5);
c = Serial.read() - '0';
Serial.flush();
}
delay(1000);
digitalWrite(ledPin, LOW);
delay(500);
}
When I upload the code to Arduino, it functions properly no matter if I close the GUI and restart it. The problem happens only if Arduino loses power, e.g: when I disconnect it from USB or restart the PC.
~~~~~~~~~~~~~~~~~~~~~~~~~~EDIT~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
COM port remains the same after reconnecting and Arduino Rx LED flashes normally when I send data through the GUI.
~~~~~~~~~~~~~~~~~~~~~~~~~~EDIT 2~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OK, so, I tried using the code from Arduino Serial documentation and the problem remains. When I upload the code the Arduino receives the character properly and turns the LED on, but once I disconnect it and then connect it back, it does nothing, the LED remains low as it never enters "if".
Here's the code I used:
int incomingByte = 0;
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available() > 0) {
digitalWrite(13, HIGH);
incomingByte = Serial.read();
}
}
~~~~~~~~~~~~~~~~~~~~~~EDIT 3~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So I have the following 3 scenarios:
Use Scenario A:
Upload code
Run GUI
Send data - It receives properly
Disconnect and reconnect
Run GUI again
Send data - RX blinks but Serial.available returns 0
Use Scenario B:
Upload code
Run Brays
Send data - It receives properly
Disconnect and reconnect
Run Brays again
Send data - It receives properly
Use Scenario C (the most interesting) :
Upload code
Run GUI
Send data - It receives properly
Disconnect and reconnect
Run Brays this time
Send data - It receives properly
Run GUI again after Brays
Send data - It receives properly
I also made the QT GUI code as simple as that but the problem persists:
void myThread::run()
{
QSerialPort *serial = new QSerialPort();
serial->setPortName("COM3");
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->open(QIODevice::WriteOnly);
if (serial->isOpen() && serial->isWritable())
{
QByteArray ba(QString::number(depth_).toStdString().c_str());
serial->write(ba);
serial->flush();
serial->close();
}
delete serial;
}
~~~~~~~~~~~~~~~~~~~~~~EDIT 4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So after much effort and curiosity, I realized that the source of the problem is not the Arduino code but something related to QT or Windows. I added the delays that Jeff recommended and noticed that each time it functioned properly Rx blinked and LED became high as indicated by the code. However, after reconnecting, the problem still remained but I noticed that, this time, immediately after clicking "Send" to send the characters, the LED blinked for some milliseconds (possibly indicating some error??) and then after the 1 second delay the Rx blinked indicating the receipt of data and LED remained LOW as Serial.available remained 0.
So, what I tried next, was to remove one line of code at a time to see what causes the problem. And I ended up with literally blank Arduino code, just empty setup and loop methods, and the following QT GUI code:
void myThread::run()
{
QSerialPort *serial1 = new QSerialPort();
serial1->setPortName("COM5");
serial1->open(QIODevice::WriteOnly);
serial1->close();
}
To summarize, what happens now is:
Upload code to Arduino
Run GUI
Send data
Nothing happens (normal behaviour)
Disconnect and reconnect Arduino to USB
Run GUI
Send data
Arduino LED momentarily blinks once (possibly indicating some kind of error)

OK, so, after hours of debugging I've found what caused the problem.
The root of it was that after reconnecting the Arduino, each time I called serial.open in QT, Arduino did a reset (indicated by the blink of the LED) and by the time it was after the bootloader stage and was running the code, the main program had already passed the serial.write QT command without receiving the data.
So, what I did to solve the problem was to just add a Sleep(uint(2000)); after serial.open in order to let Arduino finish booting and then start sending data.
Thank you all for your help and immediate answers!

In my experience, the issue is not the code in the Arduino. It is because the serial port gets a different name when it is plugged back in.
For example in Linux, originally the port is /dev/ARD0, but when it is disconnected and plugged back in with connections on ARD0, the new plugin is named /dev/ARD1. (In Windows, it might be COM17 then COM18.)
The only way I know to make it become the original port name is to close everything that is connected to it before plugging in again: Close the Arduino IDE, close all programs which have opened the port, etc.

If you use this example for the Arduino Serial documentation do you receive the chars you send?
int incomingByte = 0; // for incoming serial data
void setup() {
Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
}
void loop() {
// send data only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
}

Grasping at straws here, replace my comments below a one second delay. Editing on iPhone messed with the format a little, but I believe you can see my intent.
Edit: Also, I think you should not do serial->close inside your loop. I would also try sending a single character repeatedly until we have that working.
void myThread::run()
{
QSerialPort *serial = new QSerialPort();
serial->setPortName("COM3");
serial->setBaudRate(QSerialPort::Baud9600);
serial->setDataBits(QSerialPort::Data8);
serial->open(QIODevice::WriteOnly);
if (serial->isOpen() && serial->isWritable())
{
QByteArray ba(QString::number(depth_).toStdString().c_str());
serial->write("x");
delay 1 second here
serial->flush();
delay 1 second here
}
serial->close();
delay 1 second here
delete serial;
}

Related

ESP8266-01 does not react to AT Commands over UART with TM4C123GH6PM

I am trying to connect my TM4C123GH6PM Microcontroller from Texas Instruments with my Smartphone and use it to control an alarm clock and LED Lights. (the LEDs are controlled over a Transistor, which is controlled over an GPIO Pin).
I have some experience with coding in C++ and the TM4C123GH6PM, but I am still learning a lot. So please excuse some foolish mistakes I might have made.
I want to connect the ESP8266 with the Microcontroller using UART and the TivaWare Framework.
I have written some code and my UART works correctly (I tested it by sending chars from UART 4 to 3).
According to the AT commands of ESP8266 It should respond to "AT" with "OK". But whenever I send something to the ESP it responds with exactly what I sent to it. I checked the wiring, and that's not The Issue. Or at least I think so. Please correct me, if the wiring is wrong.
ESP -> TM4C123GH6PM:
GND -> GND
VCC -> 3.3V
Tx -> Rx (UART3 / PC6)
Rx -> Tx (UART4 / PC5)
CH_PD -> 3.3V
I also checked for the power consumption of the ESP. Everything is powered by the USB-port of my laptop, since that helps keep the cable mess down. I monitor the power consumption with (https://www.amazon.de/gp/product/B07C8CM5TG/ref=ppx_yo_dt_b_asin_title_o08_s00?ie=UTF8&psc=1). The ESP is drawing about 150mA from the computer, but the port can provide a lot more. I checked with some LEDs and 400mA is not a problem.
Can anyone help me? I am working on this now for over two days and can't find a Solution. What is the Problem with the ESP not responding correctly to the AT command? The blue light is one, when the code is running.
PS: The attached code contains also code for the alarm clock control and LEDs. I attached it, since it could be part of the problem, but some of it is commented out and most of it is not used.
#include<stdint.h>
#include<stdbool.h>
#include"inc/hw_ints.h"
#include"inc/hw_memmap.h"
#include"inc/hw_types.h"
#include"driverlib/gpio.h"
#include"driverlib/sysctl.h"
#include"driverlib/timer.h"
#include"driverlib/interrupt.h"
#include"driverlib/uart.h"
#include"driverlib/pin_map.h"
#include "driverlib/rom.h"
// stores the time since system start in ms
uint32_t systemTime_ms;
//bools or controling the alarm clock and LEDS
bool an_aus = false;
bool alarm_clock = false;
void InterruptHandlerTimer0A (void)
{
// Clear the timer interrupt flag to avoid calling it up again directly
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// increase the ms counter by 1 ms
systemTime_ms++;
}
void clockSetup(void)
{
uint32_t timerPeriod;
//configure clock
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ| SYSCTL_OSC_MAIN);
//activate peripherals for the timer
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
// configure timers as 32 bit timers in periodic mode
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
// set the variable timerPeriod to the number of periods to generate a timeout every ms
timerPeriod = (SysCtlClockGet()/1000);
// pass the variable timerPeriod to the TIMER-0-A
TimerLoadSet(TIMER0_BASE, TIMER_A, timerPeriod-1);
// register the InterruptHandlerTimer0A function as an interrupt service routine
TimerIntRegister(TIMER0_BASE, TIMER_A, &(InterruptHandlerTimer0A));
// activate the interrupt on TIMER-0-A
IntEnable(INT_TIMER0A);
// generate an interrupt when TIMER-0-A generates a timeout
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
// all interrupts are activated
IntMasterEnable();
// start the timer
TimerEnable(TIMER0_BASE, TIMER_A);
}
void UART (void)
{
//configure UART 4:
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART4);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART4));
//GPIO pins for transmitting and receiving
GPIOPinConfigure(GPIO_PC4_U4RX);
GPIOPinConfigure(GPIO_PC5_U4TX);
GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_4 | GPIO_PIN_5);
//configure UART 8Bit, no parity, baudrat 38400
UARTConfigSetExpClk(UART4_BASE, SysCtlClockGet(), 38400, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
//configure UART 3:
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART3);
while(!SysCtlPeripheralReady(SYSCTL_PERIPH_UART3));
GPIOPinConfigure(GPIO_PC6_U3RX);
GPIOPinConfigure(GPIO_PC7_U3TX);
GPIOPinTypeUART(GPIO_PORTC_BASE, GPIO_PIN_6 | GPIO_PIN_7);
UARTConfigSetExpClk(UART3_BASE, SysCtlClockGet(), 38400, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
}
void delay_ms(uint32_t waitTime)
{
// Saves the current system time in ms
uint32_t aktuell = systemTime_ms;
// Wait until the current system time corresponds to the sum of the time at the start of the delay and the waiting time
while(aktuell + waitTime > systemTime_ms);
}
void ex_int_handler(void)
{
// press the button to start timer for alarm clock
alarm_clock = true;
GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
}
int main(void)
{
//Peripherals for LED and GPIO
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
//UART
UART();
//Timer
clockSetup();
// button
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,GPIO_PIN_4);
GPIOPadConfigSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_STRENGTH_2MA,GPIO_PIN_TYPE_STD_WPU);
//OnboardLED
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_1);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,GPIO_PIN_3);
//Interrupt Timer
GPIOIntDisable(GPIO_PORTF_BASE,GPIO_PIN_4);
GPIOIntClear(GPIO_PORTF_BASE,GPIO_PIN_4);
GPIOIntTypeSet(GPIO_PORTF_BASE,GPIO_PIN_4,GPIO_FALLING_EDGE);
GPIOIntRegister(GPIO_PORTF_BASE,ex_int_handler);
GPIOIntEnable(GPIO_PORTF_BASE,GPIO_PIN_4);
//Transistor Gate
GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE,GPIO_PIN_0);
//GPIOPadConfigSet(GPIO_PORTB_BASE,GPIO_PIN_0,GPIO_STRENGTH_6MA,GPIO_PIN_TYPE_STD_WPU);
//debugging only: save all the received data from the ESP in an array to look at while debugging
int32_t data[20] = {0};
int32_t j = 0;
//Code for debugging the UART and ESP8266
while(1){
//Checks for Data in the FIFO
while(!UARTCharsAvail(UART4_BASE));
//send AT-command to ESP8266
UARTCharPut(UART4_BASE, 'A');
while(UARTBusy(UART4_BASE));
UARTCharPut(UART4_BASE, 'T');
while(UARTBusy(UART4_BASE));
if(UARTCharsAvail(UART3_BASE))
{
while(UARTCharsAvail(UART3_BASE))
{
//Read data from the FIFO in UART3 -> received from ESP8266
data[j] = UARTCharGet(UART3_BASE);
j++;
}
}
//clear array when its full
if (j >= 20)
{
j = 0;
for(int32_t a = 0; a <21; a++)
{
data[a] = 0;
}
}
}
//code to run the alarm clock and leds
/*
while(1)
{
if (alarm_clock)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,GPIO_PIN_3);
//Wait
delay_ms(30600000);
GPIOPinWrite(GPIO_PORTB_BASE,GPIO_PIN_0,GPIO_PIN_0);
alarm_clock = false;
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_3,0x00);
//Start Red LED blinking when it is finished
while(1)
{
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,GPIO_PIN_1);
delay_ms(1000);
GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1,0x00);
delay_ms(1000);
}
}
}
*/
}
According to the AT commands of ESP8266 It should respond to "AT" with
"OK". But whenever I send something to the ESP it responds with
exactly what I sent to it
Modems with AT Commands commonly ship with the echo mode turned on, so that when you are interacting with it manually through serial port, it will echo the characters you sent first, and then send the reply.
So, when you are automating the process, you first send the characters, then wait for the reply until you reach a '\r'. Well, you are reaching a '\r', but its the one from the echo. You might have some other characters next. You send AT, you should receive AT first, then you have the OK.
To solve this problem, you should turn echo mode off.
The command to turn off echo is ATE0.

QSerialPort has bytes available but can't read

I'm writing a Qt GUI application that receives and sends data to an Arduino over serial. It writes correctly, but when trying to read it doesn't work.
My problem is:
I have an Arduino code that sends things over Serial, I programmed it to turn on and off a LED on pin 13 depending on the received data:
#define ARDUINO_TURNED_LED_ON "LEDON"
#define ARDUINO_TURNED_LED_OFF "LEDOFF"
#define PC_REQUESTED_LED_STATE_CHANGE u8"CHANGELED"
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
pinMode(13, OUTPUT);
}
String serialCmd = "";
bool ledState=false;
void loop()
{
// put your main code here, to run repeatedly:
if (Serial.available())
{
serialCmd=Serial.readString();
if (serialCmd==PC_REQUESTED_LED_STATE_CHANGE)
{
if (ledState)
{
digitalWrite(13, LOW);
Serial.write(ARDUINO_TURNED_LED_OFF);
}
else
{
digitalWrite(13, HIGH);
Serial.write(ARDUINO_TURNED_LED_ON);
};
ledState=!ledState;
serialCmd = "";
}
}
}
I'm using the following signal and slot at MainWindow class:
#define ARDUINO_TURNED_LED_ON "LEDON"
#define ARDUINO_TURNED_LED_OFF "LEDOFF"
#define PC_REQUESTED_LED_STATE_CHANGE u8"CHANGELED"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* Create Object the Class QSerialPort*/
serialPort = new QSerialPort(this);
/* Create Object the Class Arduino to manipulate read/write*/
arduino = new Arduino(serialPort);
//connect signal:
connect(serialPort, SIGNAL(readyRead()), this, SLOT(ReadData()));
}
//slot
void MainWindow::ReadData()
{
QString received = arduino->Read();
qDebug() << "received data:" << received;
if (received==ARDUINO_TURNED_LED_ON)
{
qDebug() << "led on";
}
else if (received==ARDUINO_TURNED_LED_OFF)
{
qDebug() << "led off";
}
}
And the Arduino::Read() method that is called by the previous slot:
QString Arduino::Read()
{
QString bufRxSerial;
qDebug() << "bytes available:" << serialPort->bytesAvailable();
/* Awaits read all the data before continuing */
while (serialPort->waitForReadyRead(20)) {
bufRxSerial += serialPort->readAll();
}
return bufRxSerial;
}
When writing to the Arduino it works well, the LED changes as it should, the problem happens when Qt tries to read the replied data.
In most of the times that the Arduino sends something, the signal is emitted and serialPort->bytesAvailable() returns a number that is not zero, but
serialPort->waitForReadyRead(20) times out without receiving anything and serialPort->readAll() returns an empty QString. If I use serialPort->waitForReadyRead(-1) the window freezes.
The weirdest thing is that, in a random moment that something is sent, everything works well and the function returns all the data that couldn't be read previously.
Here's an example of what happens:
1st attempt (fails)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns on)
Arduino writes ARDUINO_TURNED_LED_ON ("LEDON") to the serial port
Qt reads an empty QString from the Arduino
2nd attempt (fails)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns off)
Arduino writes ARDUINO_TURNED_LED_OFF ("LEDOFF") to the serial port
Qt reads an empty QString from the Arduino
3rd attempt (this is a random attempt that everything works)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns on)
Arduino writes ARDUINO_TURNED_LED_ON ("LEDON") to the serial port
Qt reads "LEDONLEDOFFLEDON" from the Arduino, that is everything that couldn't be read previosly.
4th attempt (fails)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns off)
Arduino writes ARDUINO_TURNED_LED_OFF ("LEDOFF") to the serial port
Qt reads an empty QString from the Arduino
5th attempt (this is another random attempt that everything works)
Qt writes PC_REQUESTED_LED_STATE_CHANGE (u8"CHANGELED") to the serial port
Arduino LED changes its state (turns on)
Arduino writes ARDUINO_TURNED_LED_ON ("LEDON") to the serial port
Qt reads "LEDOFFLEDON" from the Arduino, that is everything that couldn't be read previosly.
I tested using the Arduino IDE serial monitor and it worked as it was supposed to be: I wrote "CHANGELED", then the LED changed and the Arduino replied "LEDON" or "LEDOFF" all the times.
What can I do to solve this problem?
Thanks
Edit: the complete code can be found here
The Read method is incorrect and unnecessary. You should never use the waitFor methods. You also shouldn't be using QString when dealing with simple ASCII data that QByteArray handles fine:
class MainWindow : ... {
QByteArray m_inBuffer;
...
};
void MainWindow::ReadData() {
m_inBuffer.append(arduino->readAll());
QByteArray match;
while (true) {
if (m_inBuffer.startsWith((match = ARDUINO_TURNED_LED_ON))) {
qDebug() << "led on";
} else if (m_inBuffer.startsWith((match = ARDUINO_TURNED_LED_OFF))) {
qDebug() << "led off";
} else {
match = {};
break;
}
}
m_inBuffer.remove(0, match.size());
}
The deal is simple: ReadData can be called with any number of bytes available - even a single byte. Thus you must accumulate data until a full "packet" is received. In your case, packets can only be delineated by matching a full response string.
You'd make your life much easier if Arduino sent full lines - replace Serial.write with Serial.println. Then the lines are packets, and you don't need to buffer the data yourself:
void MainWindow::ReadData() {
while (arduino->canReadLine()) {
auto line = arduino->readLine();
line.chop(1); // remove the terminating '\n'
QDebug dbg; // keeps everything on a single line
dbg << "LINE=" << line;
if (line == ARDUINO_TURNED_LED_ON)
dbg << "led on";
else if (line == ARDUINO_TURNED_LED_OFF)
dbg << "led off";
}
}
See? It's much simpler that way.
You can also leverage Qt to "simulate" Arduino code without running real Arduino hardware, see this answer for an example.
This sounds like a buffering problem. Your Qt application receives the bytes, but there is nothing that tells it the reception is complete. I would add line feeds (\n) as terminator character after each string, just use Serial.println() instead of Serial.write(). Then you can use readLine() in Qt and always be sure that what you get is exactly one complete command string.
If you're on Linux you could also try to stty your device to raw with
stty -F /dev/ttyACM0 raw
to have the serial characters sent straight through instead of default line buffering (see What’s the difference between a “raw” and a “cooked” device driver?).
Note that then you might get a new issue: Your Qt application might be so fast that you receive only one letter at a time in your callback.

Avoiding Timeout on QSerialPort when handling high speed data

I'm working on a windows application that receives data from a sensor at 600Hz. In two out of five cases, my IO thread reads the 4 bytes of data from the sensor successfully and passes it on to the GUI thread.
The problem is three out of five times, QSerialPort has inexplicable timeouts where QSerialPort's waitForReadyRead() returns false and serial.errorString() has a timeout error. In which case it will never read data. If I read from the serial port despite the timeout error I will read 2000+ bytes of data in the next waitForReadyRead which will be delivered in chunks which renders the realtime data reception aspect of my application obsolete.
I've tried using the readyRead() signal of the serial port but it exhibits the same behaviour ie. if the timeout error appears, no readyRead() signal is ever fired.
UPDATE: I am able to reproduce the issue with Qt's terminal example ([QT_INSTALL_EXAMPLES]/serialport/terminal) which uses a non-blocking read. The frequency of the bug is considerably less but it's definitely still there.
UPDATE: Using Serial Port Monitor, I can see that when it gets stuck, the Qt Terminal Example gets stuck on IOCTL_SERIAL_WAIT_ON_MASK, my example gets stuck on IRP_MJ_WRITE DOWN just after the IOCT_SERIAL_WAIT_ON_MASK. This never happens with other terminal softwares leading me to think the problem is definitely with Qt.
Pastebin of Serial Port Monitor Output
void IOThread::run(){
QSerialPort serial;
serial.setPortName(portname)
serial.setBaudRage(QSerialPort::Baud115200);
serial.setStopBits(QSerialPort::OneStop)
serial.setParity(QSerialPort::NoParity);
serial.setDataBits(QSerialPort::Data8);
serial.setFlowControl(QSerialPort::NoFlowControl);
if(!serial.open(QIODevice::ReadWrite)
{
qDebug() << "Error Opening Port";
return;
}
else
{
qDebug() << "Error Message: " << serial.errorString() // prints "Unknown Error"
}
while(true)
{
if(serial.waitForReadyRead(1000))
{
qDebug() << "Normal read";
reception_buffer = serial.readAll();
}
else
{
qDebug() << "Timeout";
/* serial.readAll() here will read nothing but force next read to read huge chunk of data */
continue;
}
}
// Process data...
}
Try if this makes any difference:
while (true) {
QByteArray reception_buffer;
if (serial.waitForReadyRead(1000)) {
reception_buffer = serial.readAll();
while (serial.waitForReadyRead(10)) {
reception_buffer += serial.readAll();
}
qDebug() << "reception_buffer ready";
}
else {
qDebug() << "Timeout";
}
}
If you want to prevent from timeouting from waitForReadyRead you can set:
if(serial.waitForReadyRead(-1))
bool QSerialPort::waitForReadyRead(int msecs = 30000) will timeout after msecs milliseconds; the default timeout is 30000 milliseconds. If msecs is -1, the function will not time out.
gets stuck on IOCTL_SERIAL_WAIT_ON_MASK
Most likelly a problem is in your HW or driver. QSP use asynchronous notification, based on WaitCommEvent. If WaitCommEvent get stuck - then a problem is in your device or driver (most likelly).
Thanks to the guys at QSerialPort, this bug in Qt 5.10.1 is solved by applying this patch: https://codereview.qt-project.org/#/c/225277/
"QSP may ignore all the read events when the data comes to the device
within opening. In this case, even re-opening of a device does not
help. Reason is that the QWinOverlappedIoNotifier is enabled after
than the startAsyncCommunication() called, that probably, leads to
ignoring for all EV_RXCHAR events. A workaround is to enable the
notifier before than any of I/O operation called." - Denis Shienkov, QSerialPort Maintainer

Serial.read() skips over serial input

Trying to send serial messages using Arduino Uno and standard IDE. Ran into issue parsing the serial message sent to the device.
See that if I include this line Serial.println("Serial.available() = " + String(Serial.available())); I will be able to read the rest of the message. If this is commented out I will only see the first letter of the message and skip over the rest. Attached image of output that I'm seeing with and without the added line of code.
// the setup routine runs once when you press reset:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
while (!Serial) {} // wait for serial to be initialized
Serial.println("Setup called. Serial port ready.");
Serial.println("Waiting for time sync message...");
while (!Serial.available()) {}
processSyncMessage();
}
void processSyncMessage() {
// parse first letter of message
char messageHeader = (char) Serial.read();
switch (messageHeader) {
case TIME_HEADER:
// do processing
break;
default:
Serial.println("Unknown message sent with header: " + String(messageHeader));
// must include this line in order to see the entire message sent
// just calling a println or a Serial.available() doesn't work ????
Serial.println("Serial.available() = " + String(Serial.available()));
Serial.println("---start of message");
for (int r = 0; r != -1; r = Serial.read()) {
Serial.print((char) r);
}
Serial.println();
Serial.println("---end of message");
break;
}
}
Missing Buffer
With printout
Is this somehow related to a buffer? Can I flush it somehow with fflush(SOME_SECRET_BUFFER)?
have you tried Serial.readString() to parse the entire missing characters?
Serial data is transmitted and received one character at a time. At 9600 baud, the transmission rate is approximately one character per millisecond.
The code assumes that once the first character has arrived, all of them have. This is not the case. The addition of the println consumes CPU time, and therefore has the effect of adding a delay. This delay allows the rest of the original message to be received.
A receive function with an appropriate timeout for your application is needed here.

qt 5.2 serial port write issues with windows 7

We are using FTDI serial port CHIP in our hardware. Now we have working code in Linux and we moved to windows 7. We get some weird problems.
The Problem:
We can't write data to Serial Port without running other console application which do this:
serial.setPortName("COM3");
if (serial.open(QIODevice::ReadWrite)) {
bool success = serial.setBaudRate(QSerialPort::Baud9600) &
serial.setStopBits(QSerialPort::OneStop) &
serial.setDataBits(QSerialPort::Data8) &
serial.setParity(QSerialPort::NoParity) &
serial.setFlowControl(QSerialPort::NoFlowControl);
qDebug() << "Connected to usb device: " << (success ? "OK" : "FAIL");
while(true) {
if(serial.waitForReadyRead(-1)) {
QByteArray out = serial.readAll();
for(int i=0; i< out.length(); i++) {
qDebug() << (int) out[i];
}
}
}
serial.close();
So its just loop with read all. Hardware dosen't send anything, so read is just infinity loop. After closing and running our write program it runs correctly.
char* input;
input = new char[size+3];
QByteArray bytearr;
for(int i=0;i<size+2;i++) {
input[i] = (char) package[i];
bytearr.append((unsigned char) package[i]);
}
QString serialPortName = "COM3";
QSerialPort serialPort;
serialPort.setPortName(serialPortName);
serialPort.open(QIODevice::ReadWrite);
serialPort.write(bytearr);
serialPort.flush();
serialPort.close();
After running read everything works, but without read all, it wont work. What are we doing wrong?
Thanks.
We had similar problem in our application with a board with FTDI chip. We tried to write bytes with 19200 baud/sec, had though in real about 1200 baud/sec (seen using oscilloscope). The problem was closing the serial port right after writing a byte. Just waiting using QThread::msleep(5) before closing the port helped. It seems, that the device gets a reset or something during close operation and latest bytes are sent with false baudrate and other parameters.
I found out that the QT serial port SW requires you to process QT events in order to work.
Putting a qApp->processEvents() in the loop before the read made it work for me.
(QT 4.8.5 on Windows-7)