pySerial从AT命令读取数据

时间:2014-06-22 23:06:40

标签: python serial-port raspberry-pi pyserial obd-ii

我无法通过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 :(得分: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: specify end-of-line in readline()

同样的问题困扰着所有终端计划。对于pySerial miniterm,请参阅其文档(命令行选项--cr)。

如果有超时,他们可以而且应该更长时间用于调试目的。一秒钟的超时可能会更改为十秒超时,以确保其他设备有足够的时间来应答。

答案 1 :(得分:0)

我有完全相同的问题,通过Python 2 IDLE在IDLE屏幕上没有显示结果,但结果被重定向到终端上的picocom活动。我需要捕获结果,因为我的目标是读取传入的SMS。 以下代码解决了我的问题,我还不知道原因,正在进行分析。

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()