来自readline的'Drunk'输入,来自其他程序的OK(读取智能电表P1端口)

时间:2013-09-25 09:38:07

标签: python raspberry-pi readline pyserial

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

用CU等读取P1端口时一切正常,硬件等都可以。使用dx.com(this one

中的串口转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.")

已经尝试过使用波特率等但是没有任何区别。

5 个答案:

答案 0 :(得分:1)

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

ser.parity=serial.PARITY_NONE

如果没有奇偶校验位,你也可能想要

ser.bytesize=serial.EIGHTBITS

答案 1 :(得分: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

答案 2 :(得分: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方法读取串口。 &#34;!&#34;始终结束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)

答案 3 :(得分: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位),并保留。因此,通信信道使用偶校验,其在接收端未被正确解码。

答案 4 :(得分:0)

我也有同样的问题,也是在P1智能电表端口环境中,我花了很长时间才找到它。

'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-Serial适配器可能会产生一点差异???