I try to read a utility meter using python's serial module (and a USB to serial converter (FTDI) with an optical read head. It uses the protocol described in IEC 62056-21). The setup is known to work with other software.
After sending b'/?!\r\n' at 300bit/s the meter responds with Manufacturer, firmware version and a proposal for switching communication speed. So the program acknowledges the speed and requests the "data readout mode" <ack>050<CR><LF>
where 5 is the speed 9600. The program then switches baudrate.
Everything is fine until the baudrate switch. The normal behaviour is that the meter than sends a frame of data beginning with <STX>
and ending with <ETX>
and containing several lines of text ending with <CR><LF>
.
But about 9 lines of text are missing. The program catches up somewhere in the middle of the 9th or 10th line. Sometimes I get a long string of Null bytes instead.
Here's the code (I left some of the comments left over from testing):
import serial
import re
from time import sleep
def readframe():
buf=b''
while (buf == b'' or buf[-1] != b'\x03'):
buf+=ser.read(1)
print(buf)
return buf
ser =serial.Serial(port=PORT,baudrate=300,bytesize=serial.SEVENBITS,parity=serial.PARITY_EVEN,stopbits=serial.STOPBITS_ONE,timeout=5)
repeat=True
while repeat:
ser.setBaudrate(300)
sleep(1)
ser.write(b'/?!\r\n')
response=ser.readline()
print(response)
pattern = re.compile(r'/(...)(\d)\\@(.*)')
m=pattern.match(response.decode("ASCII"))
if not m:
repeat= True
else: repeat=False
manufacturer,speed,version=m.group(1,2,3)
speedcode=int(speed)
baudrates=(300,600,1200,2400,4800,9600,19200)
s=bytes([6,48,48+speedcode,48,13,10]) #option request data readout mode
print(s)
ser.write(s)
sleep(1)
ser.flush()
#ser.close()
#ser =serial.Serial(port=PORT,baudrate=baudrates[speedcode],bytesize=7,parity='E',stopbits=1,timeout=5)
ser.setBaudrate(9600)
#sleep(1)
#ser.setTimeout=None
frame=readframe()
ser.close()
I tried many things like flushing the buffer, closing and reopening with the different speed, inserting pauses, reading lines instead of bytes, using inWaiting() and maybe more. I suspect that for some time nothing gets buffered when switching baudrates, so I lose some data. BTW I use it on Windows, Winpython distribution (at my work place that's why) the version of the serial module is 2.7.
Below is an example how the communication should be (communication transcript with timings and direction partly in german). With my program I only receive from the line with 0.2.0 on:
Komm: 11:29:57,62 -- Teilnehmer-Name = Lokale Schnittstelle
Komm: 11:29:58,34 -- Schnittstelle = SERIELL über COM-Port Nr: 4
Komm: 11:29:58,35 -- Komm Settings = 300,7,E,1
Send: 11:29:58,57 -- /?!<CR><LF>
Recv: 11:29:59,80 -- /ABB5\@V4.50 <CR><LF>
Send: 11:30:00,01 -- <ACK>050<CR><LF>
Komm: 11:30:00,31 -- Komm Settings = 9600,7,E,1
Recv: 11:30:00,57 -- <STX>0.0.0(00491465)<CR><LF>
Recv: 11:30:00,60 -- 0.9.1(112957)<CR><LF>
Recv: 11:30:00,66 -- 1.6.1(0000.00*kW)(0000000000)<CR><LF>
Recv: 11:30:00,73 -- 1.6.1*04(0000.00)(0000000000)<CR><LF>
Recv: 11:30:00,79 -- 1.6.2(0000.00*kW)(0000000000)<CR><LF>
Recv: 11:30:00,88 -- 1.6.2*04(0000.00)(0000000000)<CR><LF>
Recv: 11:30:00,91 -- 1.8.1(00000000*kWh)<CR><LF>
Recv: 11:30:00,94 -- 1.8.1*04(00000000)<CR><LF>
Recv: 11:30:00,97 -- 1.8.2(00000000*kWh)<CR><LF>
Recv: 11:30:01,02 -- 1.8.2*04(00000000)<CR><LF>
Recv: 11:30:01,05 -- 0.2.0(05F1)<CR><LF>
Recv: 11:30:01,08 -- !<CR><LF>
Recv: 11:30:01,09 -- <ETX>i Soll: i
Komm: 11:30:01,41 -- Seriell-Status = Geschlossen
Thanks for reading and any help is greatly appreciated!
I guess the meter is switching to the new baudrate quickly - transmitting the five characters of the baud rate change command will take maybe 0.2s, but your sleep(1) means the meter probably already started sending at the new rate and data is lost until you have then changed baud rate to 9600. Try shortening this?