为什么 QtSerialPort 在运行不止一次或两次后不会读取?

问题描述

我只是想从一个 QtSerialPort 连接读取值到一个 arduino,在一个值被用来表示开始之后。我可以在 python 代码中设置最大计数,但这不会改变它在多次运行 python 脚本后不从 arduino 读取 println 的结果。起初它会打印出“开始”、“阅读”和“V”,如果我很快再次运行它,也许会再次打印出来。但在那之后,它在打印出“开始”后停止读取。这是python代码

from PyQt5 import QtCore,QtSerialPort
import sys 

app = QtCore.QCoreApplication([]) 

serial_port = QtSerialPort.QSerialPort('COM3') 

serial_port.open(QtCore.qiodevice.ReadWrite) 

def handle_ready_read(): 
    print('to read')
    while serial_port.canReadLine():
        serialrd = serial_port.readLine().data().decode().strip()
        print(serialrd)
        serial_port.close()
        app.quit() 

serial_port.readyRead.connect(handle_ready_read) 

serial_port.write(bytes([100])) 
print('start')
sys.exit(app.exec_())

这是 arduino 代码

int measured;
int counter = 0;
int maxnum = 10;

void setup()
{
Serial.begin(9600);
}

void loop()
{
  if(Serial.available() > 0)
  {
    while(counter < maxnum)
    {

        Serial.println("V");
        counter++;
        delay(100);

    }
  }
}

任何可以解释为什么不再阅读的想法都会很有用。我每次都必须拔下并重新插入 arduino 才能读取它,但最好避免这样做。我还注意到我无法在 pyserial 连接后运行 PyQt5 串行端口连接,首先断开 arduino,但我可以在 PyQt5 串行端口连接后运行 pyserial,尽管我不知道该问题是否有某种关系。

编辑:我在handle_ready_read中添加了'to read'打印语句,它在第一次运行时有时会打印两次,所以虽然有一些东西要读,但它并不总是能被readline读取,所以我添加了到 handle_ready_read 结束吧,

    while serial_port.bytesAvailable(): 
        dataByte = serial_port.readLineData(1)
        print(dataByte)
        serial_port.close()
        app.quit()

因此有时会在读取时打印出 b'V' 而不仅仅是 V。但是尝试一两次后仍然无法读取。

编辑 2:我添加一个计时器和函数来检查是否写入了字节,

attempts = []
def check_bytes_written():    
    print('bytes to write',serial_port.bytesToWrite())
    attempts.append(1)
    if len(attempts) > 10:
        serial_port.close()
        timer.stop()
        app.quit()

timer = QtCore.QTimer()
timer.setInterval(50) 
timer.timeout.connect(check_bytes_written)
timer.start()

这在第一次或第二次尝试时不会打印出任何内容,然后在以后的尝试中说“要写入 0 的字节数”,即使端口和应用程序已结束,仍然没有打印出任何形式的“V”,除非我仍然断开 arduino 并将其重新插入(尽管现在至少不需要每次都重新启动内核)。我还在 arduino 中添加代码以检查写入的值是否可用于更改输出引脚的电压,它可以工作并且每次运行 python 脚本时都会更改输出电压,无论是否读取了“V” .所以我稍微更改了问题标题和之前的文字,因为它并没有真正崩溃或冻结,只是没有读取 arduino 正在打印的内容

编辑 3:我将 if(Serial.available() > 0) { int counter = 0; 放在 arduino 代码中,而不是在开始时初始化计数器,python 代码将在重新运行脚本时从 arduino 中读取(尽管 V 周围有一堆空格,或者只是从 readlineData 部分打印 b'\x00' ,但这可能是另一个问题,可以通过在 readyRead 之前放置 serial_port.clear() 来清除,但仍然打印 V 或 b'V')。所以看起来python脚本没有重新启动arduino代码,也许这是qserialport如何关闭或打开(如DTR或RTS)的问题,与pyserial不同?更多讨论here

编辑 4:如果我添加(在 arduino 代码的开头初始化计数器时,如最初所示,撤消上次编辑)

serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)

serial_port.open 语句并运行它之后,它仍然不会从串行读取中打印(没有断开 arduino 并先重新连接它),但是如果我评论 DTR 设置,那么 {{ 1}} 再次运行时按预期打印出来,不需要断开连接,但随后又回到不打印 V。仅使用 V 会产生相同的效果,每隔一次对其进行注释输入/输出

编辑 5:我发现(一旦它至少正常运行一次,首先拔掉电源或使用编辑 4 中的方法),如果我把

serial_port.setDataTerminalReady(1)

就在 serial_port.setDataTerminalReady(0) serial_port.setDataTerminalReady(1) serial_port.setDataTerminalReady(0) 之前,它可以再次运行多次并打印出“V”。我猜是将 DTR 重置为 0。如果有解释为什么需要发生这种情况,那就太好了。

解决方法

您的函数逻辑缩进是否正确?

在我看来,您似乎只阅读了一行内容,然后立即关闭了串行端口并退出了您的应用程序。

也许你打算:

def handle_ready_read(): 
    while serial_port.canReadLine():
        serialrd = serial_port.readLine().data().decode().strip()
        print(serialrd)
    # Close serial port and quit app when no more data is available
    serial_port.close()
    app.quit()