QSerialPort有可用字节,但无法读取

时间:2018-08-10 13:30:44

标签: c++ qt arduino qt5 qtserialport

我正在编写一个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”,这是所有无法预先读取的内容。

  
     

第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从Arduino读取“ LEDOFFLEDON”,这是所有无法预先读取的内容。

  

我使用Arduino IDE串行监视器进行了测试,并且按预期的方式工作:我写了“ CHANGELED”,然后LED改变了,Arduino一直都回答“ LEDON”或“ LEDOFF”。

我该怎么做才能解决此问题? 谢谢

编辑:完整的代码可以在here

中找到

2 个答案:

答案 0 :(得分:0)

这听起来像一个缓冲问题。您的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应用程序可能是如此之快,以至于您在回调中一次只收到一个字母。

答案 1 :(得分:0)

Read方法不正确且不必要。您永远不要使用waitFor方法。在处理QString处理良好的简单ASCII数据时,您也不应该使用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.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代码,有关示例,请参见this answer