QSerialPort 有字节可用但无法读取
QSerialPort has bytes available but can't read
我正在编写一个 Qt GUI 应用程序,它通过串行接收数据并将数据发送到 Arduino。它写入正确,但在尝试读取时它不起作用。
我的问题是:
我有一个 Arduino 代码可以通过串口发送东西,我将它编程为根据接收到的数据打开和关闭引脚 13 上的 LED:
#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 = "";
}
}
}
我在 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";
}
}
以及前一个槽调用的Arduino::Read()方法:
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;
}
写入 Arduino 时运行良好,LED 会正常变化,问题发生在 Qt 尝试读取回复的数据时。
在大多数情况下,Arduino 会发送一些信号,并且 serialPort->bytesAvailable() returns 一个不为零的数字,但是
serialPort->waitForReadyRead(20) 超时没有收到任何东西,serialPort->readAll() returns 一个空的 QString。如果我使用 serialPort->waitForReadyRead(-1),window 会冻结。
最奇怪的是,在一个随机发送的时刻,一切正常,函数returns所有以前无法读取的数据。
下面是一个例子:
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
第二次尝试(失败)
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
第三次尝试(这是一次一切正常的随机尝试)
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.
第四次尝试(失败)
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
第 5 次尝试(这是又一次一切正常的随机尝试)
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.
我使用 Arduino IDE 串口监视器进行了测试,它按预期工作:我写了“CHANGELED”,然后 LED 变了,Arduino 回复了“LEDON”或“LEDOFF”所有次。
我该怎么做才能解决这个问题?
谢谢
编辑:完整代码可见here
这听起来像是一个缓冲问题。您的 Qt 应用程序接收字节,但没有任何信息告诉它接收已完成。我会在每个字符串后添加换行符 (\n
) 作为终止符,只需在 Qt 中使用 Serial.println()
instead of Serial.write()
. Then you can use readLine()
并始终确保您得到的是一个完整的命令字符串。
如果你在 Linux 你也可以尝试 stty your device to raw
和
stty -F /dev/ttyACM0 raw
直接发送串行字符而不是默认行缓冲(参见 What’s the difference between a “raw” and a “cooked” device driver?)。
请注意,您可能会遇到一个新问题:您的 Qt 应用程序可能非常快,以至于您在回调中一次只收到一封信。
Read
方法不正确且不必要。你不应该使用 waitFor
方法。在处理 QByteArray
可以很好处理的简单 ASCII 数据时,您也不应该使用 QString
:
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());
}
交易很简单:ReadData
可以用任意数量的可用字节调用 - 甚至是单个字节。因此,您必须积累数据,直到收到完整的 "packet"。在您的情况下,只能通过匹配完整的响应字符串来描述数据包。
如果 Arduino 发送完整的行,你会让你的生活更轻松——用 Serial.println
替换 Serial.write
。那么线路就是数据包,不需要自己缓冲数据:
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";
}
}
看到了吗?这样就简单多了。
您也可以在没有 运行 真实 Arduino 硬件的情况下将 Qt 用于 "simulate" Arduino 代码,请参阅 示例。
我正在编写一个 Qt GUI 应用程序,它通过串行接收数据并将数据发送到 Arduino。它写入正确,但在尝试读取时它不起作用。
我的问题是:
我有一个 Arduino 代码可以通过串口发送东西,我将它编程为根据接收到的数据打开和关闭引脚 13 上的 LED:
#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 = "";
}
}
}
我在 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";
}
}
以及前一个槽调用的Arduino::Read()方法:
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;
}
写入 Arduino 时运行良好,LED 会正常变化,问题发生在 Qt 尝试读取回复的数据时。
在大多数情况下,Arduino 会发送一些信号,并且 serialPort->bytesAvailable() returns 一个不为零的数字,但是 serialPort->waitForReadyRead(20) 超时没有收到任何东西,serialPort->readAll() returns 一个空的 QString。如果我使用 serialPort->waitForReadyRead(-1),window 会冻结。
最奇怪的是,在一个随机发送的时刻,一切正常,函数returns所有以前无法读取的数据。
下面是一个例子:
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
第二次尝试(失败)
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
第三次尝试(这是一次一切正常的随机尝试)
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.
第四次尝试(失败)
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
第 5 次尝试(这是又一次一切正常的随机尝试)
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.
我使用 Arduino IDE 串口监视器进行了测试,它按预期工作:我写了“CHANGELED”,然后 LED 变了,Arduino 回复了“LEDON”或“LEDOFF”所有次。
我该怎么做才能解决这个问题? 谢谢
编辑:完整代码可见here
这听起来像是一个缓冲问题。您的 Qt 应用程序接收字节,但没有任何信息告诉它接收已完成。我会在每个字符串后添加换行符 (\n
) 作为终止符,只需在 Qt 中使用 Serial.println()
instead of Serial.write()
. Then you can use readLine()
并始终确保您得到的是一个完整的命令字符串。
如果你在 Linux 你也可以尝试 stty your device to raw
和
stty -F /dev/ttyACM0 raw
直接发送串行字符而不是默认行缓冲(参见 What’s the difference between a “raw” and a “cooked” device driver?)。
请注意,您可能会遇到一个新问题:您的 Qt 应用程序可能非常快,以至于您在回调中一次只收到一封信。
Read
方法不正确且不必要。你不应该使用 waitFor
方法。在处理 QByteArray
可以很好处理的简单 ASCII 数据时,您也不应该使用 QString
:
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());
}
交易很简单:ReadData
可以用任意数量的可用字节调用 - 甚至是单个字节。因此,您必须积累数据,直到收到完整的 "packet"。在您的情况下,只能通过匹配完整的响应字符串来描述数据包。
如果 Arduino 发送完整的行,你会让你的生活更轻松——用 Serial.println
替换 Serial.write
。那么线路就是数据包,不需要自己缓冲数据:
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";
}
}
看到了吗?这样就简单多了。
您也可以在没有 运行 真实 Arduino 硬件的情况下将 Qt 用于 "simulate" Arduino 代码,请参阅