簡體   English   中英

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類中使用以下信號和插槽:

#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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM