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 代码,请参阅 示例。