PySerial无法与Arduino通信

8

我编写了一些代码来模拟我正在使用的硬件,并将其上传到Arduino板。这段代码是有效的。我知道这一点,因为我从HyperTerminal得到了预期的响应。

然而,当我尝试使用PySerial连接时,连接不会出错,但我没有收到发送的命令的任何响应。

这是为什么呢?

Python代码

import serial

def main():
    sp = serial.Serial()
    sp.port = 'COM4'
    sp.baudrate = 19200
    sp.parity = serial.PARITY_NONE
    sp.bytesize = serial.EIGHTBITS
    sp.stopbits = serial.STOPBITS_ONE
    sp.timeout = 0.5
    sp.xonxoff = False
    sp.rtscts = False
    sp.dsrdtr = False

    sp.open()

    sp.write("GV\r\n".encode('ascii'))
    value = sp.readline()
    print value
    sp.write("GI\r\n".encode('ascii'))
    value = sp.readline()
    print value

    sp.close()
 
if __name__ == "__main__":
    main()

注意:Arduino上的代码在响应命令结束时会返回\r\n
HyperTerminal配置: COM4 configuration in HyperTerminal 编辑:
我发现如果将超时时间增加到10秒,并在发送任何内容之前添加sp.readline(),那么我将得到两个命令的响应。
PySerial和Arduino或USB RS-232端口之间通常需要多长时间进行硬件握手?

这里关于向REPL(命令处理器)发送命令的一个小注释。我注意到你发送的命令以\r\n结尾。虽然常规文本行通常以\r\n结尾,但是发送的命令通常只以单个字符\r结尾,就像人类用户输入的一样。如果发送了\n,它可能会被解释为下一个命令的开始,并且可能会挂起或触发其他不良结果。 - RufusVS
5个回答

5

无法确认,但可能是您在有数据之前尝试读取,因此您没有收到回复。

为测试此问题,您可以尝试轮询直到有数据可用。

value = None
while not value:
   value = sp.readline()
print value

编辑

当您打开串行连接时,Arduino将会重置,任何在启动期间写入的数据可能会丢失。在进行任何读/写操作之前,您可以使用2秒的休眠时间(无法找到确切的所需时间,可能会有所变化)。

或者,您可以不断向其写入数据,直到收到回复后才开始进行“真正的工作”。


不幸的是,readline 在超时后返回一个空字符串。这对我来说行不通。我尝试等待非空字符串,但结果只是导致无限循环。 - Matt Ellen
太糟糕了,你尝试过进行基本的Arduino测试,只需不断发送Serial.println以查看是否能够获得任何输出吗? - ib.lundgren
是的,我可以得到输出。最初只能通过HyperTerminal获得它,但我发现问题是连接和能够发送/接收数据之间存在延迟。我已经更新了问题。感谢你目前的帮助。 - Matt Ellen

3

目前我正在使用一个解决方法。我将 timeout 设置为 1.5 秒,并在第一次写入之前加入了一个 readline 调用。

因此,现在 Python 代码如下:

import serial

def main():
    sp = serial.Serial()
    sp.port = 'COM4'
    sp.baudrate = 19200
    sp.parity = serial.PARITY_NONE
    sp.bytesize = serial.EIGHTBITS
    sp.stopbits = serial.STOPBITS_ONE
    sp.timeout = 1.5 #1.5 to give the hardware handshake time to happen
    sp.xonxoff = False
    sp.rtscts = False
    sp.dsrdtr = False

    sp.open()

    sp.readline() #to give the hardware handshake time to happen

    sp.write("GV\r\n".encode('ascii'))
    value = sp.readline()
    print value
    sp.write("GI\r\n".encode('ascii'))
    value = sp.readline()
    print value

    sp.close()

if __name__ == "__main__":
    main()

1
我不明白为什么需要这样做,但在write前面添加一个readline()对我也起作用了。 - wizofwor
1
@wizofwor 这并非必需,只是一种方便的方式,让软件等待硬件准备就绪。 - Matt Ellen

2
在打开端口后,添加一个延迟,因为Arduino正在重置并开始监听新的固件。如果此时发送了任何内容,MCU将保持在引导加载程序中无法退出。延迟会使引导加载程序超时。
sp.open()
time.sleep(2) # 2 seconds or possibly a bit less
sp.write("blahblah")

2

我最近也遇到了这个问题,这是我的解决方案:

import serial

ser = serial.Serial(4, timeout=2)
ser.setRTS(True)
ser.setRTS(False)
while 1:
    line = ser.readline()
    print(line)
ser.close

原来这将成功重置Arduino板。

0

通过使用兼容库SerialTransfer.hpySerialTransfer,您可以轻松而稳健地连接和通信Python与Arduino板之间。这些库会自动对串行数据包进行分组和解析,使用起始/结束标记、循环冗余校验、一致的开销字节填充和动态负载长度。

SerialTransfer.h可通过Arduino IDE的库管理器进行安装,而pySerialTrasnfer可以使用pip进行安装。

Python示例:

from pySerialTransfer import pySerialTransfer as txfer

if __name__ == '__main__':
    try:
        link = txfer.SerialTransfer('COM13')

        link.txBuff[0] = 'h'
        link.txBuff[1] = 'i'
        link.txBuff[2] = '\n'

        link.send(3)

        while not link.available():
            if link.status < 0:
                print('ERROR: {}'.format(link.status))

        print('Response received:')

        response = ''
        for index in range(link.bytesRead):
            response += chr(link.rxBuff[index])

        print(response)
        link.close()

    except KeyboardInterrupt:
        link.close()

Arduino示例:

#include "SerialTransfer.h"
#include <SoftwareSerial.h>


SoftwareSerial mySerial(2, 3); // RX, TX
SerialTransfer myTransfer;


void setup()
{
  Serial.begin(115200);
  mySerial.begin(9600);
  myTransfer.begin(mySerial);
}

void loop()
{
  myTransfer.txBuff[0] = 'h';
  myTransfer.txBuff[1] = 'i';
  myTransfer.txBuff[2] = '\n';

  myTransfer.sendData(3);
  delay(100);

  if(myTransfer.available())
  {
    Serial.println("New Data");
    for(byte i = 0; i < myTransfer.bytesRead; i++)
      Serial.write(myTransfer.rxBuff[i]);
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");
    Serial.println(myTransfer.status);
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接