读取串口时出现编码问题(我认为)
我正在尝试通过P1接口读取我的智能电表。网上有一些Python脚本可以做到这一点,我试过了,但从我的串口接口中得到的输出似乎有些杂乱(不是完全的杂乱,所以我猜测串口设置是正确的。此外,我是从其他看起来正常工作的脚本中复制的)。
当我使用cu命令尝试读取接口时,它确实显示了正确的输出,所以我的硬件似乎是正常的。
我运行的系统是:Linux版本3.10.25+ (dc4@dc4-arm-01) (gcc版本4.7.2 20120731 (预发布) (crosstool-NG linaro-1.13.1+bzr2458 - Linaro GCC 2012.08) ) #622 PREEMPT 2014年1月3日星期五 18:41:00 GMT
这是我期望的输出样子 - 通过以下命令获取:
命令:
cu -l /dev/ttyUSB0 -s 9600 --parity=none
输出
/INTENTIONALLY ALTERED
0-0:96.1.1(39373936353532302020202020202020)
1-0:1.8.1(05090.742*kWh)
1-0:1.8.2(06618.743*kWh)
1-0:2.8.1(00000.000*kWh)
1-0:2.8.2(00000.000*kWh)
0-0:96.14.0(0001)
1-0:1.7.0(0000.71*kW)
1-0:2.7.0(0000.00*kW)
0-0:17.0.0(999*A)
0-0:96.3.10(1)
0-0:96.13.1()
0-0:96.13.0()
0-1:96.1.0(3238303131303038323033333632313132)
0-1:24.1.0(03)
0-1:24.3.0(140806220000)(2C)(60)(1)(0-1:24.2.0)(m3)
(03447.404)
0-1:24.4.0(1)
!
当我使用以下Python代码:
# DSMR P1 uitlezen
# (c) 10-2012 - GJ - gratis te kopieren en te plakken
versie = "1.0"
import sys
import serial
##############################################################################
#Main program
##############################################################################
print ("DSMR P1 uitlezen", versie)
print ("Control-C om te stoppen")
print ("Pas eventueel de waarde ser.port aan in het python script")
#Set COM port config
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"
#Open COM port
try:
ser.open()
except:
sys.exit ("Fout bij het openen van %s. Aaaaarch." % ser.name)
#Initialize
#p1_teller is mijn tellertje voor van 0 tot 20 te tellen
p1_teller=0
while p1_teller < 20:
p1_line=''
#Read 1 line van de seriele poort
try:
p1_raw = ser.readline()
print str(p1_teller),':', p1_raw
except:
sys.exit ("Seriele poort %s kan niet gelezen worden. Aaaaaaaaarch." % ser.name )
#p1_str=str(p1_raw)
#p1_line=p1_str.strip()
# als je alles wil zien moet je de volgende line uncommenten
#print (p1_line.encode('ascii','ignore'))
p1_teller = p1_teller +1
#Close port and show status
try:
ser.close()
except:
sys.exit ("Oops %s. Programma afgebroken. Kon de seriele poort niet sluiten." % ser.name )
输出变成了这样:
('DSMR P1 uitlezen', '1.0')
Control-C om te stoppen
Pas eventueel de waarde ser.port aan in het python script
0 : INTENTIONALLY ALTERED BUT ALSO WITH THE ? SYMBOLS IN THE ORIGINAL OUTPUT
1 : �
2 : 0-0:96.�.�(393�393635353�30�0�0�0�0�0�0�0�0��
3 : �-0:�.�.�(05090.�9����詍
4 : �-0:�.�.�(066��.��3���詍
5 : �-0:�.�.�(00000.000���詍
6 : �-0:�.�.�(00000.000���詍
7 : 0-0:96.��.0(000���
8 : �-0:�.�.0(0000.�0��ש�
9 : �-0:�.�.0(0000.00��ש�
10 : 0-0:��.0.0(999�A��
11 : 0-0:96.3.�0(���
12 : 0-0:96.�3.�(��
13 : 0-0:96.�3.0(��
14 : 0-�:96.�.0(3�3�303�3�30303�3�303333363�3�3�3���
15 : 0-�:��.�.0(03��
16 : 0-�:��.3.0(��0�06��0000�(�é(60�(��(0-�:��.�.0�(�3��
17 : (03���.�0���
18 : 0-�:��.�.0(���
19 : !�
所以在我的Python输出中出现了很多�字符,我猜这可能是编码问题……但我不太确定,也不知道该怎么解决……所以任何帮助都很感激。
5 个回答
感谢Geoffrey,我终于弄明白了如何设置我的C#串口。
private static SerialPort CreateP1SerialPort(string name)
{
return new SerialPort
{
BaudRate = 115200,
DataBits = 8,
Handshake = Handshake.XOnXOff,
Parity = Parity.None,
PortName = name,
RtsEnable = false,
StopBits = StopBits.One,
ReadTimeout = 10 * 1000,
WriteTimeout = 1000
};
}
这是一个简单的例子:
public static void Main()
{
// this will help you find your com port name
var portNames = SerialPort.GetPortNames().OrderBy(x => x).ToArray();
// Create a new SerialPort object with default settings.
var serialPort = CreateP1SerialPort("COM7");
serialPort.Open();
while (true)
{
var line = serialPort.ReadExisting();
if (string.IsNullOrEmpty(line))
{
Thread.Sleep(10000); // one message every 10 seconds
} else
Console.WriteLine(line);
}
}
在代码中,把字节大小改成EIGHTBITS,把奇偶校验改成PARITY_NONE。这些设置和cu使用的一样(也是PySerial的默认设置)。
这是我最后解决这个问题的方法 - 我也会试试Ldevries的方法……看起来挺简单的:
#
# MBSolget P1 Telegram Catch
#
version = "v1.00"
import sys
import os
import stat
import serial
import datetime
import locale
###############################################################################################################
# Main program
###############################################################################################################
#Initialize
p1_telegram = False
p1_timestamp = ""
p1_teller = 0
p1_log = True
#Set COM port config
ser = serial.Serial()
ser.baudrate = 9600
ser.bytesize = serial.EIGHTBITS
ser.parity = serial.PARITY_NONE
ser.stopbits = serial.STOPBITS_ONE
ser.xonxoff = 1
ser.rtscts = 0
ser.timeout = 30
ser.port = "/dev/ttyUSB0"
#Show startup arguments
print ("MBSolget P1 Telegram Catch %s" % version)
print ("Control-C om af te breken")
print ("Poort: (%s)" % (ser.name) )
#Open COM port
try:
ser.open()
except:
sys.exit ("Fout bij het openen van poort %s. " % ser.name)
while p1_log:
p1_line = ''
try:
p1_raw = ser.readline()
except:
sys.exit ("Fout bij het lezen van poort %s. " % ser.name )
ser.close()
p1_raw = ''.join(chr(ch & 0x7f) for ch in p1_raw)
# for ch in p1_raw:
# print(chr(ch & 0x7f))
# print( chr(ord(ch) & 0x7f))
# print(p1_raw)
p1_str = p1_raw #str(p1_raw ,"utf-8",errors="ignore")
p1_line = p1_str.strip()
print (p1_line)
if p1_line[0:1] == "/":
p1_telegram = True
p1_teller = p1_teller + 1
f=open("/home/geoffrey/p1_temp.log", "w")
elif p1_line[0:1] == "!":
if p1_telegram:
p1_teller = 0
p1_telegram = False
p1_log = False
f.write (p1_line)
f.write ('\r\n')
f.close()
os.chmod("/home/geoffrey/p1_temp.log", stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)
if p1_telegram:
f.write (p1_line)
f.write ('\r\n')
#Close port and show status
try:
ser.close()
except:
sys.exit ("Fout bij het sluiten van %s. Programma afgebroken." % ser.name )
我想这跟数据解码有关。使用TextIOWrapper时,解码对我来说很顺利。希望我的看法能对你有所帮助:
为了让读取p1协议变得尽可能简单,我建议使用TextIOWrapper,就像我之前提到的那样,这样你仍然可以用readline方法读取串口数据。“!”总是标志着P1电报的结束,所以可以用它来检测消息的结束,而不是用计数器。当完整的电报接收完毕后,就可以进行处理。举个例子:
import io
import serial
serialport = serial.Serial( # Configure Serial communication port
port = "/dev/ttyUSB0"
baudrate = 9600,
timeout = 11,
bytesize = serial.SEVENBITS,
parity = serial.PARITY_EVEN,
stopbits = serial.STOPBITS_ONE )
p1port = io.TextIOWrapper(io.BufferedReader(serialport, buffer_size=1), newline='\n', encoding='ascii')
P1Message = []
while True:
try:
rawline = 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)
祝你好运!
看起来你读取的字节可能还包含了校验位,这让它们变成了无效字符。试试下面的方法来去掉第八位:
p1_raw = ''.join(chr(ord(ch) & 0x7f) for ch in p1_raw)