view iec1107.py @ 1:99f25c8ab92f

Make class version actually work, remove procedure. Rename as it doesn't get continuous results, just one shot.
author Daniel O'Connor <darius@dons.net.au>
date Wed, 20 Nov 2013 12:46:59 +1030
parents fd8520d969c4
children 8f1a773a3cd5
line wrap: on
line source

#!/usr/bin/env python

#
# http://www.domoticaforum.eu/viewtopic.php?f=71&t=7489
#

import exceptions
import serial
import sys
import time

baudtable = {'0' : 300, '1' : 600, '2' : 1200, '3' : 2400, '4' : 4800, '5' : 9600, '6' : 19200}

class Error(exceptions.BaseException):
    pass

class IEC1107Reading(object):
    def __init__(self, port, force300bps = True):
        # Open port
        s = serial.Serial(port, baudrate = 300, bytesize = 7, parity = 'E', stopbits = 1)
        s.timeout = 2.5

        # Send ident message
        s.write('/?!\r\n')
        rtn = s.readline()
        if len(rtn) == 0:
            raise Error('No reply to probe')
        if len(rtn) < 6 or rtn[0] != '/' or rtn[-1] != '\n' or rtn[-2] != '\r':
            raise Error('Invalid line "%s"' % (rtn))

        rtn = rtn.strip()
        self.mfg = rtn[1:4]

        if self.mfg[2].isupper():
            self.restime = 0.2
        else:
            self.restime = 0.02

        if force300bps:
            self.baudid = '0'
        else:
            self.baudid = rtn[4]
        if self.baudid not in baudtable:
            raise Error('Invalid baud rate %c from "%s"' % (selfbaudid, rtn))
        else:
            self.baud = baudtable[self.baudid]
            
        if rtn[5] == '/':
            self.mode = rtn[6]
            self.mfg = rtn[7:]
        else:
            self.mode = None
            self.mfg = rtn[5:]

        # Send ACK/option message
        # Byte	Meaning
        # 0	ACK (0x06)
        # 1	Protocol character ('0' = normal, '1' = secondary, '2' = HDLC protocol)
        # 2	Baud rate ID ('0', '1', etc)
        # 3	Mode control('0' = read data, '1' = device prog)
        s.write('\x060%c0\r\n' % (self.baudid))

        time.sleep(self.restime)
        s.setBaudrate(self.baud)

        lines = []
        cksum = 0

        # Read STX
        head = s.read(1)
        if len(head) == 0:
            raise Error('No reply to query')
        if head != '\x02':
            raise Error('Invalid reply header 0x%02x' % (ord(head)))
        
        # Read result lines
        while True:
            line = s.readline()
            cksum ^= reduce(lambda x, y: x ^ y, map(ord, line))
            line = line.strip()
            if len(line) == 0:
                raise Error('Timeout during message')
            if line == '!':
                break
            lines.append(line)

        # Read trailer
        fin = s.read(2)
        if len(fin) != 2:
            raise Error('Timeout reading trailer')
        if fin[0] != '\x03':
            raise Error('Trailer malformed, expected 0x03, got 0x%02x' % (ord(fin[0])))

        # Validate checksum
        cksum ^= ord(fin[0])
        if cksum != ord(fin[1]):
            raise Error('Checksum mismatch, expected 0x%02x, got 0x%02x' % (cksum, ord(fin[1])))
        self.reading = lines
        del s

def main():
    if len(sys.argv) != 2:
        print 'Bad usage'
        print '\t%s portname' % (sys.argv[0])
        sys.exit(1)

    res = IEC1107Reading(sys.argv[1])
    print res.reading

if __name__ == '__main__':
    main()

# Meter number is 1288004
# 1.8.0 is import
# 1.8.1 is ??
# 1.8.2 is ??
# 1.8.3 is ??
# 2.8.0 is export

# C.1(12880041.0(22:25 18-11-13)
# 1.8.1(0000000597*Wh)
# 1.8.2(0000000000*Wh)
# 1.8.3(0000264238*Wh)
# 1.8.0(0000264835*Wh)
# 2.8.0(0000511354*Wh)

# ==> /?!<0D><0A>
# <== /ACE5SMLCD
# ==> <06>050<0D><0A>
# <==  -- STX -- 
# <== C.1(12880041.0(22:48 18-11-13)
# <== 1.8.1(0000000597*Wh)
# <== 1.8.2(0000000000*Wh)
# <== 1.8.3(0000264460*Wh)
# <== 1.8.0(0000265057*Wh)
# <== 2.8.0(0000511354*Wh)
# <== !
# <==  -- ETX -- 
# <==  -- BCC --