1

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!

Mr Lister
  • 45,515
  • 15
  • 108
  • 150
  • So after you send the command to change speed, the unit responds immediately at the new speed? So maybe your sleep(1) is why your program is missing data? Sending s at six characters long will take 0.2-ish seconds. Or, can you tell the unit to switch speed without sending anything back, then change speed, then send it a command at the new speed asking it to send data? In htat case your sleep(1) will just slow things down but nothing will be lost. – DisappointedByUnaccountableMod Nov 26 '15 at 17:37
  • Your post is rather disorganized and confusing. What has *"an optical read head"*? Google search only finds your posts for *"ISC62056-21"*. *"We usually switch to 9600bits/s then ...The program then switches baudrate."* -- Are you switching the baudrate twice? *"The normal behaviour is that the meter than [sic] sends a frame of data beginning with and ending with and containing several lines of text ending with ."* -- With what? **Do a sanity test: change the baudrate to the same speed.** – sawdust Nov 27 '15 at 00:12
  • I edited my post and hope it is more understandable now. There was a typo, it is of course an IEC standard. The meter is negiotiating a baudrate and my program acknowledges the proposed baudrate of 9600 and then switches to that one. @barny: thanks for the tip. You where right that by lowering the sleep time I would loose less bytes. I have it set to 0.3 seconds now and get everything. I wanted to have a more elegant way by calling flush(). It turns out that flush does nothing, the documentation states that it is not implemented for non-blocking streams. So I need sleep(). – Jay Christnach Nov 27 '15 at 15:10

2 Answers2

1

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?

0

In case it helps someone, it was good for me to put a sleep (0.3) before the change of baudrate to be.Baudrate = 9600. I use a Hexing 23DL meter.

The important thing is that you must have 300ms before the change of speed. If you have many lines of code you should calculate the sleep time () decided. Or test values between 0.270 and 0.30.

...
time.sleep (0.3)
ser.Baudrate = speed # any speed
...
Diego
  • 1
  • 1