It's been quite a while since I've looked at IEC 62056-21. I used to have a Siemens S2A at home and now I have an Ampy 5235. I bought an S2A from ebay (couldn't really be playing with the official meter could I). For the infra red interface I made the common 741 op-amp based interface from the net and it worked ok but I almost immediately figured it wasn't good enough (and I wanted far better quality of signal and also USB) so I designed my own.
For development I used Linux and Perl and recently someone asked me to port to Python which I have just started when time permits. I have reversed the Siemens P0/P2 password exchange algo and can freely communicate with the S2A. I did a bit of fuzzing with the meter and did get some interesting preliminary results (I can talk about this more later on). Seeing as a lot of the meters out there seem to be from the same base manufacturer I was wondering if my Siemens algo will work on other meters.
It could now be time to revive this project on 62056-21 meter interaction what with the new daisy chain smart meters that are apparently on their way.
Anyway, if anyone fancies having a go at building an interface and joining in then the more the merrier. As I said, I use Linux for developement but if anyone wants to use Windows then I do have an XP box I could use - maybe with Strawberry Perl or Python for Windows. Only XP though. I have various scopes including a Picoscope and a logic analyzer to aid in the project from my end and can test for unknown problems in submitted code snippets for people that don't have the facilities.
The components for the interface are not expensive and test meters can be easily and cheaply bought from ebay etc. For the USB interface you will need a few components and a USB to Serial converter module. GET ONE WITH AN FTDI CHIP ON IT! I have tried a number of these modules and the FTDI ones are by far the best. The drivers for these seem to be the most complete and functional - maybe with the exception of the INTERCHAR_TIMEOUT but we will have to live with that.
These are the USB to Serial modules I have used:
These are both the same except one has LED's that flash when data is flowing. (Both chips on these are the same but just have different packages).
This is the diagram for the infra red interface:
(Quality diagram I know).
And these are the USB IR modules built up:
In the following pictures you can see the differences in the signal quality between the RS232 Op-Amp based interface and the USB interface. Don't get me wrong, the Op-Amp 741 interface does work but without a scope it can be a pain to set up. My USB interface uses a 7555 standard timer IC as a schmitt trigger to clean up the received signal (and then a transistor to invert the 7555 output). If anyone does want the 741 based diagram it is out there on the net somewhere but I can't find my diagram.
RS232 Op-Amp based:
USB/7555 Based:
Before building the interface it might be a good idea to read IEC62056-21 and see what you will be up against. It looks complicated but when you get your interface up and running and can see some data going in and out the clouds do begin to clear a little. To start with we will need to get the interface working and talking to the meter:
First of all, we need to send the opening request message to the meter: / ? ! CR LF which is /?!\r\n
(If you are reading 62056-21.pdf don't worry about the device address from 6.3.1 - just leave this null).
If all is well the meter sends back its ID message. 50ms after the above send my meter returns the following:
2f 46 4d 6c 34 41 30 30 30 30 56 38 30 0d 0a which is / F M l 4 A 0 0 0 0 V 8 0 CR LF
To close the connection with the meter now send the break message: SOH B 0 ETX BCC which is \x01B0\x03qHere is the code for testing the connection, test_signon.py:
# test_signon.py
#--------------------------------------------------------------------------------------------------------------------------------
import serial
import time
from binascii import hexlify
from watchdog import wdTimer
ser = serial.Serial()
ser.port = "/dev/ttyUSB0" # set interface instance
ser.baudrate = 300 # set baudrate to 300
ser.parity = serial.PARITY_EVEN # set parity to even parity
ser.bytesize = serial.SEVENBITS # number of bits per byte
ser.stopbits = serial.STOPBITS_ONE # number of stop bits
#ser.timeout = None # block read
#ser.timeout = 0 # non-block read
ser.timeout = 0.05 # timeout block read -set to 50ms to start with (value for 300bd)
ser.xonxoff = False # disable software flow control - same as handshake ???
ser.rtscts = False # disable hardware (RTS/CTS) flow control
ser.dsrdtr = False # disable hardware (DSR/DTR) flow control
ser.writeTimeout = 0 # timeout for write
ser.interCharTimeout = 0 # timeout between bytes - doesn't seem to work ???
ser.open()
print ('sending: / ? ! CR LF')
ser.write("/?!\r\n")
time.sleep(0.17) # give write time to finish (34ms per char @ 300bd)
# 50ms after the write the meter sends the following:
# / F M l 4 A 0 0 0 0 V 8 0 CR LF
# 2f 46 4d 6c 34 41 30 30 30 30 56 38 30 0d 0a
#
def readPort():
ser.flushInput()
readBuffer = ''
readBytes = ''
while ('' == readBuffer):
readBuffer = ser.read(1) # poll for 1st received byte
time.sleep(0.025) # polling sample time (25ms)
while ('' != readBuffer):
readBytes += readBuffer # build received message
readBuffer = ser.read(1) # read 1 byte at a time
return readBytes
try:
with wdTimer(2): # timeout of 2 seconds before 1st char received
readData = readPort()
print ('data read: ' + hexlify(readData))
print ('length: ' + str(len(readData)))
except Watchdog:
print "No chars received" # timeout tidying / error logging goes here
print ('sending: SOH B 0 ETX q')
ser.write("\x01B0\x03q")
time.sleep(0.2)
ser.close()
And the code for watchdog.py:
# file: watchdog.py
import signal
class wdTimer(Exception):
# usage wdTimer(timeout_in_seconds) or Watchdog() >> defaults to 2 seconds
def __init__(self, timeout=2):
self.time = timeout
def __enter__(self):
signal.signal(signal.SIGALRM, self.handler)
signal.alarm(self.time)
def __exit__(self, type, value, traceback):
signal.alarm(0)
def handler(self, signum, frame):
raise self
# def __str__(self):
# return "Timeout occured after %d seconds" % self.time
This is what test_signon.py looks like when it works:
~/test$ python test_signon.py
sending: / ? ! CR LF
data read: 2f464d6c3441303030305638300d0a
length: 15
sending: SOH B 0 ETX q
~/test$ This code is very basic - nothing fancy at all - but it works. Later functional code is marginally better - I've never used python so don't be taking the p1$$. Once this part is working there is a lot more stuff to think about. There is the baudrate change after signon and then the P0-P2 password exchange to gain access to the other modes of the meter.
Here is a screenshot of one of the later scripts connectiong to the meter with baudrate change and password exchange:
The baudrate change is immediately after the ACK 0 4 1 CR LF - see IEC62056-21 6.3.3
The password exchange are the following P 0 and P 2 lines.
All the files you will need for now are here:
http://www.team594.site50.net/iec62056/