我正在编写一个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类中使用以下信号和插槽:
#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发送东西的大多数时候,信号都会被发出,并且串行端口->bytesAvailable()返回一个不为零的数字,但是serialPort->waitForReadyRead(20)超时而没有接收到任何东西并且serialPort->readAll()返回一个空的QString。如果我使用serialPort->waitForReadyRead(-1)窗口冻结。
最奇怪的是,在发送东西的随机时刻,一切都很好,函数返回了以前无法读取的所有数据。
下面是一个例子:
第一次尝试(失败)
Qt将PC_REQUESTED_LED_STATE_CHANGE(u8"CHANGELED")写入串行端口
Arduino LED改变其状态(打开)
Arduino将Arduino_TURNED_LED_ON("LEDON")写入串行端口
Qt从Arduino 读取一个空的QString
第二次尝试(失败)
Qt将PC_REQUESTED_LED_STATE_CHANGE(u8"CHANGELED")写入串行端口
Arduino LED改变其状态(关闭)
Arduino将Arduino_TURNED_LED_OFF("LEDOFF")写入串行端口
Qt从Arduino 读取一个空的QString
第三次尝试(这是一次随机尝试,一切正常)
Qt将PC_REQUESTED_LED_STATE_CHANGE(u8"CHANGELED")写入串行端口
Arduino LED改变其状态(打开)
Arduino将Arduino_TURNED_LED_ON("LEDON")写入串行端口
Qt读";LEDONLEDOFFLEDON";来自Arduino,这是以前无法阅读的所有内容。
第4次尝试(失败)
Qt将PC_REQUESTED_LED_STATE_CHANGE(u8"CHANGELED")写入串行端口
Arduino LED改变其状态(关闭)
Arduino将Arduino_TURNED_LED_OFF("LEDOFF")写入串行端口
Qt从Arduino 读取一个空的QString
第5次尝试(这是另一次随机尝试,一切正常)
Qt将PC_REQUESTED_LED_STATE_CHANGE(u8"CHANGELED")写入串行端口
Arduino LED改变其状态(打开)
Arduino将Arduino_TURNED_LED_ON("LEDON")写入串行端口
Qt读";LEDOFFLEDON";来自Arduino,这是以前无法阅读的所有内容。
我使用Arduino IDE串行监视器进行了测试,它按照预期工作:;CHANGELED";,则LED改变并且Arduino回答";LEDON";或";LEDOFF";一直以来。
我能做些什么来解决这个问题?感谢
编辑:完整的代码可以在这里找到
Read
方法不正确且不必要。您永远不应该使用waitFor
方法。在处理简单的ASCII数据时也不应该使用QString
,QByteArray
可以很好地处理这些数据:
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
可以用任意数量的可用字节调用,甚至是一个字节。因此,您必须积累数据,直到收到完整的"数据包"。在您的情况下,只能通过匹配完整的响应字符串来描述数据包。
如果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";
}
}
看到了吗?这样简单多了。
您还可以利用Qt来"模拟"Arduino代码,而无需运行真正的Arduino硬件,请参阅此答案以获取示例。
这听起来像是一个缓冲问题。您的Qt应用程序接收字节,但没有任何内容告诉它接收已完成。我会在每个字符串后面添加换行符(n
)作为终止符,只需使用Serial.println()
而不是Serial.write()
。然后,您可以在Qt中使用readLine()
,并始终确保您得到的正是一个完整的命令字符串。
如果你在Linux上,你也可以尝试用将你的设备连接到raw
stty -F /dev/ttyACM0 raw
直接发送串行字符,而不是默认的行缓冲(请参阅"原始"one_answers"熟"设备驱动程序之间的区别?)
请注意,然后您可能会遇到一个新问题:您的Qt应用程序可能太快,以至于在回调中一次只收到一封信。