1

我是 Python 新手,想使用 Raspberry Pi 和 Python 读取我的智能电表 P1 端口。问题:输入看起来像某些组件被喝醉了。我敢肯定它很容易修复,但经过几个小时的搜索和尝试,不得不寻求帮助。

当用 CU 等读取 P1 端口时,一切都很好,所以硬件等也正常。使用 dx.com 的串口转 USB 转换器(这个

命令和(部分)输出:cu -l /dev/ttyUSB0 -s 9600 --parity=none

0-0:96.1.1(205A414246303031363631323463949271)
1-0:1.8.1(03118.000*kWh)

但是,当尝试从 Python 中读取它时,输入会变得乱七八糟(但至少是一致的):

0-0:96.±.±(²05A´±´²´630303±39363±3²3´639·3±3²©
±-0:±.¸.±(03±±¸.000ªë×è©

如何解决这个问题?我正在使用的代码是:

import serial

ser = serial.Serial()
ser.baudrate = 9600
ser.bytesize=serial.SEVENBITS
ser.parity=serial.PARITY_EVEN
ser.stopbits=serial.STOPBITS_ONE
ser.xonxoff=0
ser.rtscts=0
ser.timeout=20
ser.port="/dev/ttyUSB0"

ser.close()
ser.open()
print ("Waiting for P1 output on "  + ser.portstr)

counter=0
#read 20 lines    
while counter < 20:
    print ser.readline()
    counter=counter+1

try:
    ser.close()
    print ("Closed serial port.")
except:
    sys.exit ("Couldn't close serial port.")

已经尝试过弄乱波特率等,但这没有任何区别。

4

5 回答 5

2

我对该serial模块不是很熟悉,但我注意到您的cu命令假定没有奇偶校验位 ( --parity=none),但您的 python 脚本假定存在偶校验位 ( ser.parity=serial.PARITY_EVEN)。我会尝试

ser.parity=serial.PARITY_NONE

如果没有奇偶校验位,您可能还想要

ser.bytesize=serial.EIGHTBITS
于 2013-09-25T09:56:55.393 回答
0

更新:通过替换顽皮的字符找到了解决方法。这可能适用于其他有同样问题的人,但我不知道坏字符是否完全相同。因此,更换部件可能需要一些工作才能使其适用于其他部件。

这不完全是一个解决方案,因为传入的电报仍然一团糟,但下面的代码可以解决这个问题。我的电报现在完全干净了。

我现在使用的代码的相关部分:

#Define 2 variables
P1_numbers = {'±':'1', '²':'2', '´':'4', '·':'7', '¸':'8'}
P1_rest    = {'¯':'/', 'ª':'*', '©':')', 'Æ':'F', 'ë':'k', '×':'W', 'è':'h', 'í':'m'}

# Define function to read the telegram. Calls a function to clean it.
def P1_read(stack):
    counter = 0
    while counter < TelegramLength:
        stack.append(P1_clean(ser.readline()))
        counter=counter+1
    return stack

# Define function to clean up P1 output
def P1_clean(line):
    for i, j in P1_numbers.iteritems():
        line = line.replace(i, j)
    for i, j in P1_rest.iteritems():
        line = line.replace(i, j)
    return line
于 2013-10-17T12:23:37.487 回答
0

我想你有一个 P1 协议的智能电表:DSMR 3.0?然后这些是您已经拥有的正确串行端口设置:

serialport = serial.Serial(  # Configure Serial communication port
                            baudrate = 9600,
                            timeout  = 11,
                            bytesize = serial.SEVENBITS,
                            parity   = serial.PARITY_EVEN,
                            stopbits = serial.STOPBITS_ONE )

您可能对数据的某些编码或解释出错了。这是读取智能电表的另一种方法:

为了使 p1 协议的读取尽可能简单,我建议使用 TextIOWrapper,这样您就可以使用 readline 方法读取串行端口。这 ”!” 总是结束 P1 电报,因此可以用来检测消息的结束。收到完整的电报后,即可处理该电报。例子:

import io
p1port = io.TextIOWrapper(io.BufferedReader(serialport, buffer_size=1), newline='\n', encoding='ascii')

P1Message = []

while True:
    try:
         rawline = self.p1port.readline()
    except UnicodeDecodeError:
         print "Encode error on readline"

    if '!' in rawline:
        # Process your P1Message here

        P1Message = [] # Clear message, wait for new one
    else:
        P1Message.append(rawline)
于 2014-08-13T12:02:05.090 回答
0

OP 已经消失了,但这个问题具有足够的普遍意义,所以这里有一个新的答案。用户@Brionius 是对的:查看所涉及的位模式表明这绝对是一个奇偶校验问题。以下是如何检查字符的位模式"1""±"

>>> "{0:b}".format(ord("1"))
'110001'
>>> "{0:b}".format(ord("±"))
'10110001'

得到它?由于打开了高(第 8 位)位,字符会被破坏。或者你可以通过设置 ascii 的高位来看到这一点"1"

>>> chr(ord("1") | 0b10000000)
'±'

现在,设置了三个位(奇校验),"1", "2"并且"4"已损坏。"0", "3", "5",等具有偶校验(2 或 4 位设置),并被保留。所以通信信道使用的是偶校验,在接收端没有正确解码。

于 2016-01-06T19:49:05.320 回答
0

在 P1 Smart Meter 端口的情况下,我也遇到了同样的问题,我花了很长时间才找到它。

'cu' 正确显示了数据,但 Python 没有(其他一些程序也没有)。显然奇偶校验位在某种程度上没有被正确处理。下面解决了这个问题:

p1_raw = ser.readline()
print 'raw:', p1_raw

decoded = ''.join(chr(ord(ch) & 0x7f) for ch in p1_raw)
print 'decoded:', decoded

我仍然觉得发生这种情况很奇怪,因为这实际上是在我第二次尝试读取智能仪表的输出时发生的。几年来,我已经有一个脚本成功地监控了另一所房子的另一个智能电表,但我从未遇到过这个问题。

也许导致这种情况的 USB 串行适配器存在细微差别?!?

于 2016-03-20T17:15:20.160 回答