我是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.")
已经尝试过使用波特率等但是没有任何区别。
答案 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适配器可能会产生一点差异???