【问题标题】:QSerialPort has bytes available but can't readQSerialPort 有可用字节但无法读取
【发布时间】:2019-01-18 03:20:57
【问题描述】:

我正在编写一个 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

第 5 次尝试(这是另一个随机尝试,一切正常)

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”。

我可以做些什么来解决这个问题? 谢谢

编辑:完整代码可见here

【问题讨论】:

  • 为什么在serialPort-&gt;waitForReadyRead(20) 中有20
  • bool QSerialPort::waitForReadyRead(int msecs = 30000):此函数阻塞,直到有新数据可供读取并且已发出 readyRead() 信号。该函数将在 mssecs 毫秒后超时;默认超时为 30000 毫秒。如果 msecs 为 -1,则此函数不会超时。如果发出了 readyRead() 信号并且有新数据可供读取,则该函数返回 true;否则返回 false(如果发生错误或操作超时)。
  • 好的,你可以提供一个minimal reproducible example,你的代码应该是最少的但也是完整的
  • 对不起,我会尽快编辑并放一个完整的代码
  • 请提供更多代码。

标签: c++ qt arduino qt5 qtserialport


【解决方案1】:

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";
  }
}

看到了吗?这样就简单多了。

您还可以利用 Qt 来“模拟”Arduino 代码,而无需运行真正的 Arduino 硬件,请参阅 this answer 以获取示例。

【讨论】:

    【解决方案2】:

    这听起来像是一个缓冲问题。您的 Qt 应用程序接收到字节,但没有任何信息告诉它接收已完成。我会在每个字符串之后添加换行符(\n)作为终止符,只需使用Serial.println() 而不是Serial.write()。然后你可以在 Qt 中使用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 应用程序可能非常快,以至于您在回调中一次只能收到一封信。

    【讨论】:

    • 我对此进行了测试,但没有成功。我会搜索更多关于如何告诉Qt接收完成,也许问题在于
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多