[英]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類中使用以下信號和插槽:
#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()返回的數字不為零,但是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從Arduino讀取“ LEDONLEDOFFLEDON”,這是所有無法預先讀取的內容。
第四次嘗試(失敗)
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從Arduino讀取“ LEDOFFLEDON”,這是所有無法預先讀取的內容。
我使用Arduino IDE串行監視器進行了測試,它按預期的方式工作:我寫了“ CHANGELED”,然后LED改變了,Arduino一直都回答“ LEDON”或“ LEDOFF”。
我該怎么做才能解決這個問題? 謝謝
編輯:完整的代碼可以在這里找到
這聽起來像一個緩沖問題。 您的Qt應用程序接收到字節,但是沒有任何信息表明接收已完成。 我將在每個字符串后添加換行符( \\n
)作為終止符,只需使用Serial.println()
而不是Serial.write()
。 然后,您可以在Qt中使用readLine()
並始終確保所得到的正是一個完整的命令字符串。
如果你是在Linux上,你也可以嘗試到您的設備的stty到raw
與
stty -F /dev/ttyACM0 raw
使串行字符直接發送,而不是使用默認的行緩沖(請參見“原始”設備驅動程序和“成熟”設備驅動程序之間有什么區別? )。
請注意,然后您可能會遇到一個新問題:您的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
。 因此,您必須累積數據,直到收到完整的“數據包”為止。 在您的情況下,只能通過匹配完整的響應字符串來描繪數據包。
如果Arduino發送完整行,您的生活會變得更加輕松-用Serial.write
替換Serial.println
。 然后,這些行就是數據包,您不需要自己緩沖數據:
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來“模擬” Arduino代碼,請參見此答案以獲取示例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.