pySerial 从AT命令读取数据

0 投票
2 回答
8139 浏览
提问于 2025-04-18 10:42

我在用pySerial读取RS232 OBD2接口的响应时遇到了问题。虽然代码能成功发送数据,我在一个直接的终端屏幕上可以看到,但它却无法读取和打印响应,不管响应是什么。

现在这段代码在两个版本的Python中都无法打印出响应。代码大概是这样的:

from serial import * # I also tried using /from serial import Serial
import time
ser = Serial("/dev/rfcomm1", 38400, timeout=1)
#print ('Starting up, formatting responses')
#ser.write("ATZ\r"),
#ser.write("ATSP0\r"),
#ser.write("ATS1\r"),
#ser.write("ATL1\r"),
#ser.write("ATH1\r"),
#ser.write("ATF1\r")
#time.sleep(1)
#print ('We have lift-off !')
if ser.inWaiting() > 0:
    ser.flushInput()
#ser.timeout = 1.
time.sleep(1)
#print (raw_data)
ser.write("AT RV\r") #The response should be something like 13.5V, but nothing
ser.timeout = 1.
msg = ser.read(size=1024)
print msg
ser.close()

我只留下了AT RV这个命令,因为在我调试的时候,我发送了一些文本格式化的命令来简化工作。目前当我发送这个命令时,它只给我一行空白(尽管在同一台机器上运行的终端显示了我想要的输出)。

代码没有错误,命令也能成功发送并得到接口的响应,我在另一个实时终端中可以看到这一点,但运行Python代码时却什么都不显示。我该怎么办呢?

2 个回答

0

我遇到了完全一样的问题。在Python 2的IDLE界面上没有显示任何结果,但结果却被重定向到了终端上正在运行的picocom。我需要捕捉这些结果,因为我的目标是读取收到的短信。下面的代码解决了我的问题,但我还不知道具体原因,正在继续分析中。

import time
import serial

modem1 = serial.Serial("/dev/ttyUSB3",baudrate=115200,timeout=0,rtscts=0,xonxoff=0)
def sendat1(cmd):
    if cmd == 'res' : modem1.write('Z'); return
    if cmd == 'out' : modem1.write(chr(26)); return
    modem1.write('AT+'+cmd+'\r')
    time.sleep(3)
    obu = str(modem1.inWaiting())
    msg = modem1.read(32798)
    print(obu+':\n'+msg)
    return

try:
    if modem1.inWaiting()>0: modem1.flushInput()
    sendat1('res')
    sendat1('CMGF=1')
    sendat1('CMGL')
    sendat1('out')
finally:
    modem1.close()
4

你应该在写完之后再去阅读,而不是在之前。

# before writing anything, ensure there is nothing in the buffer
if ser.inWaiting() > 0:
    ser.flushInput()

# set the timeout to something reasonable, e.g., 1 s
ser.timeout = 1.

# send the commands:
ser.write("ATZ\r")
# ...

# read the response, guess a length that is more than the message
msg = ser.read(1024)
print msg

# send more commands
# read more responses
# ...

这里的重点是,我们无法知道什么时候收到了响应。这个代码在每发送一个命令后会等待一秒钟,除非在这段时间内接收到超过1024字节的数据。虽然有更聪明的算法,但我们先试试这个。

如果你想在串口通信上做一些更复杂的事情,可以看看 pexpect 模块。


一些调试 Python 串口问题的想法

串口通信的问题有时候比较难解决。pySerial 是一个可靠的库,但由于不同平台有不同的串口 API,细节很多。随着物理串口的消失,USB 转换器又增加了一层复杂性,蓝牙转换器更是麻烦。

调试物理层的最好方法是使用一些监控硬件,连接两个串口到串口线上。这种嗅探器可以帮助我们确定问题出在连接的哪一端。不幸的是,这种嗅探器在需要的时候很少能找到。

下一个最好的办法是将串口线的 RD 和 TD(RXD, TXD)引脚短接。这样所有数据都会被回显。如果数据能按发送的方式接收到,说明物理连接是好的。需要注意的是握手。如果你不太了解,最好禁用所有流控制(xon/xoff, rts/cts, dtr/dsr)。如果没有特别指示,pySerial 会禁用这些。

在上面的问题中,物理连接是好的,因为另一款软件证明了数据被发送并被另一台设备理解。(看到数据被发送并不证明什么,因为那信息并没有经过物理层,但看到另一台设备产生的东西被接收,证明物理连接是好的。)

现在我们知道数据进入了操作系统,但 pySerial 却没有看到它。或者我们的代码可能还有问题(不应该是这样,但...)

让我们怀疑一下自己的代码,试试别人的代码。可以在命令提示符下运行:

python -m serial.tools.miniterm /dev/rfcomm1 38400

现在我们有了一个终端,可以用来手动发送/接收来自另一方的数据。如果这个行为可以重复(发送正常,数据进入系统但在终端上不显示),那么问题可能不在我们的代码里。

接下来的步骤是尝试:

sudo python -m serial.tools.miniterm /dev/rfcomm1 38400

原则上,访问权限问题会导致我们可以接收但无法发送的情况。但测试一下也无妨,因为奇怪的权限会导致奇怪的问题。

pySerial 有一个很方便的函数 readline,可以一次从串口读取一行。这通常是我们想要的。然而,在这个特定的情况下,行似乎以 \r 结尾,而不是 \n。在代码的其他地方也可能出现类似情况,所以对于特殊数据需要特别小心。(简单的“带超时读取”在这方面是安全但较慢的。)这个问题在这里讨论过:pySerial 2.6: 指定 readline() 的行结束符

同样的问题也困扰着所有终端程序。关于 pySerial 的 miniterm,可以查看它的文档(命令行选项 --cr)。

如果有超时,可以并且应该在调试时将其延长。比如将一秒的超时改为十秒,以确保另一台设备有足够的时间来回应。

撰写回答