Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 103 additions & 34 deletions kamstrup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

from __future__ import print_function

# You need pySerial
# You need pySerial
import serial

import math


#######################################################################
# These are the variables I have managed to identify
# Submissions welcome.
Expand Down Expand Up @@ -47,7 +47,7 @@
0x005B: "Pressure in flow",
0x005C: "Pressure in return flow",
0x004A: "Current flow in flow",
0x004B: "Current flow in return flow"
0x004B: "Current flow in return flow",
0x03ff: "Power In",
0x0438: "Power p1 In",
0x0439: "Power p2 In",
Expand Down Expand Up @@ -77,6 +77,16 @@
0x03EC: "Operation hours counter",
}

kamstrup_MC21_var = {
0x0044: "V1",
0x00f3: "V1 Reverse",
0x004a: "Flow",
0x03ec: "Hours counter",
0x0063: "Info",
0x012b: "Meter temperature",
0x0124: "Water temperature",
}


#######################################################################
# Units, provided by Erik Jensen
Expand Down Expand Up @@ -161,7 +171,7 @@ def debug_msg(self, msg):

def wr(self, b):
b = bytearray(b)
self.debug("Wr", b);
self.debug("Wr", b)
self.ser.write(b)

def rd(self):
Expand Down Expand Up @@ -194,16 +204,20 @@ def send(self, pfx, msg):
self.wr(c)

def recv(self):
b = bytearray()
# Skip first response, which is repetition of initial command,
# only break on 0x0d if it comes after 0x40
b = None
while True:
d = self.rd()
if d == None:
return None
if d == 0x40:
b = bytearray()
b.append(d)
if d == 0x0d:
break
if b != None:
b.append(d)
if d == 0x0d:
break

c = bytearray()
i = 1;
while i < len(b) - 1:
Expand All @@ -221,59 +235,108 @@ def recv(self):
self.debug_msg("CRC error")
return c[:-2]

def readvar(self, nbr):
# I wouldn't be surprised if you can ask for more than
# one variable at the time, given that the length is
# encoded in the response. Havn't tried.
def process_response(self, nbr, data):
# Process response data

self.send(0x80, (0x3f, 0x10, 0x01, nbr >> 8, nbr & 0xff))

b = self.recv()
if b == None:
if data[0] != nbr >> 8 or data[1] != nbr & 0xff:
self.debug_msg("NBR error")
return (None, None)

if b[0] != 0x3f or b[1] != 0x10:
return (None, None)

if b[2] != nbr >> 8 or b[3] != nbr & 0xff:
return (None, None)

if b[4] in units:
u = units[b[4]]
if data[2] in units:
u = units[data[2]]
else:
u = None

# Decode the mantissa
x = 0
for i in range(0,b[5]):
for i in range(0,data[3]):
x <<= 8
x |= b[i + 7]
x |= data[i + 5]

# Decode the exponent
i = b[6] & 0x3f
if b[6] & 0x40:
i = data[4] & 0x3f
if data[4] & 0x40:
i = -i
i = math.pow(10,i)
if b[6] & 0x80:
if data[4] & 0x80:
i = -i
x *= i

if False:
# Debug print
s = ""
for i in b[:4]:
for i in data[:2]:
s += " %02x" % i
s += " |"
for i in b[4:7]:
for i in data[2:5]:
s += " %02x" % i
s += " |"
for i in b[7:]:
for i in data[5:5+data[3]]:
s += " %02x" % i
s += " ||"
for i in data[5+data[3]:]:
s += " %02x" % i

print(s, "=", x, units[b[4]])
print(s, "=", x, units[data[2]])

return x, u

def readvar(self, nbr):
# Read single variable

self.send(0x80, (0x3f, 0x10, 0x01, nbr >> 8, nbr & 0xff))

b = self.recv()
if b == None:
return (None, None)

if b[0] != 0x3f or b[1] != 0x10:
return (None, None)

x, u = self.process_response(nbr, b[2:])

return (x, u)


def readvar_multiple(self, multiple_nbr):
# Read multiple vars at once

# Construct request
req = bytearray()
req.append(0x3f) #destination address
req.append(0x10) #CID
req.append(len(multiple_nbr)) #number of nbrs
for nbr in multiple_nbr:
req.append(nbr >> 8)
req.append(nbr & 0xff)

self.send(0x80, req)

# Process response
b = self.recv()
if b == None:
return (None, None)

# Check destination address and CID
if b[0] != 0x3f or b[1] != 0x10:
return (None, None)

# Decode response data, containing multiple variables
result = {}
remaining_data = b[2:]
counter = 0
# Continue processing data until all variables processed
while counter < (len(multiple_nbr)):
current_nbr = multiple_nbr[counter]
x, u = self.process_response(current_nbr,remaining_data)
result[current_nbr] = (x,u)
# length of current variable response data =
# nbr (2) + units (1) + length (1) + sigexp (1) (=5)
# + length of actual value
len_current_nbr = 5 + remaining_data[3]
remaining_data = remaining_data[len_current_nbr:]
counter += 1

return result

if __name__ == "__main__":

Expand All @@ -284,3 +347,9 @@ def readvar(self, nbr):
for i in kamstrup_382_var:
x,u = foo.readvar(i)
print("%-25s" % kamstrup_382_var[i], x, u)

# Multiple var example using multical 21:
# result = foo.readvar_multiple(kamstrup_MC21_var.keys())
# for i in result:
# x, u = result[i]
# print("%-25s" % kamstrup_MC21_var[i], x, u)