我正在编写一个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
中找到答案 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。