From a82431c529e89f5a7c5d5789a79224ccfb19df3b Mon Sep 17 00:00:00 2001
From: Wolfgang Hottgenroth <wolfgang.hottgenroth@icloud.com>
Date: Tue, 3 Nov 2020 08:18:07 +0100
Subject: [PATCH] add libmbus files

---
 cube/libmbus/mbus-protocol.c | 3861 ++++++++++++++++++++++++++++++++++
 cube/libmbus/mbus-protocol.h |  624 ++++++
 2 files changed, 4485 insertions(+)
 create mode 100644 cube/libmbus/mbus-protocol.c
 create mode 100644 cube/libmbus/mbus-protocol.h

diff --git a/cube/libmbus/mbus-protocol.c b/cube/libmbus/mbus-protocol.c
new file mode 100644
index 0000000..45df1be
--- /dev/null
+++ b/cube/libmbus/mbus-protocol.c
@@ -0,0 +1,3861 @@
+//------------------------------------------------------------------------------
+// Copyright (C) 2010-2011, Robert Johansson, Raditex AB
+// All rights reserved.
+//
+// rSCADA 
+// http://www.rSCADA.se
+// info@rscada.se
+//
+//------------------------------------------------------------------------------
+
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <mbus/mbus-protocol.h>
+
+static int parse_debug = 0, debug = 0;
+static char error_str[512];
+
+#define NITEMS(x) (sizeof(x)/sizeof(x[0]))
+
+//------------------------------------------------------------------------------
+// internal data
+//------------------------------------------------------------------------------
+static mbus_slave_data slave_data[MBUS_MAX_PRIMARY_SLAVES];
+
+//
+// init event callback
+//
+void (*_mbus_recv_event)(u_char src_type, const char *buff, size_t len) = NULL;
+void (*_mbus_send_event)(u_char src_type, const char *buff, size_t len) = NULL;
+
+//
+//  trace callbacks
+//
+void
+mbus_dump_recv_event(u_char src_type, const char *buff, size_t len)
+{
+    mbus_hex_dump("RECV", buff, len);
+}
+
+void
+mbus_dump_send_event(u_char src_type, const char *buff, size_t len)
+{
+    mbus_hex_dump("SEND", buff, len);
+}
+
+//------------------------------------------------------------------------------
+/// Register a function for receive events.
+//------------------------------------------------------------------------------
+void
+mbus_register_recv_event(void (*event)(u_char src_type, const char *buff, size_t len))
+{
+    _mbus_recv_event = event;
+}
+
+//------------------------------------------------------------------------------
+/// Register a function for send events.
+//------------------------------------------------------------------------------
+void
+mbus_register_send_event(void (*event)(u_char src_type, const char *buff, size_t len))
+{
+    _mbus_send_event = event;
+}
+
+//------------------------------------------------------------------------------
+/// Return a string that contains an the latest error message.
+//------------------------------------------------------------------------------
+char *
+mbus_error_str()
+{
+    return error_str;
+}
+
+void
+mbus_error_str_set(char *message)
+{
+    if (message)
+    {
+        snprintf(error_str, sizeof(error_str), "%s", message);
+    }
+}
+
+void
+mbus_error_reset()
+{
+    snprintf(error_str, sizeof(error_str), "no errors");
+}
+
+//------------------------------------------------------------------------------
+/// Return a pointer to the slave_data register. This register can be used for 
+/// storing current slave status.
+//------------------------------------------------------------------------------
+mbus_slave_data *
+mbus_slave_data_get(size_t i)
+{
+    if (i < MBUS_MAX_PRIMARY_SLAVES)
+    {
+        return &slave_data[i];
+    }
+    
+    return NULL;
+}
+
+//------------------------------------------------------------------------------
+//
+// M-Bus FRAME RELATED FUNCTIONS
+//
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// Allocate an M-bus frame data structure and initialize it according to which
+/// frame type is requested.
+//------------------------------------------------------------------------------
+mbus_frame *
+mbus_frame_new(int frame_type)
+{
+    mbus_frame *frame = NULL;
+    
+    if ((frame = malloc(sizeof(mbus_frame))) != NULL)
+    {
+        memset((void *)frame, 0, sizeof(mbus_frame));
+        
+        frame->type = frame_type;
+        switch (frame->type)
+        {
+            case MBUS_FRAME_TYPE_ACK:
+            
+                frame->start1 = MBUS_FRAME_ACK_START;
+            
+                break;
+
+            case MBUS_FRAME_TYPE_SHORT:
+            
+                frame->start1 = MBUS_FRAME_SHORT_START;
+                frame->stop   = MBUS_FRAME_STOP;  
+
+                break;
+
+            case MBUS_FRAME_TYPE_CONTROL:
+
+                frame->start1 = MBUS_FRAME_CONTROL_START;
+                frame->start2 = MBUS_FRAME_CONTROL_START;
+                frame->length1 = 3;
+                frame->length2 = 3;
+                frame->stop   = MBUS_FRAME_STOP;  
+                  
+                break;
+
+            case MBUS_FRAME_TYPE_LONG:
+
+                frame->start1 = MBUS_FRAME_LONG_START;
+                frame->start2 = MBUS_FRAME_LONG_START;
+                frame->stop   = MBUS_FRAME_STOP;              
+
+                break;
+        }            
+    }
+
+    return frame;
+}
+
+//------------------------------------------------------------------------------
+/// Free the memory resources allocated for the M-Bus frame data structure.
+//------------------------------------------------------------------------------
+int
+mbus_frame_free(mbus_frame *frame)
+{
+    if (frame)
+    {
+        if (frame->next != NULL)
+            mbus_frame_free(frame->next);
+    
+        free(frame);
+        return 0;
+    }
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+/// Caclulate the checksum of the M-Bus frame. Internal.
+//------------------------------------------------------------------------------
+u_char
+calc_checksum(mbus_frame *frame)
+{
+    size_t i;
+    u_char cksum;
+    
+    assert(frame != NULL);
+    switch(frame->type)
+    {
+        case MBUS_FRAME_TYPE_SHORT:
+
+            cksum = frame->control;
+            cksum += frame->address;
+
+            break;
+
+        case MBUS_FRAME_TYPE_CONTROL:
+
+            cksum = frame->control;
+            cksum += frame->address;
+            cksum += frame->control_information;
+
+            break;
+
+        case MBUS_FRAME_TYPE_LONG:
+
+            cksum = frame->control;
+            cksum += frame->address;
+            cksum += frame->control_information;
+
+            for (i = 0; i < frame->data_size; i++)
+            {
+                cksum += frame->data[i];
+            }
+
+            break;
+
+        case MBUS_FRAME_TYPE_ACK:
+        default:
+            cksum = 0;
+    }
+
+    return cksum;
+}
+
+//------------------------------------------------------------------------------
+/// Caclulate the checksum of the M-Bus frame. The checksum algorithm is the
+/// arithmetic sum of the frame content, without using carry. Which content
+/// that is included in the checksum calculation depends on the frame type.
+//------------------------------------------------------------------------------
+int
+mbus_frame_calc_checksum(mbus_frame *frame)
+{
+    if (frame)
+    {
+        switch (frame->type)
+        {
+            case MBUS_FRAME_TYPE_ACK:
+            case MBUS_FRAME_TYPE_SHORT:
+            case MBUS_FRAME_TYPE_CONTROL:
+            case MBUS_FRAME_TYPE_LONG:
+                frame->checksum = calc_checksum(frame);
+            
+                break;
+        
+            default:
+                return -1;
+        }        
+    }
+    
+    return 0;
+}
+
+///
+/// Calculate the values of the lengths fields in the M-Bus frame. Internal.
+///
+u_char
+calc_length(const mbus_frame *frame)
+{
+    assert(frame != NULL);
+    switch(frame->type)
+    {
+        case MBUS_FRAME_TYPE_CONTROL:
+            return 3;
+        case MBUS_FRAME_TYPE_LONG:
+            return frame->data_size + 3;
+        default:
+            return 0;
+    }
+}
+
+//------------------------------------------------------------------------------
+/// Calculate the values of the lengths fields in the M-Bus frame.
+//------------------------------------------------------------------------------
+int
+mbus_frame_calc_length(mbus_frame *frame)
+{
+    if (frame)
+    {
+        frame->length1 = frame->length2 = calc_length(frame);
+    }
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------
+/// Return the M-Bus frame type
+//------------------------------------------------------------------------------
+int
+mbus_frame_type(mbus_frame *frame)
+{
+    if (frame)
+    {
+        return frame->type;
+    }
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+/// Verify that parsed frame is a valid M-bus frame. 
+//
+// Possible checks:
+//
+// 1) frame type
+// 2) Start/stop bytes
+// 3) control field
+// 4) length field and actual data size
+// 5) checksum
+//
+//------------------------------------------------------------------------------
+int
+mbus_frame_verify(mbus_frame *frame)
+{
+    u_char checksum;
+
+    if (frame)
+    {
+        switch (frame->type)
+        {
+            case MBUS_FRAME_TYPE_ACK:
+                return frame->start1 == MBUS_FRAME_ACK_START;
+
+            case MBUS_FRAME_TYPE_SHORT:
+                if(frame->start1 != MBUS_FRAME_SHORT_START)
+                {
+                    snprintf(error_str, sizeof(error_str), "No frame start");
+                        
+                    return -1;
+                }
+                
+                if ((frame->control !=  MBUS_CONTROL_MASK_SND_NKE)                          &&
+                    (frame->control !=  MBUS_CONTROL_MASK_REQ_UD1)                          &&
+                    (frame->control != (MBUS_CONTROL_MASK_REQ_UD1 | MBUS_CONTROL_MASK_FCB)) &&
+                    (frame->control !=  MBUS_CONTROL_MASK_REQ_UD2)                          &&
+                    (frame->control != (MBUS_CONTROL_MASK_REQ_UD2 | MBUS_CONTROL_MASK_FCB)))
+                {
+                    snprintf(error_str, sizeof(error_str), "Unknown Control Code 0x%.2x", frame->control);
+                
+                    return -1;
+                }
+
+                break;
+
+            case MBUS_FRAME_TYPE_CONTROL:
+            case MBUS_FRAME_TYPE_LONG:
+                if(frame->start1  != MBUS_FRAME_CONTROL_START ||
+                   frame->start2  != MBUS_FRAME_CONTROL_START)
+                {
+                    snprintf(error_str, sizeof(error_str), "No frame start");
+                        
+                    return -1;
+                }
+                
+                if ((frame->control !=  MBUS_CONTROL_MASK_SND_UD)                          &&
+                    (frame->control != (MBUS_CONTROL_MASK_SND_UD | MBUS_CONTROL_MASK_FCB)) &&
+                    (frame->control !=  MBUS_CONTROL_MASK_RSP_UD)                          &&
+                    (frame->control != (MBUS_CONTROL_MASK_RSP_UD | MBUS_CONTROL_MASK_DFC)) &&
+                    (frame->control != (MBUS_CONTROL_MASK_RSP_UD | MBUS_CONTROL_MASK_ACD)) &&
+                    (frame->control != (MBUS_CONTROL_MASK_RSP_UD | MBUS_CONTROL_MASK_DFC | MBUS_CONTROL_MASK_ACD)))
+                {
+                    snprintf(error_str, sizeof(error_str), "Unknown Control Code 0x%.2x", frame->control);
+                
+                    return -1;
+                }
+                
+                if (frame->length1 != frame->length2)
+                {
+                    snprintf(error_str, sizeof(error_str), "Frame length 1 != 2");
+        
+                    return -1;
+                }
+                
+                if (frame->length1 != calc_length(frame))
+                {
+                    snprintf(error_str, sizeof(error_str), "Frame length 1 != calc length");
+                
+                    return -1;
+                }
+
+                break;
+
+            default:
+                snprintf(error_str, sizeof(error_str), "Unknown frame type 0x%.2x", frame->type);
+                
+                return -1;
+        }
+
+        if(frame->stop != MBUS_FRAME_STOP)
+        {
+            snprintf(error_str, sizeof(error_str), "No frame stop");
+                        
+            return -1;
+        }
+        
+        checksum = calc_checksum(frame);
+        
+        if(frame->checksum != checksum)
+        {
+            snprintf(error_str, sizeof(error_str), "Invalid checksum (0x%.2x != 0x%.2x)", frame->checksum, checksum);
+                
+            return -1;
+        }
+
+        return 0;
+    }
+    
+    snprintf(error_str, sizeof(error_str), "Got null pointer to frame.");
+
+    return -1;
+}
+
+
+//------------------------------------------------------------------------------
+//
+// DATA ENCODING, DECODING, AND CONVERSION FUNCTIONS
+//
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+///
+/// Encode BCD data
+///
+//------------------------------------------------------------------------------
+int
+mbus_data_bcd_encode(u_char *bcd_data, size_t bcd_data_size, int value)
+{ 
+    int v0, v1, v2, x1, x2;
+    size_t i;
+
+    v2 = value;
+
+    if (bcd_data)
+    {
+        for (i = 0; i < bcd_data_size; i++)
+        {
+            v0 = v2;
+            v1 = (int)(v0 / 10.0);
+            v2 = (int)(v1 / 10.0);
+
+            x1 = v0 - v1 * 10;
+            x2 = v1 - v2 * 10;
+
+            bcd_data[bcd_data_size-1-i] = (x2 << 4) | x1;
+        }
+
+        return 0;    
+    }
+    
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Decode BCD data
+///
+//------------------------------------------------------------------------------
+long long
+mbus_data_bcd_decode(u_char *bcd_data, size_t bcd_data_size)
+{       
+    long long val = 0;
+    size_t i;
+    
+    if (bcd_data)
+    {
+        for (i = bcd_data_size; i > 0; i--)
+        {
+            val = (val * 10) + ((bcd_data[i-1]>>4) & 0xF);
+            val = (val * 10) + ( bcd_data[i-1]     & 0xF);
+        }
+
+        return val;    
+    }
+    
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Decode INTEGER data
+///
+//------------------------------------------------------------------------------
+int
+mbus_data_int_decode(u_char *int_data, size_t int_data_size)
+{
+    int val = 0;
+    size_t i;
+    
+    if (int_data)
+    {
+        for (i = int_data_size; i > 0; i--)
+        {
+            val = (val << 8) + int_data[i-1];
+        }
+
+        return val;    
+    }
+
+    return -1;
+}
+
+long
+mbus_data_long_decode(u_char *int_data, size_t int_data_size)
+{
+    long val = 0;
+    size_t i;
+    
+    if (int_data)
+    {
+        for (i = int_data_size; i > 0; i--)
+        {
+            val = (val << 8) + int_data[i-1];
+        }
+
+        return val;    
+    }
+
+    return -1;
+}
+
+long long
+mbus_data_long_long_decode(u_char *int_data, size_t int_data_size)
+{
+    long long val = 0;
+    size_t i;
+    
+    if (int_data)
+    {
+        for (i = int_data_size; i > 0; i--)
+        {
+            val = (val << 8) + int_data[i-1];
+        }
+
+        return val;    
+    }
+
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Encode INTEGER data (into 'int_data_size' bytes)
+///
+//------------------------------------------------------------------------------
+int
+mbus_data_int_encode(u_char *int_data, size_t int_data_size, int value)
+{
+    int i;
+    
+    if (int_data)
+    {
+        for (i = 0; i < int_data_size; i++)
+        {
+            int_data[i] = (value>>(i*8)) & 0xFF;
+        }
+
+        return 0;
+    }
+
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Decode float data 
+///
+/// see also http://en.wikipedia.org/wiki/Single-precision_floating-point_format
+///
+//------------------------------------------------------------------------------
+float
+mbus_data_float_decode(u_char *float_data)
+{
+    float val = 0.0f;
+    long temp = 0, fraction;
+    int sign,exponent;
+    size_t i;
+    
+    if (float_data)
+    {
+        for (i = 4; i > 0; i--)
+        {
+            temp = (temp << 8) + float_data[i-1];
+        }
+        
+        // first bit = sign bit
+        sign     = (temp >> 31) ? -1 : 1;
+  
+        // decode 8 bit exponent
+        exponent = ((temp & 0x7F800000) >> 23) - 127;
+  
+        // decode explicit 23 bit fraction
+        fraction = temp & 0x007FFFFF;
+  
+        if ((exponent != -127) &&
+            (exponent != 128))
+        {
+            // normalized value, add bit 24 
+            fraction |= 0x800000;
+        }
+  
+        // calculate float value
+        val = (float) sign * fraction * pow(2.0f, -23.0f) * (1 << exponent);
+
+        return val;    
+    }
+
+    return -1.0;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Decode string data.
+///
+//------------------------------------------------------------------------------
+void
+mbus_data_str_decode(u_char *dst, const u_char *src, size_t len)
+{
+    size_t i;
+
+    i = 0;
+    
+    if (src && dst)
+    {
+        dst[len] = '\0';
+        while(len > 0) {
+            dst[i++] = src[--len];
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+///
+/// Decode binary data.
+///
+//------------------------------------------------------------------------------
+void
+mbus_data_bin_decode(u_char *dst, const u_char *src, size_t len, size_t max_len)
+{
+    size_t i, pos;
+    
+    i = 0;
+    pos = 0;
+    
+    if (src && dst)
+    {
+        while((i < len) && ((pos+3) < max_len)) {
+            pos += snprintf(&dst[pos], max_len - pos, "%.2X ", src[i]);
+            i++;
+        }
+        
+        if (pos > 0)
+        {
+            // remove last space
+            pos--;
+        }
+        
+        dst[pos] = '\0';
+    }
+}
+
+//------------------------------------------------------------------------------
+///
+/// Decode time data (usable for type f = 4 bytes or type g = 2 bytes)
+///
+//------------------------------------------------------------------------------
+void
+mbus_data_tm_decode(struct tm *t, u_char *t_data, size_t t_data_size)
+{
+    if (t && t_data)
+    {
+        t->tm_sec   = 0;
+        t->tm_min   = 0;
+        t->tm_hour  = 0;
+        t->tm_mday  = 0;
+        t->tm_mon   = 0;
+        t->tm_year  = 0; 
+        t->tm_isdst = 0;
+   
+        if (t_data_size == 4)                // Type F = Compound CP32: Date and Time
+        {     
+            if ((t_data[0] & 0x80) == 0)     // Time valid ?
+            {
+                t->tm_min   = t_data[0] & 0x3F;
+                t->tm_hour  = t_data[1] & 0x1F;
+                t->tm_mday  = t_data[2] & 0x1F;
+                t->tm_mon   = (t_data[3] & 0x0F) - 1;
+                t->tm_year  = ((t_data[2] & 0xE0) >> 5) | 
+                              ((t_data[3] & 0xF0) >> 1); 
+                t->tm_isdst = (t_data[1] & 0x80) ? 1 : 0;  // day saving time
+            }
+        }
+        else if (t_data_size == 2)           // Type G: Compound CP16: Date
+        {
+            t->tm_mday = t_data[0] & 0x1F;
+            t->tm_mon  = (t_data[1] & 0x0F) - 1; 
+            t->tm_year = ((t_data[0] & 0xE0) >> 5) | 
+                         ((t_data[1] & 0xF0) >> 1);  
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+///
+/// Generate manufacturer code from 2-byte encoded data
+///
+//------------------------------------------------------------------------------
+int
+mbus_data_manufacturer_encode(u_char *m_data, u_char *m_code)
+{
+    int m_val;
+
+    if (m_data == NULL || m_code == NULL)
+        return -1;
+
+    m_val = ((((int)m_code[0] - 64) & 0x001F) << 10) + 
+            ((((int)m_code[1] - 64) & 0x001F) << 5) +
+            ((((int)m_code[2] - 64) & 0x001F));
+
+    mbus_data_int_encode(m_data, 2, m_val);
+    
+    return 0;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Generate manufacturer code from 2-byte encoded data
+///
+//------------------------------------------------------------------------------
+const char *
+mbus_decode_manufacturer(u_char byte1, u_char byte2)
+{
+    static char m_str[4];
+    
+    int m_id;
+    
+    m_str[0] = byte1;
+    m_str[1] = byte2;
+    
+    m_id = mbus_data_int_decode(m_str, 2);
+        
+    m_str[0] = (char)(((m_id>>10) & 0x001F) + 64);
+    m_str[1] = (char)(((m_id>>5)  & 0x001F) + 64);
+    m_str[2] = (char)(((m_id)     & 0x001F) + 64);
+    m_str[3] = 0;
+
+    return m_str;
+}
+
+const char *
+mbus_data_product_name(mbus_data_variable_header *header)
+{
+    static char buff[128];
+    unsigned int manufacturer;
+
+    memset(buff, 0, sizeof(buff));
+
+    if (header)
+    {
+        manufacturer = (header->manufacturer[1] << 8) + header->manufacturer[0];
+
+        if (manufacturer == MBUS_VARIABLE_DATA_MAN_ACW) 
+        {
+            switch (header->version)
+            {
+                case 0x09:
+                    strcpy(buff,"Itron CF Echo 2");
+                    break;
+                case 0x0A:
+                    strcpy(buff,"Itron CF 51");
+                    break;
+                case 0x0B:
+                    strcpy(buff,"Itron CF 55");
+                    break;
+                case 0x0E:
+                    strcpy(buff,"Itron BM +m");
+                    break;
+                case 0x0F:
+                    strcpy(buff,"Itron CF 800");
+                    break;
+                case 0x14:
+                    strcpy(buff,"Itron CYBLE M-Bus 1.4");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_EFE)
+        {
+            switch (header->version)
+            {
+                case 0x00:
+                    strcpy(buff, ((header->medium == 0x06) ? "Engelmann WaterStar" : "Engelmann SensoStar 2"));
+                    break;
+                case 0x01:
+                    strcpy(buff,"Engelmann SensoStar 2C");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_SLB) 
+        {
+            switch (header->version)
+            {
+                case 0x02:
+                    strcpy(buff,"Allmess Megacontrol CF-50");
+                    break;
+                case 0x06:
+                    strcpy(buff,"CF Compact / Integral MK MaXX");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_HYD) 
+        {
+            switch (header->version)
+            {
+                case 0x28:
+                    strcpy(buff,"ABB F95 Typ US770");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_LUG) 
+        {
+            switch (header->version)
+            {
+                case 0x02:
+                    strcpy(buff,"Landis & Gyr Ultraheat 2WR5");
+                    break;
+                case 0x03:
+                    strcpy(buff,"Landis & Gyr Ultraheat 2WR6");
+                    break;
+                case 0x04:
+                    strcpy(buff,"Landis & Gyr Ultraheat UH50");
+                    break;
+                case 0x07:
+                    strcpy(buff,"Landis & Gyr Ultraheat T230");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_SVM) 
+        {
+            switch (header->version)
+            {
+                case 0x08:
+                    strcpy(buff,"Elster F2");
+                    break;
+                case 0x09:
+                    strcpy(buff,"Kamstrup SVM F22");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_SON) 
+        {
+            switch (header->version)
+            {
+                case 0x0D:
+                    strcpy(buff,"Sontex Supercal 531");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_LSE)
+        {
+            switch (header->version)
+            {
+                case 0x99:
+                    strcpy(buff,"Siemens WFH21");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_SPX) 
+        {
+            switch (header->version)
+            {
+                case 0x31:
+                case 0x34:
+                    strcpy(buff,"Sensus PolluTherm");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_ELS)
+        {
+            switch (header->version)
+            {
+                case 0x02:
+                    strcpy(buff,"Elster TMP-A");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_NZR)
+        {
+            switch (header->version)
+            {
+                case 0x01:
+                    strcpy(buff,"NZR DHZ 5/63");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_KAM)
+        {
+            switch (header->version)
+            {
+                case 0x08:
+                    strcpy(buff,"Kamstrup Multical 601");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_EMH)
+        {
+            switch (header->version)
+            {
+                case 0x00:
+                    strcpy(buff,"EMH DIZ");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_TCH)
+        {
+            switch (header->version)
+            {
+                case 0x26:
+                    strcpy(buff,"Techem m-bus S");
+                    break;
+            }
+        }
+        else if (manufacturer == MBUS_VARIABLE_DATA_MAN_ZRM)
+        {
+            switch (header->version)
+            {
+                case 0x81:
+                    strcpy(buff,"Minol Minocal C2");
+                    break;
+                case 0x82:
+                    strcpy(buff,"Minol Minocal WR3");
+                    break;
+            }
+        }
+    }
+
+    return buff;
+}
+
+//------------------------------------------------------------------------------
+//
+// FIXED-LENGTH DATA RECORD FUNCTIONS
+//
+//------------------------------------------------------------------------------
+
+
+//
+//   Value         Field Medium/Unit              Medium
+// hexadecimal Bit 16  Bit 15    Bit 8  Bit 7
+//     0        0       0         0     0         Other
+//     1        0       0         0     1         Oil
+//     2        0       0         1     0         Electricity
+//     3        0       0         1     1         Gas
+//     4        0       1         0     0         Heat
+//     5        0       1         0     1         Steam
+//     6        0       1         1     0         Hot Water
+//     7        0       1         1     1         Water
+//     8        1       0         0     0         H.C.A.
+//     9        1       0         0     1         Reserved
+//     A        1       0         1     0         Gas Mode 2
+//     B        1       0         1     1         Heat Mode 2
+//     C        1       1         0     0         Hot Water Mode 2
+//     D        1       1         0     1         Water Mode 2
+//     E        1       1         1     0         H.C.A. Mode 2
+//     F        1       1         1     1         Reserved
+//
+
+///
+/// For fixed-length frames, get a string describing the medium.
+///
+const char *
+mbus_data_fixed_medium(mbus_data_fixed *data)
+{
+    static char buff[256];
+
+    if (data)
+    {
+        switch ( (data->cnt1_type&0xC0)>>6 | (data->cnt2_type&0xC0)>>4 )
+        {
+            case 0x00:    
+                snprintf(buff, sizeof(buff), "Other");
+                break;    
+            case 0x01:    
+                snprintf(buff, sizeof(buff), "Oil");
+                break;    
+            case 0x02:    
+                snprintf(buff, sizeof(buff), "Electricity");
+                break;    
+            case 0x03:    
+                snprintf(buff, sizeof(buff), "Gas");
+                break;    
+            case 0x04:    
+                snprintf(buff, sizeof(buff), "Heat");
+                break;    
+            case 0x05:    
+                snprintf(buff, sizeof(buff), "Steam");
+                break;    
+            case 0x06:    
+                snprintf(buff, sizeof(buff), "Hot Water");
+                break;    
+            case 0x07:    
+                snprintf(buff, sizeof(buff), "Water");
+                break;    
+            case 0x08:    
+                snprintf(buff, sizeof(buff), "H.C.A.");
+                break;    
+            case 0x09:    
+                snprintf(buff, sizeof(buff), "Reserved");
+                break;    
+            case 0x0A:    
+                snprintf(buff, sizeof(buff), "Gas Mode 2");
+                break;
+            case 0x0B:
+                snprintf(buff, sizeof(buff), "Heat Mode 2");
+                break;
+            case 0x0C:
+                snprintf(buff, sizeof(buff), "Hot Water Mode 2");
+                break;
+            case 0x0D:
+                snprintf(buff, sizeof(buff), "Water Mode 2");
+                break;
+            case 0x0E:
+                snprintf(buff, sizeof(buff), "H.C.A. Mode 2");
+                break;    
+            case 0x0F:
+                snprintf(buff, sizeof(buff), "Reserved");
+                break;    
+            default:
+                snprintf(buff, sizeof(buff), "unknown");
+                break;                
+        }
+        
+        return buff;
+    }
+
+    return NULL;
+}
+
+
+//------------------------------------------------------------------------------
+//                        Hex code                            Hex code
+//Unit                    share     Unit                      share
+//              MSB..LSB                            MSB..LSB
+//                       Byte 7/8                            Byte 7/8
+// h,m,s         000000     00        MJ/h           100000     20
+// D,M,Y         000001     01        MJ/h * 10      100001     21
+//     Wh        000010     02        MJ/h * 100     100010     22
+//     Wh * 10   000011     03        GJ/h           100011     23
+//     Wh * 100  000100     04        GJ/h * 10      100100     24
+//   kWh         000101     05        GJ/h * 100     100101     25
+//  kWh   * 10   000110     06           ml          100110     26
+//   kWh * 100   000111     07           ml * 10     100111     27
+//   MWh         001000     08           ml * 100    101000     28
+//   MWh * 10    001001     09            l          101001     29
+//   MWh * 100   001010     0A            l * 10     101010     2A
+//     kJ        001011     0B            l * 100    101011     2B
+//     kJ * 10   001100     0C           m3          101100     2C
+//     kJ * 100  001101     0D        m3 * 10        101101     2D
+//     MJ        001110     0E        m3 * 100       101110     2E
+//     MJ * 10   001111     0F        ml/h           101111     2F
+//     MJ * 100  010000     10        ml/h * 10      110000     30
+//     GJ        010001     11        ml/h * 100     110001     31
+//     GJ * 10   010010     12         l/h           110010     32
+//     GJ * 100  010011     13         l/h * 10      110011     33
+//      W        010100     14         l/h * 100     110100     34                              
+//      W * 10   010101     15       m3/h           110101     35                         
+//      W * 100  010110     16     m3/h * 10       110110     36
+//     kW        010111     17      m3/h * 100       110111     37
+//     kW * 10   011000     18        °C* 10-3       111000     38
+//     kW * 100  011001     19      units   for HCA  111001     39
+//     MW        011010     1A    reserved           111010     3A
+//     MW * 10   011011     1B    reserved           111011     3B
+//     MW * 100  011100     1C    reserved           111100     3C
+//  kJ/h         011101     1D    reserved           111101     3D
+//  kJ/h * 10    011110     1E    same but historic  111110     3E
+//  kJ/h * 100   011111     1F    without   units    111111     3F
+//
+//------------------------------------------------------------------------------
+///
+/// For fixed-length frames, get a string describing the unit of the data.
+///
+const char *
+mbus_data_fixed_unit(int medium_unit_byte)
+{
+    static char buff[256];
+    
+    switch (medium_unit_byte & 0x3F)
+    {
+        case 0x00:    
+            snprintf(buff, sizeof(buff), "h,m,s");
+            break;    
+        case 0x01:    
+            snprintf(buff, sizeof(buff), "D,M,Y");
+            break;    
+            
+        case 0x02:    
+            snprintf(buff, sizeof(buff), "Wh");
+            break;    
+        case 0x03:    
+            snprintf(buff, sizeof(buff), "10 Wh");
+            break;    
+        case 0x04:    
+            snprintf(buff, sizeof(buff), "100 Wh");
+            break;    
+        case 0x05:    
+            snprintf(buff, sizeof(buff), "kWh");
+            break;    
+        case 0x06:    
+            snprintf(buff, sizeof(buff), "10 kWh");
+            break;    
+        case 0x07:    
+            snprintf(buff, sizeof(buff), "100 kWh");
+            break;    
+        case 0x08:    
+            snprintf(buff, sizeof(buff), "MWh");
+            break;    
+        case 0x09:    
+            snprintf(buff, sizeof(buff), "10 MWh");
+            break;    
+        case 0x0A:    
+            snprintf(buff, sizeof(buff), "100 MWh");
+            break;    
+            
+        case 0x0B:    
+            snprintf(buff, sizeof(buff), "kJ");
+            break;    
+        case 0x0C:    
+            snprintf(buff, sizeof(buff), "10 kJ");
+            break;    
+        case 0x0E:    
+            snprintf(buff, sizeof(buff), "100 kJ");
+            break;    
+        case 0x0D:    
+            snprintf(buff, sizeof(buff), "MJ");
+            break;    
+        case 0x0F:    
+            snprintf(buff, sizeof(buff), "10 MJ");
+            break;    
+        case 0x10:    
+            snprintf(buff, sizeof(buff), "100 MJ");
+            break;
+        case 0x11:    
+            snprintf(buff, sizeof(buff), "GJ");
+            break;    
+        case 0x12:    
+            snprintf(buff, sizeof(buff), "10 GJ");
+            break;    
+        case 0x13:    
+            snprintf(buff, sizeof(buff), "100 GJ");
+            break;
+
+        case 0x14:    
+            snprintf(buff, sizeof(buff), "W");
+            break;    
+        case 0x15:    
+            snprintf(buff, sizeof(buff), "10 W");
+            break;    
+        case 0x16:    
+            snprintf(buff, sizeof(buff), "100 W");
+            break;    
+        case 0x17:    
+            snprintf(buff, sizeof(buff), "kW");
+            break;    
+        case 0x18:    
+            snprintf(buff, sizeof(buff), "10 kW");
+            break;    
+        case 0x19:    
+            snprintf(buff, sizeof(buff), "100 kW");
+            break;
+        case 0x1A:    
+            snprintf(buff, sizeof(buff), "MW");
+            break;    
+        case 0x1B:    
+            snprintf(buff, sizeof(buff), "10 MW");
+            break;    
+        case 0x1C:    
+            snprintf(buff, sizeof(buff), "100 MW");
+            break;
+            
+        case 0x1D:
+            snprintf(buff, sizeof(buff), "kJ/h");
+            break;
+        case 0x1E:
+            snprintf(buff, sizeof(buff), "10 kJ/h");
+            break;
+        case 0x1F:
+            snprintf(buff, sizeof(buff), "100 kJ/h");
+            break;
+        case 0x20:
+            snprintf(buff, sizeof(buff), "MJ/h");
+            break;
+        case 0x21:
+            snprintf(buff, sizeof(buff), "10 MJ/h");
+            break;
+        case 0x22:
+            snprintf(buff, sizeof(buff), "100 MJ/h");
+            break;
+        case 0x23:
+            snprintf(buff, sizeof(buff), "GJ/h");
+            break;
+        case 0x24:
+            snprintf(buff, sizeof(buff), "10 GJ/h");
+            break;
+        case 0x25:
+            snprintf(buff, sizeof(buff), "100 GJ/h");
+            break;
+            
+        case 0x26:
+            snprintf(buff, sizeof(buff), "ml");
+            break;
+        case 0x27:
+            snprintf(buff, sizeof(buff), "10 ml");
+            break;
+        case 0x28:
+            snprintf(buff, sizeof(buff), "100 ml");
+            break;
+        case 0x29:    
+            snprintf(buff, sizeof(buff), "l");
+            break;
+        case 0x2A:    
+            snprintf(buff, sizeof(buff), "10 l");
+            break;
+        case 0x2B:    
+            snprintf(buff, sizeof(buff), "100 l");
+            break;           
+        case 0x2C:
+            snprintf(buff, sizeof(buff), "m^3");
+            break;
+        case 0x2D:
+            snprintf(buff, sizeof(buff), "10 m^3");
+            break;
+        case 0x2E:
+            snprintf(buff, sizeof(buff), "m^3");
+            break;
+ 
+        case 0x2F:
+            snprintf(buff, sizeof(buff), "ml/h");
+            break;
+        case 0x30:
+            snprintf(buff, sizeof(buff), "10 ml/h");
+            break;
+        case 0x31:
+            snprintf(buff, sizeof(buff), "100 ml/h");
+            break;
+        case 0x32:
+            snprintf(buff, sizeof(buff), "l/h");
+            break;
+        case 0x33:
+            snprintf(buff, sizeof(buff), "10 l/h");
+            break;
+        case 0x34:
+            snprintf(buff, sizeof(buff), "100 l/h");
+            break;
+        case 0x35:
+            snprintf(buff, sizeof(buff), "m^3/h");
+            break;
+        case 0x36:
+            snprintf(buff, sizeof(buff), "10 m^3/h");
+            break;
+        case 0x37:
+            snprintf(buff, sizeof(buff), "100 m^3/h");
+            break;
+            
+        case 0x38:
+            snprintf(buff, sizeof(buff), "1e-3 °C");
+            break;
+        case 0x39:
+            snprintf(buff, sizeof(buff), "units for HCA");
+            break;    
+        case 0x3A:
+        case 0x3B:
+        case 0x3C:
+        case 0x3D:
+            snprintf(buff, sizeof(buff), "reserved");
+            break;
+        case 0x3E:    
+            snprintf(buff, sizeof(buff), "reserved but historic");
+            break;
+        case 0x3F:
+            snprintf(buff, sizeof(buff), "without units");
+            break;
+        default:
+            snprintf(buff, sizeof(buff), "unknown");
+            break;    
+    }
+
+    return buff;
+}
+
+//------------------------------------------------------------------------------
+//
+// VARIABLE-LENGTH DATA RECORD FUNCTIONS
+//
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//
+// Medium                                                              Code bin    Code hex
+// Other                                                              0000 0000        00
+// Oil                                                                0000 0001        01
+// Electricity                                                        0000 0010        02
+// Gas                                                                0000 0011        03
+// Heat (Volume measured at return temperature: outlet)               0000 0100        04
+// Steam                                                              0000 0101        05
+// Hot Water                                                          0000 0110        06
+// Water                                                              0000 0111        07
+// Heat Cost Allocator.                                               0000 1000        08
+// Compressed Air                                                     0000 1001        09
+// Cooling load meter (Volume measured at return temperature: outlet) 0000 1010        0A
+// Cooling load meter (Volume measured at flow temperature: inlet) ♣  0000 1011        0B
+// Heat (Volume measured at flow temperature: inlet)                  0000 1100        0C
+// Heat / Cooling load meter ♣                                        0000 1101        OD
+// Bus / System                                                       0000 1110        0E
+// Unknown Medium                                                     0000 1111        0F
+// Reserved                                                           .......... 10 to 15
+// Cold Water                                                         0001 0110        16
+// Dual Water                                                         0001 0111        17
+// Pressure                                                           0001 1000        18
+// A/D Converter                                                      0001 1001        19
+// Reserved                                                           .......... 20 to FF
+//------------------------------------------------------------------------------
+
+///
+/// For variable-length frames, returns a string describing the medium.
+///
+const char *
+mbus_data_variable_medium_lookup(u_char medium)
+{
+    static char buff[256];
+
+    switch (medium)
+    {
+        case MBUS_VARIABLE_DATA_MEDIUM_OTHER:
+            snprintf(buff, sizeof(buff), "Other");
+            break;
+                                          
+        case MBUS_VARIABLE_DATA_MEDIUM_OIL:
+            snprintf(buff, sizeof(buff), "Oil");
+            break;
+                  
+        case MBUS_VARIABLE_DATA_MEDIUM_ELECTRICITY:
+            snprintf(buff, sizeof(buff), "Electricity");
+            break;
+                                          
+        case MBUS_VARIABLE_DATA_MEDIUM_GAS:
+            snprintf(buff, sizeof(buff), "Gas");
+            break;
+                                          
+        case MBUS_VARIABLE_DATA_MEDIUM_HEAT_OUT:
+            snprintf(buff, sizeof(buff), "Heat: Outlet");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_STEAM:
+            snprintf(buff, sizeof(buff), "Steam");
+            break;
+                                          
+        case MBUS_VARIABLE_DATA_MEDIUM_HOT_WATER:
+            snprintf(buff, sizeof(buff), "Hot water");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_WATER:
+            snprintf(buff, sizeof(buff), "Water");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_HEAT_COST:
+            snprintf(buff, sizeof(buff), "Heat Cost Allocator");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_COMPR_AIR:
+            snprintf(buff, sizeof(buff), "Compressed Air");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_COOL_OUT:
+            snprintf(buff, sizeof(buff), "Cooling load meter: Outlet");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_COOL_IN:
+            snprintf(buff, sizeof(buff), "Cooling load meter: Inlet");
+            break;
+            
+        case MBUS_VARIABLE_DATA_MEDIUM_HEAT_IN:
+            snprintf(buff, sizeof(buff), "Heat: Inlet");
+            break;
+            
+        case MBUS_VARIABLE_DATA_MEDIUM_HEAT_COOL:
+            snprintf(buff, sizeof(buff), "Heat / Cooling load meter");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_BUS:
+            snprintf(buff, sizeof(buff), "Bus/System");
+            break;
+            
+        case MBUS_VARIABLE_DATA_MEDIUM_UNKNOWN:
+            snprintf(buff, sizeof(buff), "Unknown Medium");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_COLD_WATER:
+            snprintf(buff, sizeof(buff), "Cold water");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_DUAL_WATER:
+            snprintf(buff, sizeof(buff), "Dual water");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_PRESSURE:
+            snprintf(buff, sizeof(buff), "Pressure");
+            break;
+
+        case MBUS_VARIABLE_DATA_MEDIUM_ADC:
+            snprintf(buff, sizeof(buff), "A/D Converter");
+            break;
+
+        case 0x10: // - 0x15
+        case 0x20: // - 0xFF
+            snprintf(buff, sizeof(buff), "Reserved");
+            break;
+
+                                          
+        // add more ...
+        default:
+            snprintf(buff, sizeof(buff), "Unknown medium (0x%.2x)", medium);
+            break;            
+    }
+
+    return buff;       
+}
+
+//------------------------------------------------------------------------------
+///
+/// Lookup the unit description from a VIF field in a data record
+///
+//------------------------------------------------------------------------------
+const char *
+mbus_unit_prefix(int exp)
+{
+    static char buff[256];
+    
+    switch (exp)
+    {
+        case 0:
+            buff[0] = 0;
+            break;
+
+        case -3:
+            snprintf(buff, sizeof(buff), "m");
+            break;
+       
+        case -6:
+            snprintf(buff, sizeof(buff), "my");
+            break;
+
+        case 1:
+            snprintf(buff, sizeof(buff), "10 ");
+            break;
+
+        case 2:
+            snprintf(buff, sizeof(buff), "100 ");
+            break;
+
+        case 3:
+            snprintf(buff, sizeof(buff), "k");
+            break;
+            
+        case 4:
+            snprintf(buff, sizeof(buff), "10 k");
+            break;            
+
+        case 5:
+            snprintf(buff, sizeof(buff), "100 k");
+            break;            
+
+        case 6:
+            snprintf(buff, sizeof(buff), "M");
+            break;
+       
+        case 9:
+            snprintf(buff, sizeof(buff), "T");
+            break;
+           
+        default:
+            snprintf(buff, sizeof(buff), "1e%d ", exp);
+    }
+        
+    return buff;
+}
+
+//------------------------------------------------------------------------------
+/// Look up the data lenght from a VIF field in the data record.
+/// 
+/// See the table on page 41 the M-BUS specification.
+//------------------------------------------------------------------------------
+u_char
+mbus_dif_datalength_lookup(u_char dif)
+{
+	switch (dif&0x0F)
+	{
+		case 0x0:
+			return 0;
+		case 0x1:
+			return 1;
+		case 0x2:
+			return 2;
+		case 0x3:
+			return 3;
+		case 0x4:
+			return 4;
+		case 0x5:
+			return 4;
+		case 0x6:
+			return 6;
+		case 0x7:
+			return 8;
+
+		case 0x8:
+			return 0;
+		case 0x9:
+			return 1;
+		case 0xA:
+			return 2;
+		case 0xB:
+			return 3;
+		case 0xC:
+			return 4;
+		case 0xD: 
+            // variable data length, 
+            // data length stored in data field
+			return 0;
+		case 0xE:
+			return 6;
+		case 0xF:
+			return 8;
+
+		default: // never reached
+			return 0x00;
+
+	}
+}
+
+//------------------------------------------------------------------------------
+/// Look up the unit from a VIF field in the data record.
+/// 
+/// See section 8.4.3  Codes for Value Information Field (VIF) in the M-BUS spec
+//------------------------------------------------------------------------------
+const char *
+mbus_vif_unit_lookup(u_char vif)
+{
+    static char buff[256];
+    int n;
+
+    switch (vif & 0x7F) // ignore the extension bit in this selection
+    {
+        // E000 0nnn Energy 10(nnn-3) W
+        case 0x00:
+        case 0x00+1:
+        case 0x00+2:
+        case 0x00+3:
+        case 0x00+4:
+        case 0x00+5:
+        case 0x00+6:
+        case 0x00+7:
+            n = (vif & 0x07) - 3;
+            snprintf(buff, sizeof(buff), "Energy (%sWh)", mbus_unit_prefix(n)); 
+            break;
+
+        // 0000 1nnn          Energy       10(nnn)J     (0.001kJ to 10000kJ)
+        case 0x08:
+        case 0x08+1:
+        case 0x08+2:
+        case 0x08+3:
+        case 0x08+4:
+        case 0x08+5:
+        case 0x08+6:
+        case 0x08+7:
+            
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Energy (%sJ)", mbus_unit_prefix(n));
+  
+            break;        
+
+        // E001 1nnn Mass 10(nnn-3) kg 0.001kg to 10000kg
+        case 0x18:
+        case 0x18+1:
+        case 0x18+2:
+        case 0x18+3:
+        case 0x18+4:
+        case 0x18+5:
+        case 0x18+6:
+        case 0x18+7:
+            
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Mass (%skg)", mbus_unit_prefix(n-3));
+  
+            break;        
+
+        // E010 1nnn Power 10(nnn-3) W 0.001W to 10000W
+        case 0x28:
+        case 0x28+1:
+        case 0x28+2:
+        case 0x28+3:
+        case 0x28+4:
+        case 0x28+5:
+        case 0x28+6:
+        case 0x28+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Power (%sW)", mbus_unit_prefix(n-3));
+            //snprintf(buff, sizeof(buff), "Power (10^%d W)", n-3);
+  
+            break;
+            
+        // E011 0nnn Power 10(nnn) J/h 0.001kJ/h to 10000kJ/h
+        case 0x30:
+        case 0x30+1:
+        case 0x30+2:
+        case 0x30+3:
+        case 0x30+4:
+        case 0x30+5:
+        case 0x30+6:
+        case 0x30+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Power (%sJ/h)", mbus_unit_prefix(n));
+  
+            break;       
+
+        // E001 0nnn Volume 10(nnn-6) m3 0.001l to 10000l
+        case 0x10:
+        case 0x10+1:
+        case 0x10+2:
+        case 0x10+3:
+        case 0x10+4:
+        case 0x10+5:
+        case 0x10+6:
+        case 0x10+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Volume (%s m^3)", mbus_unit_prefix(n-6));
+  
+            break;
+        
+        // E011 1nnn Volume Flow 10(nnn-6) m3/h 0.001l/h to 10000l/
+        case 0x38:
+        case 0x38+1:
+        case 0x38+2:
+        case 0x38+3:
+        case 0x38+4:
+        case 0x38+5:
+        case 0x38+6:
+        case 0x38+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Volume flow (%s m^3/h)", mbus_unit_prefix(n-6));
+  
+            break;     
+
+        // E100 0nnn Volume Flow ext. 10(nnn-7) m3/min 0.0001l/min to 1000l/min
+        case 0x40:
+        case 0x40+1:
+        case 0x40+2:
+        case 0x40+3:
+        case 0x40+4:
+        case 0x40+5:
+        case 0x40+6:
+        case 0x40+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Volume flow (%s m^3/min)", mbus_unit_prefix(n-7));
+  
+            break;  
+
+        // E100 1nnn Volume Flow ext. 10(nnn-9) m3/s 0.001ml/s to 10000ml/
+        case 0x48:
+        case 0x48+1:
+        case 0x48+2:
+        case 0x48+3:
+        case 0x48+4:
+        case 0x48+5:
+        case 0x48+6:
+        case 0x48+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Volume flow (%s m^3/s)", mbus_unit_prefix(n-9));
+  
+            break;       
+
+        // E101 0nnn Mass flow 10(nnn-3) kg/h 0.001kg/h to 10000kg/
+        case 0x50:
+        case 0x50+1:
+        case 0x50+2:
+        case 0x50+3:
+        case 0x50+4:
+        case 0x50+5:
+        case 0x50+6:
+        case 0x50+7:
+
+            n = (vif & 0x07);
+            snprintf(buff, sizeof(buff), "Mass flow (%s kg/h)", mbus_unit_prefix(n-3));
+  
+            break;       
+
+        // E101 10nn Flow Temperature 10(nn-3) °C 0.001°C to 1°C
+        case 0x58:
+        case 0x58+1:
+        case 0x58+2:
+        case 0x58+3:
+
+            n = (vif & 0x03);
+            snprintf(buff, sizeof(buff), "Flow temperature (%sdeg C)", mbus_unit_prefix(n-3));
+  
+            break;
+
+        // E101 11nn Return Temperature 10(nn-3) °C 0.001°C to 1°C
+        case 0x5C:
+        case 0x5C+1:
+        case 0x5C+2:
+        case 0x5C+3:
+
+            n = (vif & 0x03);
+            snprintf(buff, sizeof(buff), "Return temperature (%sdeg C)", mbus_unit_prefix(n-3));
+  
+            break;
+
+        // E110 10nn Pressure 10(nn-3) bar 1mbar to 1000mbar
+        case 0x68:
+        case 0x68+1:
+        case 0x68+2:
+        case 0x68+3:
+
+            n = (vif & 0x03);
+            snprintf(buff, sizeof(buff), "Pressure (%s bar)", mbus_unit_prefix(n-3));
+  
+            break;
+
+        // E010 00nn On Time
+        // nn = 00 seconds
+        // nn = 01 minutes
+        // nn = 10   hours
+        // nn = 11    days
+        // E010 01nn Operating Time coded like OnTime
+        // E111 00nn Averaging Duration	coded like OnTime	 
+        // E111 01nn Actuality Duration coded like OnTime
+        case 0x20:
+        case 0x20+1:
+        case 0x20+2:
+        case 0x20+3:
+        case 0x24:
+        case 0x24+1:
+        case 0x24+2:
+        case 0x24+3:
+        case 0x70:
+        case 0x70+1:
+        case 0x70+2:
+        case 0x70+3:
+        case 0x74:
+        case 0x74+1:
+        case 0x74+2:
+        case 0x74+3:
+            {
+                int offset;
+
+                if      ((vif & 0x7C) == 0x20)
+                    offset = snprintf(buff, sizeof(buff), "On time ");
+                else if ((vif & 0x7C) == 0x24) 
+                    offset = snprintf(buff, sizeof(buff), "Operating time ");
+                else if ((vif & 0x7C) == 0x70)
+                    offset = snprintf(buff, sizeof(buff), "Averaging Duration ");
+                else
+                    offset = snprintf(buff, sizeof(buff), "Actuality Duration ");
+                
+                switch (vif & 0x03)
+                {
+                    case 0x00:
+                        snprintf(&buff[offset], sizeof(buff)-offset, "(seconds)");
+                        break;
+                    case 0x01:
+                        snprintf(&buff[offset], sizeof(buff)-offset, "(minutes)");
+                        break;
+                    case 0x02:
+                        snprintf(&buff[offset], sizeof(buff)-offset, "(hours)");
+                        break;
+                    case 0x03:
+                        snprintf(&buff[offset], sizeof(buff)-offset, "(days)");
+                        break;
+                }
+            }
+            break;                     
+
+        // E110 110n Time Point
+        // n = 0        date
+        // n = 1 time & date
+        // data type G
+        // data type F
+        case 0x6C:
+        case 0x6C+1:
+
+            if (vif & 0x1)
+                snprintf(buff, sizeof(buff), "Time Point (time & date)");
+            else
+                snprintf(buff, sizeof(buff), "Time Point (date)");
+  
+            break;
+            
+        // E110 00nn    Temperature Difference   10(nn-3)K   (mK to  K)
+        case 0x60:
+        case 0x60+1:
+        case 0x60+2:
+        case 0x60+3:
+
+            n = (vif & 0x03);
+            
+            snprintf(buff, sizeof(buff), "Temperature Difference (%s deg C)", mbus_unit_prefix(n-3));
+                        
+            break;
+
+        // E110 01nn External Temperature 10(nn-3) °C 0.001°C to 1°C
+        case 0x64:
+        case 0x64+1:
+        case 0x64+2:
+        case 0x64+3:
+
+            n = (vif & 0x03);
+            snprintf(buff, sizeof(buff), "External temperature (%s deg C)", mbus_unit_prefix(n-3));
+  
+            break;
+
+        // E110 1110 Units for H.C.A. dimensionless
+        case 0x6E:
+            snprintf(buff, sizeof(buff), "Units for H.C.A.");
+            break; 
+
+        // E110 1111 Reserved
+        case 0x6F:
+            snprintf(buff, sizeof(buff), "Reserved");
+            break;        
+
+        // Custom VIF in the following string: never reached...
+        case 0x7C:
+            snprintf(buff, sizeof(buff), "Custom VIF");
+            break;        
+
+        // Fabrication No
+        case 0x78:
+            snprintf(buff, sizeof(buff), "Fabrication number");
+            break;
+            
+        // Bus Address
+        case 0x7A:
+            snprintf(buff, sizeof(buff), "Bus Address");
+            break;
+
+        // Manufacturer specific: 7Fh / FF  
+        case 0x7F:
+        case 0xFF:
+            snprintf(buff, sizeof(buff), "Manufacturer specific");
+            break;
+
+        default:
+            snprintf(buff, sizeof(buff), "Unknown (VIF=0x%.2X)", vif);
+            break;
+    }
+    
+
+    return buff;
+}
+
+
+//------------------------------------------------------------------------------
+// Lookup the error message
+//
+// See section 6.6  Codes for general application errors in the M-BUS spec
+//------------------------------------------------------------------------------
+const char *
+mbus_data_error_lookup(int error)
+{
+    static char buff[256];
+    
+    switch (error)
+    {
+        case MBUS_ERROR_DATA_UNSPECIFIED:
+            snprintf(buff, sizeof(buff), "Unspecified error");
+            break;
+           
+        case MBUS_ERROR_DATA_UNIMPLEMENTED_CI:
+            snprintf(buff, sizeof(buff), "Unimplemented CI-Field");
+            break;
+            
+        case MBUS_ERROR_DATA_BUFFER_TOO_LONG:
+            snprintf(buff, sizeof(buff), "Buffer too long, truncated");
+            break;
+            
+        case MBUS_ERROR_DATA_TOO_MANY_RECORDS:
+            snprintf(buff, sizeof(buff), "Too many records");
+            break;
+            
+        case MBUS_ERROR_DATA_PREMATURE_END:
+            snprintf(buff, sizeof(buff), "Premature end of record");
+            break;
+            
+        case MBUS_ERROR_DATA_TOO_MANY_DIFES:
+            snprintf(buff, sizeof(buff), "More than 10 DIFE´s");
+            break;
+            
+        case MBUS_ERROR_DATA_TOO_MANY_VIFES:
+            snprintf(buff, sizeof(buff), "More than 10 VIFE´s");
+            break;
+            
+        case MBUS_ERROR_DATA_RESERVED:
+            snprintf(buff, sizeof(buff), "Reserved");
+            break;
+            
+        case MBUS_ERROR_DATA_APPLICATION_BUSY:
+            snprintf(buff, sizeof(buff), "Application busy");
+            break;
+            
+        case MBUS_ERROR_DATA_TOO_MANY_READOUTS:
+            snprintf(buff, sizeof(buff), "Too many readouts");
+            break;
+            
+        default:
+            snprintf(buff, sizeof(buff), "Unknown error (0x%.2X)", error);
+            break;
+    }
+    
+    return buff;
+}
+
+
+//------------------------------------------------------------------------------
+/// Lookup the unit from the VIB (VIF or VIFE)
+//
+//  Enhanced Identification
+//    E000 1000      Access Number (transmission count) 
+//    E000 1001      Medium (as in fixed header) 
+//    E000 1010      Manufacturer (as in fixed header) 
+//    E000 1011      Parameter set identification 
+//    E000 1100      Model / Version 
+//    E000 1101      Hardware version # 
+//    E000 1110      Firmware version # 
+//    E000 1111      Software version # 
+//------------------------------------------------------------------------------
+const char *
+mbus_vib_unit_lookup(mbus_value_information_block *vib)
+{   
+    static char buff[256];
+    int n;
+    
+    if (vib == NULL)
+        return "";
+
+    if (vib->vif == 0xFD || vib->vif == 0xFB) // first type of VIF extention: see table 8.4.4 
+    {
+        if (vib->nvife == 0)
+        {
+            snprintf(buff, sizeof(buff), "Missing VIF extension");
+        }
+        else if (vib->vife[0] == 0x08 || vib->vife[0] == 0x88)
+        {
+            // E000 1000 
+            snprintf(buff, sizeof(buff), "Access Number (transmission count)");
+        }
+        else if (vib->vife[0] == 0x09|| vib->vife[0] == 0x89)
+        {
+            // E000 1001
+            snprintf(buff, sizeof(buff), "Medium (as in fixed header)");
+        }
+        else if (vib->vife[0] == 0x0A || vib->vife[0] == 0x8A)
+        {
+            // E000 1010
+            snprintf(buff, sizeof(buff), "Manufacturer (as in fixed header)");
+        }
+        else if (vib->vife[0] == 0x0B || vib->vife[0] == 0x8B)
+        {
+            // E000 1010
+            snprintf(buff, sizeof(buff), "Parameter set identification");
+        }
+        else if (vib->vife[0] == 0x0C || vib->vife[0] == 0x8C)
+        {
+            // E000 1100
+            snprintf(buff, sizeof(buff), "Model / Version");
+        }
+        else if (vib->vife[0] == 0x0D || vib->vife[0] == 0x8D)
+        {
+            // E000 1100
+            snprintf(buff, sizeof(buff), "Hardware version");
+        }
+        else if (vib->vife[0] == 0x0E || vib->vife[0] == 0x8E)
+        {
+            // E000 1101
+            snprintf(buff, sizeof(buff), "Firmware version");
+        }
+        else if (vib->vife[0] == 0x0F || vib->vife[0] == 0x8F)
+        {
+            // E000 1101
+            snprintf(buff, sizeof(buff), "Software version");
+        }
+        else if (vib->vife[0] == 0x17 || vib->vife[0] == 0x97)
+		{
+            // VIFE = E001 0111 Error flags
+            snprintf(buff, sizeof(buff), "Error flags");
+		}
+        else if (vib->vife[0] == 0x10)
+        {
+            // VIFE = E001 0000 Customer location
+            snprintf(buff, sizeof(buff), "Customer location");
+        }
+        else if (vib->vife[0] == 0x0C)
+        {
+            // E000 1100 Model / Version
+            snprintf(buff, sizeof(buff), "Model / Version");
+        }
+        else if (vib->vife[0] == 0x11)
+        {
+            // VIFE = E001 0001 Customer
+            snprintf(buff, sizeof(buff), "Customer");
+        }
+        else if (vib->vife[0] == 0x9)
+        {
+            // VIFE = E001 0110 Password
+            snprintf(buff, sizeof(buff), "Password");
+        }
+        else if (vib->vife[0] == 0x0b)
+        {
+            // VIFE = E000 1011 Parameter set identification
+            snprintf(buff, sizeof(buff), "Parameter set identification");
+        }
+        else if ((vib->vife[0] & 0x70) == 0x40)
+        {
+            // VIFE = E100 nnnn 10^(nnnn-9) V
+            n = (vib->vife[0] & 0x0F);
+            snprintf(buff, sizeof(buff), "%s V", mbus_unit_prefix(n-9));
+        }
+        else if ((vib->vife[0] & 0x70) == 0x50)
+        {
+            // VIFE = E101 nnnn 10nnnn-12 A
+            n = (vib->vife[0] & 0x0F);
+            snprintf(buff, sizeof(buff), "%s A", mbus_unit_prefix(n-12));
+        }
+        else if ((vib->vife[0] & 0xF0) == 0x70)
+        {
+            // VIFE = E111 nnn Reserved
+            snprintf(buff, sizeof(buff), "Reserved VIF extension");
+        }
+        else
+        {
+            snprintf(buff, sizeof(buff), "Unrecongized VIF extension: 0x%.2x", vib->vife[0]);
+        }
+        return buff;
+    }
+    else if (vib->vif == 0x7C)
+    {
+        // custom VIF
+        snprintf(buff, sizeof(buff), "%s", vib->custom_vif);
+        return buff;
+    }
+
+    return mbus_vif_unit_lookup(vib->vif); // no extention, use VIF
+}
+
+//------------------------------------------------------------------------------
+// Decode data and write to string
+//
+// Data format (for record->data data array)
+// 
+// Length in Bit   Code    Meaning           Code      Meaning
+//      0          0000    No data           1000      Selection for Readout
+//      8          0001     8 Bit Integer    1001      2 digit BCD
+//     16          0010    16 Bit Integer    1010      4 digit BCD
+//     24          0011    24 Bit Integer    1011      6 digit BCD
+//     32          0100    32 Bit Integer    1100      8 digit BCD
+//   32 / N        0101    32 Bit Real       1101      variable length
+//     48          0110    48 Bit Integer    1110      12 digit BCD
+//     64          0111    64 Bit Integer    1111      Special Functions
+//
+// The Code is stored in record->drh.dib.dif
+//
+///
+/// Return a string containing the data
+///
+// Source: MBDOC48.PDF
+//
+//------------------------------------------------------------------------------
+const char *
+mbus_data_record_decode(mbus_data_record *record)
+{
+    static char buff[768];
+    u_char vif, vife;
+    
+    // ignore extension bit
+    vif = (record->drh.vib.vif & 0x7F);       
+    vife = (record->drh.vib.vife[0] & 0x7F);
+
+    if (record)
+    {
+        int val;
+        float val3;
+        long long val4;
+        struct tm time;
+            
+        switch (record->drh.dib.dif & 0x0F)
+        {
+            case 0x00: // no data
+        
+                buff[0] = 0;
+                
+                break; 
+
+            case 0x01: // 1 byte integer (8 bit)
+        
+                val = mbus_data_int_decode(record->data, 1);
+               
+                snprintf(buff, sizeof(buff), "%d", val);
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 1 byte integer\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break; 
+
+
+            case 0x02: // 2 byte (16 bit)
+                
+                // E110 1100  Time Point (date)
+                if (vif == 0x6C)            
+                {
+                    mbus_data_tm_decode(&time, record->data, 2);
+                    snprintf(buff, sizeof(buff), "%04d-%02d-%02d", 
+                                                 (time.tm_year + 2000), 
+                                                 (time.tm_mon + 1), 
+                                                  time.tm_mday);
+                }
+                else  // 2 byte integer
+                {
+                    val = mbus_data_int_decode(record->data, 2);
+                    snprintf(buff, sizeof(buff), "%d", val);
+                    if (debug)
+                        printf("%s: DIF 0x%.2x was decoded using 2 byte integer\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                }
+        
+                break; 
+
+            case 0x03: // 3 byte integer (24 bit)
+
+                val = mbus_data_int_decode(record->data, 3);
+                        
+                snprintf(buff, sizeof(buff), "%d", val);
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 3 byte integer\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break; 
+                
+            case 0x04: // 4 byte (32 bit)
+            
+                // E110 1101  Time Point (date/time)
+                // E011 0000  Start (date/time) of tariff
+                // E111 0000  Date and time of battery change
+                if ( (vif == 0x6D) ||                                     
+                    ((record->drh.vib.vif == 0xFD) && (vife == 0x30)) ||  
+                    ((record->drh.vib.vif == 0xFD) && (vife == 0x70)))    
+                {
+                    mbus_data_tm_decode(&time, record->data, 4);
+                    snprintf(buff, sizeof(buff), "%04d-%02d-%02dT%02d:%02d:%02d", 
+                                                 (time.tm_year + 2000), 
+                                                 (time.tm_mon + 1), 
+                                                  time.tm_mday,
+                                                  time.tm_hour,
+                                                  time.tm_min,
+                                                  time.tm_sec);
+                }
+                else  // 4 byte integer
+                {
+                    val = mbus_data_int_decode(record->data, 4);
+                    snprintf(buff, sizeof(buff), "%d", val);
+                }
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 4 byte integer\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;  
+
+            case 0x05: // 4 Byte Real (32 bit)
+            
+                val3 = mbus_data_float_decode(record->data);
+                
+                snprintf(buff, sizeof(buff), "%f", val3);
+                
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 4 byte Real\n", __PRETTY_FUNCTION__, record->drh.dib.dif);    
+                    
+                break;
+
+            case 0x06: // 6 byte integer (48 bit)
+
+                val4 = mbus_data_long_long_decode(record->data, 6);
+        
+                snprintf(buff, sizeof(buff), "%lld", val4);
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 6 byte integer\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;          
+
+            case 0x07: // 8 byte integer (64 bit)
+
+                val4 = mbus_data_long_long_decode(record->data, 8);
+        
+                snprintf(buff, sizeof(buff), "%lld", val4);
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 8 byte integer\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;          
+
+            //case 0x08: 
+
+            case 0x09: // 2 digit BCD (8 bit)
+  
+                val = (int)mbus_data_bcd_decode(record->data, 1);  
+                snprintf(buff, sizeof(buff), "%d", val);     
+        
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 2 digit BCD\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;
+                
+            case 0x0A: // 4 digit BCD (16 bit)
+        
+                val = (int)mbus_data_bcd_decode(record->data, 2);  
+                snprintf(buff, sizeof(buff), "%d", val);     
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 4 digit BCD\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;
+
+            case 0x0B: // 6 digit BCD (24 bit)
+
+                val = (int)mbus_data_bcd_decode(record->data, 3);  
+                snprintf(buff, sizeof(buff), "%d", val);     
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 6 digit BCD\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;
+                
+            case 0x0C: // 8 digit BCD (32 bit)
+
+                val = (int)mbus_data_bcd_decode(record->data, 4);  
+                snprintf(buff, sizeof(buff), "%d", val);
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 8 digit BCD\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;
+                                                                  
+            case 0x0E: // 12 digit BCD (48 bit)
+
+                val4 = mbus_data_bcd_decode(record->data, 6);  
+                snprintf(buff, sizeof(buff), "%lld", val4);
+
+                if (debug)
+                    printf("%s: DIF 0x%.2x was decoded using 12 digit BCD\n", __PRETTY_FUNCTION__, record->drh.dib.dif);
+
+                break;
+
+            case 0x0F: // special functions
+            
+                mbus_data_bin_decode(buff, record->data, record->data_len, sizeof(buff));
+                break;
+
+            case 0x0D: // variable length
+                if (record->data_len <= 0xBF)
+                {
+                    mbus_data_str_decode(buff, record->data, record->data_len);
+                    break;
+                }
+                /* FALLTHROUGH */
+
+            default:
+        
+                snprintf(buff, sizeof(buff), "Unknown DIF (0x%.2x)", record->drh.dib.dif);
+                break;
+        }
+
+        return buff;     
+    }
+
+    return NULL;
+}
+//------------------------------------------------------------------------------
+/// Return the unit description for a variable-length data record
+//------------------------------------------------------------------------------
+const char *
+mbus_data_record_unit(mbus_data_record *record)
+{
+    static char buff[128];
+    
+    if (record)
+    {
+        snprintf(buff, sizeof(buff), "%s", mbus_vib_unit_lookup(&(record->drh.vib)));
+                 
+        return buff;    
+    }
+    
+    return NULL;
+}
+
+//------------------------------------------------------------------------------
+/// Return the value for a variable-length data record
+//------------------------------------------------------------------------------
+const char *
+mbus_data_record_value(mbus_data_record *record)
+{
+    static char buff[768];
+    
+    if (record)
+    {
+        snprintf(buff, sizeof(buff), "%s", mbus_data_record_decode(record));
+                 
+        return buff;    
+    }
+    
+    return NULL;
+}
+
+//------------------------------------------------------------------------------
+/// Return a string containing the function description
+//------------------------------------------------------------------------------
+const char *
+mbus_data_record_function(mbus_data_record *record)
+{
+    static char buff[128];
+    
+    if (record)
+    {
+        switch (record->drh.dib.dif & MBUS_DATA_RECORD_DIF_MASK_FUNCTION)
+        {
+            case 0x00:
+                snprintf(buff, sizeof(buff), "Instantaneous value");           
+                break;
+
+            case 0x10:
+                snprintf(buff, sizeof(buff), "Maximum value");
+                break;
+
+            case 0x20:
+                snprintf(buff, sizeof(buff), "Minimum value");
+                break;
+
+            case 0x30:
+                snprintf(buff, sizeof(buff), "Value during error state");                       
+                break;
+        
+            default:
+                snprintf(buff, sizeof(buff), "unknown");                 
+        }
+
+        return buff;    
+    }
+    
+    return NULL;
+}
+
+
+///
+/// For fixed-length frames, return a string describing the type of value (stored or actual)
+///
+const char *
+mbus_data_fixed_function(int status)
+{
+    static char buff[128];
+
+    snprintf(buff, sizeof(buff), "%s",
+            (status & MBUS_DATA_FIXED_STATUS_DATE_MASK) == MBUS_DATA_FIXED_STATUS_DATE_STORED ? 
+            "Stored value" : "Actual value" );
+    
+    return buff;
+}
+
+//------------------------------------------------------------------------------
+//
+// PARSER FUNCTIONS
+//
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// PARSE M-BUS frame data structures from binary data.
+//------------------------------------------------------------------------------
+int
+mbus_parse(mbus_frame *frame, u_char *data, size_t data_size)
+{
+    size_t i, len;
+    
+    if (frame && data && data_size > 0)
+    {
+        if (parse_debug) 
+            printf("%s: Attempting to parse binary data [size = %zu]\n", __PRETTY_FUNCTION__, data_size);
+
+        if (parse_debug) 
+            printf("%s: ", __PRETTY_FUNCTION__);
+
+        for (i = 0; i < data_size && parse_debug; i++)
+        {
+            printf("%.2X ", data[i] & 0xFF);
+        }
+
+        if (parse_debug)
+            printf("\n%s: done.\n", __PRETTY_FUNCTION__);
+    
+        switch (data[0])
+        {
+            case MBUS_FRAME_ACK_START:
+            
+                // OK, got a valid ack frame, require no more data 
+                frame->start1   = data[0];
+                frame->type = MBUS_FRAME_TYPE_ACK;
+                return 0;
+                //return MBUS_FRAME_BASE_SIZE_ACK - 1; // == 0
+                              
+            case MBUS_FRAME_SHORT_START:
+            
+                if (data_size < MBUS_FRAME_BASE_SIZE_SHORT)
+                {
+                    // OK, got a valid short packet start, but we need more data
+                    return MBUS_FRAME_BASE_SIZE_SHORT - data_size;
+                }
+                
+                if (data_size != MBUS_FRAME_BASE_SIZE_SHORT)
+                {
+                    snprintf(error_str, sizeof(error_str), "Too much data in frame.");
+                
+                    // too much data... ?
+                    return -2;
+                }
+                
+                // init frame data structure
+                frame->start1   = data[0];
+                frame->control  = data[1];
+                frame->address  = data[2];
+                frame->checksum = data[3];
+                frame->stop     = data[4];
+                
+                frame->type = MBUS_FRAME_TYPE_SHORT;
+
+                // verify the frame
+                if (mbus_frame_verify(frame) != 0)
+                {
+                    return -3;
+                }
+
+                // successfully parsed data
+                return 0;
+
+            case MBUS_FRAME_LONG_START: // (also CONTROL)
+            
+                if (data_size < 3)
+                {
+                    // OK, got a valid long/control packet start, but we need
+                    // more data to determine the length
+                    return 3 - data_size;
+                }
+                
+                // init frame data structure
+                frame->start1   = data[0];
+                frame->length1  = data[1];
+                frame->length2  = data[2];
+                
+                if (frame->length1 != frame->length2)
+                {
+                    snprintf(error_str, sizeof(error_str), "Invalid M-Bus frame length.");
+                
+                    // not a valid M-bus frame
+                    return -2;
+                }
+                
+                // check length of packet:
+                len = frame->length1;
+                
+                if (data_size < (size_t)(MBUS_FRAME_FIXED_SIZE_LONG + len))
+                {
+                    // OK, but we need more data
+                    return MBUS_FRAME_FIXED_SIZE_LONG + len - data_size;                
+                }
+                
+                if (data_size > (size_t)(MBUS_FRAME_FIXED_SIZE_LONG + len))
+                {
+                    snprintf(error_str, sizeof(error_str), "Too much data in frame.");
+                
+                    // too much data... ?
+                    return -2;
+                }
+                
+                // we got the whole packet, continue parsing
+                frame->start2   = data[3];
+                frame->control  = data[4];
+                frame->address  = data[5];
+                frame->control_information = data[6];
+                
+                frame->data_size = len - 3;
+                for (i = 0; i < frame->data_size; i++)
+                {
+                    frame->data[i] = data[7 + i];
+                }                              
+                                
+                frame->checksum = data[data_size-2]; // data[6 + frame->data_size + 1]
+                frame->stop     = data[data_size-1]; // data[6 + frame->data_size + 2]          
+                
+                if (frame->data_size == 0)
+                {
+                    frame->type = MBUS_FRAME_TYPE_CONTROL;
+                }
+                else
+                {
+                    frame->type = MBUS_FRAME_TYPE_LONG;                
+                }
+
+                // verify the frame
+                if (mbus_frame_verify(frame) != 0)
+                {
+                    return -3;
+                }
+
+                // successfully parsed data
+                return 0;        
+            default:
+                snprintf(error_str, sizeof(error_str), "Invalid M-Bus frame start.");
+                
+                // not a valid M-Bus frame header (start byte)
+                return -4;
+        }    
+        
+    }
+    
+    snprintf(error_str, sizeof(error_str), "Got null pointer to frame, data or zero data_size.");
+
+    return -1;
+}
+
+
+//------------------------------------------------------------------------------
+/// Parse the fixed-length data of a M-Bus frame
+//------------------------------------------------------------------------------
+int
+mbus_data_fixed_parse(mbus_frame *frame, mbus_data_fixed *data)
+{
+    if (frame && data)
+    {  
+        // copy the fixed-length data structure
+        memcpy((void *)data, (void *)(frame->data), sizeof(mbus_data_fixed));
+
+        return 0;
+    }    
+
+    return -1;
+}
+
+
+//------------------------------------------------------------------------------
+/// Parse the variable-length data of a M-Bus frame
+//------------------------------------------------------------------------------
+int
+mbus_data_variable_parse(mbus_frame *frame, mbus_data_variable *data)
+{
+    mbus_data_record *record;
+    size_t i, j;
+    
+    if (frame && data)
+    {
+        // parse header
+        data->nrecords = 0;
+        data->more_records_follow = 0;
+        i = sizeof(mbus_data_variable_header);
+        if(frame->data_size < i)
+            return -1;
+
+        // first copy the variable data fixed header
+        memcpy((void *)&(data->header), (void *)(frame->data), i);
+
+        data->record = NULL;
+
+        while (i < frame->data_size)
+        {
+            if ((record = mbus_data_record_new()) == NULL)
+            {
+                // clean up...
+                return (-2);
+            }
+            
+            // copy timestamp
+            memcpy((void *)&(record->timestamp), (void *)&(frame->timestamp), sizeof(time_t));
+
+            // read and parse DIB (= DIF + DIFE)
+        
+            // DIF
+            record->drh.dib.dif = frame->data[i];
+
+            if (record->drh.dib.dif == 0x0F || record->drh.dib.dif == 0x1F)
+            {
+                if ((record->drh.dib.dif & 0xFF) == 0x1F)
+                {
+                  data->more_records_follow = 1;
+                }
+                
+                i++;
+                // just copy the remaining data as it is vendor specific
+                record->data_len = frame->data_size - i;
+                for (j = 0; j < record->data_len; j++)
+                {
+                    record->data[j] = frame->data[i++];
+                }
+
+                // append the record and move on to next one
+                mbus_data_record_append(data, record);
+                data->nrecords++;
+                continue;
+            }
+        
+            // calculate length of data record         
+            record->data_len = mbus_dif_datalength_lookup(record->drh.dib.dif);       
+
+            // read DIF extensions
+            record->drh.dib.ndife = 0;
+            while (frame->data[i] & MBUS_DIB_DIF_EXTENSION_BIT && 
+                   record->drh.dib.ndife < NITEMS(record->drh.dib.dife))
+            {
+                u_char dife = frame->data[i+1];
+                record->drh.dib.dife[record->drh.dib.ndife] = dife;
+
+                record->drh.dib.ndife++;
+                i++;
+            }
+            i++;
+
+            // read and parse VIB (= VIF + VIFE)
+
+            // VIF
+            record->drh.vib.vif = frame->data[i]; 
+                
+            if (record->drh.vib.vif == 0x7C)
+            {
+                // variable length VIF in ASCII format
+                int var_vif_len;
+                i++;
+                var_vif_len = frame->data[i++];
+                mbus_data_str_decode(record->drh.vib.custom_vif, &(frame->data[i]), var_vif_len);
+                i += var_vif_len;
+            }
+            else
+            {
+                // VIFE
+                record->drh.vib.nvife = 0;
+                while (frame->data[i] & MBUS_DIB_VIF_EXTENSION_BIT && 
+                       record->drh.vib.nvife < NITEMS(record->drh.vib.vife))
+                {
+                    u_char vife = frame->data[i+1];
+                    record->drh.vib.vife[record->drh.vib.nvife] = vife;
+                
+                    record->drh.vib.nvife++;
+                    i++;
+                }
+                i++;       
+            }
+                
+            // re-calculate data length, if of variable length type
+            if ((record->drh.dib.dif & 0x0F) == 0x0D) // flag for variable length data
+            {
+                if(frame->data[i] <= 0xBF)
+                    record->data_len = frame->data[i++];
+                else if(frame->data[i] >= 0xC0 && frame->data[i] <= 0xCF)
+                    record->data_len = (frame->data[i++] - 0xC0) * 2;
+                else if(frame->data[i] >= 0xD0 && frame->data[i] <= 0xDF)
+                    record->data_len = (frame->data[i++] - 0xD0) * 2;
+                else if(frame->data[i] >= 0xE0 && frame->data[i] <= 0xEF)
+                    record->data_len = frame->data[i++] - 0xE0;
+                else if(frame->data[i] >= 0xF0 && frame->data[i] <= 0xFA)
+                    record->data_len = frame->data[i++] - 0xF0;
+            }
+
+            // copy data
+            for (j = 0; j < record->data_len; j++)
+            {
+                record->data[j] = frame->data[i++];       
+            }
+
+            // append the record and move on to next one
+            mbus_data_record_append(data, record);
+            data->nrecords++;
+        }
+        
+        return 0;
+    }    
+    
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+/// Check the stype of the frame data (error, fixed or variable) and dispatch to the
+/// corresponding parser function.
+//------------------------------------------------------------------------------
+int
+mbus_frame_data_parse(mbus_frame *frame, mbus_frame_data *data)
+{
+    if (frame && data)
+    {
+        if (frame->control_information == MBUS_CONTROL_INFO_ERROR_GENERAL)
+        {
+            data->type = MBUS_DATA_TYPE_ERROR;
+            
+            if (frame->data_size > 0)
+            {
+                data->error = (int) frame->data[0];
+            }
+            else
+            {
+                data->error = 0;
+            }
+            
+            return 0;
+        }
+        else if (frame->control_information == MBUS_CONTROL_INFO_RESP_FIXED)
+        {
+            if (frame->data_size == 0)
+            {
+                snprintf(error_str, sizeof(error_str), "Got zero data_size.");
+                
+                return -1;
+            }
+        
+            data->type = MBUS_DATA_TYPE_FIXED;
+            return mbus_data_fixed_parse(frame, &(data->data_fix));
+        }
+        else if (frame->control_information == MBUS_CONTROL_INFO_RESP_VARIABLE)
+        {
+            if (frame->data_size == 0)
+            {
+                snprintf(error_str, sizeof(error_str), "Got zero data_size.");
+                
+                return -1;
+            }
+            
+            data->type = MBUS_DATA_TYPE_VARIABLE;
+            return mbus_data_variable_parse(frame, &(data->data_var));
+        }
+        else
+        {
+            snprintf(error_str, sizeof(error_str), "Unknown control information 0x%.2x", frame->control_information);
+            
+            return -1;
+        }
+    }
+    
+    snprintf(error_str, sizeof(error_str), "Got null pointer to frame or data.");
+
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+/// Pack the M-bus frame into a binary string representation that can be sent 
+/// on the bus. The binary packet format is different for the different types
+/// of M-bus frames.
+//------------------------------------------------------------------------------
+int
+mbus_frame_pack(mbus_frame *frame, u_char *data, size_t data_size)
+{
+    size_t i, offset = 0;
+    
+    if (frame && data)
+    {   
+        if (mbus_frame_calc_length(frame) == -1)
+        {
+            return -2;
+        }
+
+        if (mbus_frame_calc_checksum(frame) == -1)
+        {
+            return -3;
+        }
+        
+        switch (frame->type)
+        {
+            case MBUS_FRAME_TYPE_ACK:
+            
+                if (data_size < MBUS_FRAME_ACK_BASE_SIZE)
+                {
+                    return -4;
+                }
+        
+                data[offset++] = frame->start1; 
+        
+                return offset;        
+
+            case MBUS_FRAME_TYPE_SHORT:
+
+                if (data_size < MBUS_FRAME_SHORT_BASE_SIZE)
+                {
+                    return -4;
+                }
+                
+                data[offset++] = frame->start1;
+                data[offset++] = frame->control;
+                data[offset++] = frame->address;
+                data[offset++] = frame->checksum;
+                data[offset++] = frame->stop;
+        
+                return offset;        
+
+            case MBUS_FRAME_TYPE_CONTROL:
+        
+                if (data_size < MBUS_FRAME_CONTROL_BASE_SIZE)
+                {
+                    return -4;
+                }
+                
+                data[offset++] = frame->start1;
+                data[offset++] = frame->length1;
+                data[offset++] = frame->length2;
+                data[offset++] = frame->start2;
+
+                data[offset++] = frame->control;
+                data[offset++] = frame->address;
+                data[offset++] = frame->control_information;
+
+                data[offset++] = frame->checksum;
+                data[offset++] = frame->stop;    
+        
+                return offset;        
+
+            case MBUS_FRAME_TYPE_LONG:
+
+                if (data_size < frame->data_size + MBUS_FRAME_LONG_BASE_SIZE)
+                {
+                    return -4;
+                }
+
+                data[offset++] = frame->start1;
+                data[offset++] = frame->length1;
+                data[offset++] = frame->length2;
+                data[offset++] = frame->start2;
+
+                data[offset++] = frame->control;
+                data[offset++] = frame->address;
+                data[offset++] = frame->control_information;
+
+                for (i = 0; i < frame->data_size; i++)
+                {
+                    data[offset++] = frame->data[i];
+                }
+                
+                data[offset++] = frame->checksum;
+                data[offset++] = frame->stop;    
+        
+                return offset;      
+            
+            default:
+                return -5;
+        }        
+    }
+
+    return -1;
+}
+
+
+//------------------------------------------------------------------------------
+/// pack the data stuctures into frame->data
+//------------------------------------------------------------------------------
+int
+mbus_frame_internal_pack(mbus_frame *frame, mbus_frame_data *frame_data)
+{
+    mbus_data_record *record;
+    int j;
+
+    if (frame == NULL || frame_data == NULL)
+        return -1;
+
+    frame->data_size = 0;
+
+    switch (frame_data->type)
+    {
+        case MBUS_DATA_TYPE_ERROR:
+        
+            frame->data[frame->data_size++] = (char) frame_data->error;
+        
+            break;
+    
+        case MBUS_DATA_TYPE_FIXED:
+
+            //
+            // pack fixed data structure
+            //
+            frame->data[frame->data_size++] = frame_data->data_fix.id_bcd[0];
+            frame->data[frame->data_size++] = frame_data->data_fix.id_bcd[1];
+            frame->data[frame->data_size++] = frame_data->data_fix.id_bcd[2];
+            frame->data[frame->data_size++] = frame_data->data_fix.id_bcd[3];
+            frame->data[frame->data_size++] = frame_data->data_fix.tx_cnt;
+            frame->data[frame->data_size++] = frame_data->data_fix.status;
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt1_type;
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt2_type;
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt1_val[0];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt1_val[1];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt1_val[2];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt1_val[3];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt2_val[0];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt2_val[1];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt2_val[2];
+            frame->data[frame->data_size++] = frame_data->data_fix.cnt2_val[3];
+
+            break;
+
+        case MBUS_DATA_TYPE_VARIABLE:
+
+            //
+            // first pack variable data structure header
+            //
+            frame->data[frame->data_size++] = frame_data->data_var.header.id_bcd[0];
+            frame->data[frame->data_size++] = frame_data->data_var.header.id_bcd[1];
+            frame->data[frame->data_size++] = frame_data->data_var.header.id_bcd[2];
+            frame->data[frame->data_size++] = frame_data->data_var.header.id_bcd[3];
+            frame->data[frame->data_size++] = frame_data->data_var.header.manufacturer[0];
+            frame->data[frame->data_size++] = frame_data->data_var.header.manufacturer[1];
+            frame->data[frame->data_size++] = frame_data->data_var.header.version;
+            frame->data[frame->data_size++] = frame_data->data_var.header.medium;
+            frame->data[frame->data_size++] = frame_data->data_var.header.access_no;
+            frame->data[frame->data_size++] = frame_data->data_var.header.status;
+            frame->data[frame->data_size++] = frame_data->data_var.header.signature[0];
+            frame->data[frame->data_size++] = frame_data->data_var.header.signature[1];
+
+            //
+            // pack all data records
+            //
+            for (record = frame_data->data_var.record; record; record = record->next)
+            {
+                // pack DIF
+                if (parse_debug)
+                    printf("%s: packing DIF [%zu]", __PRETTY_FUNCTION__, frame->data_size);
+                frame->data[frame->data_size++] = record->drh.dib.dif;
+                for (j = 0; j < record->drh.dib.ndife; j++)
+                {
+                    frame->data[frame->data_size++] = record->drh.dib.dife[j];
+                }
+
+                // pack VIF
+                if (parse_debug)
+                    printf("%s: packing VIF [%zu]", __PRETTY_FUNCTION__, frame->data_size);
+                frame->data[frame->data_size++] = record->drh.vib.vif;
+                for (j = 0; j < record->drh.vib.nvife; j++)
+                {
+                    frame->data[frame->data_size++] = record->drh.vib.vife[j];
+                }
+
+                // pack data
+                if (parse_debug) 
+                    printf("%s: packing data [%zu : %zu]", __PRETTY_FUNCTION__, frame->data_size, record->data_len);
+                for (j = 0; j < record->data_len; j++)
+                {
+                    frame->data[frame->data_size++] = record->data[j];
+                }
+            }         
+            
+            break;
+
+        default:
+            return -2;
+    }
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------
+//
+// Print/Dump functions
+//
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+/// Switch parse debugging 
+//------------------------------------------------------------------------------
+void
+mbus_parse_set_debug(int debug)
+{
+    parse_debug = debug;
+}
+
+//------------------------------------------------------------------------------
+/// Dump frame in HEX on standard output
+//------------------------------------------------------------------------------
+int
+mbus_frame_print(mbus_frame *frame)
+{
+    mbus_frame *iter;
+    u_char data_buff[256];
+    int len, i;
+
+    if (frame == NULL)
+        return -1;
+
+    for (iter = frame; iter; iter = iter->next)
+    {
+        if ((len = mbus_frame_pack(iter, data_buff, sizeof(data_buff))) == -1)
+        {
+            return -2;
+        }
+
+        printf("%s: Dumping M-Bus frame [type %d, %d bytes]: ", __PRETTY_FUNCTION__, iter->type, len);
+        for (i = 0; i < len; i++)
+        {
+            printf("%.2X ", data_buff[i]);
+        }
+        printf("\n");
+    }
+
+    return 0;
+}
+
+//------------------------------------------------------------------------------
+///
+/// Print the data part of a frame.
+///
+//------------------------------------------------------------------------------
+int
+mbus_frame_data_print(mbus_frame_data *data)
+{
+    if (data)
+    {
+        if (data->type == MBUS_DATA_TYPE_ERROR)
+        {
+            return mbus_data_error_print(data->error); 
+        }
+    
+        if (data->type == MBUS_DATA_TYPE_FIXED)
+        {
+            return mbus_data_fixed_print(&(data->data_fix));
+        }
+        
+        if (data->type == MBUS_DATA_TYPE_VARIABLE)
+        {
+            return mbus_data_variable_print(&(data->data_var));
+        }
+    }
+    
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+/// Print M-bus frame info to stdout
+//------------------------------------------------------------------------------
+int
+mbus_data_variable_header_print(mbus_data_variable_header *header)
+{
+    if (header)
+    {
+        printf("%s: ID           = %lld\n", __PRETTY_FUNCTION__, 
+               mbus_data_bcd_decode(header->id_bcd, 4));
+
+        printf("%s: Manufacturer = 0x%.2X%.2X\n", __PRETTY_FUNCTION__,
+               header->manufacturer[1], header->manufacturer[0]);
+               
+        printf("%s: Manufacturer = %s\n", __PRETTY_FUNCTION__,
+               mbus_decode_manufacturer(header->manufacturer[0], header->manufacturer[1]));               
+
+        printf("%s: Version      = 0x%.2X\n", __PRETTY_FUNCTION__, header->version);
+        printf("%s: Medium       = %s (0x%.2X)\n", __PRETTY_FUNCTION__, mbus_data_variable_medium_lookup(header->medium), header->medium);
+        printf("%s: Access #     = 0x%.2X\n", __PRETTY_FUNCTION__, header->access_no);
+        printf("%s: Status       = 0x%.2X\n", __PRETTY_FUNCTION__, header->status);
+        printf("%s: Signature    = 0x%.2X%.2X\n", __PRETTY_FUNCTION__,
+               header->signature[1], header->signature[0]);
+
+    }
+    
+    return -1;
+}
+
+int
+mbus_data_variable_print(mbus_data_variable *data)
+{
+    mbus_data_record *record;
+    size_t j;
+    
+    if (data)
+    {   
+        mbus_data_variable_header_print(&(data->header));
+    
+        for (record = data->record; record; record = record->next)
+        {
+            // DIF
+            printf("DIF           = %.2X\n", record->drh.dib.dif);
+            printf("DIF.Extension = %s\n",  (record->drh.dib.dif & MBUS_DIB_DIF_EXTENSION_BIT) ? "Yes":"No");        
+            printf("DIF.Function  = %s\n",  (record->drh.dib.dif & 0x30) ? "Minimum value" : "Instantaneous value" );
+            printf("DIF.Data      = %.2X\n", record->drh.dib.dif & 0x0F);
+
+            // VENDOR SPECIFIC
+            if (record->drh.dib.dif == 0x0F || record->drh.dib.dif == 0x1F) //MBUS_DIB_DIF_VENDOR_SPECIFIC)
+            {
+                printf("%s: VENDOR DATA [size=%zd] = ", __PRETTY_FUNCTION__, record->data_len);
+                for (j = 0; j < record->data_len; j++)
+                {
+                    printf("%.2X ", record->data[j]);        
+                }
+                printf("\n");
+                
+                if (record->drh.dib.dif == 0x1F)
+                {
+                  printf("%s: More records follow in next telegram\n", __PRETTY_FUNCTION__);
+                }
+                continue;
+            }
+        
+            // calculate length of data record
+            printf("DATA LENGTH = %zd\n", record->data_len);
+
+            // DIFE
+            for (j = 0; j < record->drh.dib.ndife; j++)
+            {
+                u_char dife = record->drh.dib.dife[j];
+                
+                printf("DIFE[%zd]           = %.2X\n", j,  dife);
+                printf("DIFE[%zd].Extension = %s\n",   j, (dife & MBUS_DIB_DIF_EXTENSION_BIT) ? "Yes" : "No");        
+                printf("DIFE[%zd].Function  = %s\n",   j, (dife & 0x30) ? "Minimum value" : "Instantaneous value" );
+                printf("DIFE[%zd].Data      = %.2X\n", j,  dife & 0x0F);            
+            }
+   
+        }
+    }
+    
+    return -1;
+}
+
+int
+mbus_data_fixed_print(mbus_data_fixed *data)
+{
+    if (data)
+    {    
+        printf("%s: ID       = %d\n", __PRETTY_FUNCTION__, (int)mbus_data_bcd_decode(data->id_bcd, 4));
+        printf("%s: Access # = 0x%.2X\n", __PRETTY_FUNCTION__, data->tx_cnt);
+        printf("%s: Status   = 0x%.2X\n", __PRETTY_FUNCTION__, data->status);
+        printf("%s: Function = %s\n", __PRETTY_FUNCTION__, mbus_data_fixed_function(data->status));
+        
+        printf("%s: Medium1  = %s\n", __PRETTY_FUNCTION__, mbus_data_fixed_medium(data));
+        printf("%s: Unit1    = %s\n", __PRETTY_FUNCTION__, mbus_data_fixed_unit(data->cnt1_type));
+        if ((data->status & MBUS_DATA_FIXED_STATUS_FORMAT_MASK) == MBUS_DATA_FIXED_STATUS_FORMAT_BCD)
+        {
+            printf("%s: Counter1 = %d\n", __PRETTY_FUNCTION__, (int)mbus_data_bcd_decode(data->cnt1_val, 4));
+        }
+        else
+        {
+            printf("%s: Counter1 = %d\n", __PRETTY_FUNCTION__, mbus_data_int_decode(data->cnt1_val, 4));
+        }
+
+        printf("%s: Medium2  = %s\n", __PRETTY_FUNCTION__, mbus_data_fixed_medium(data));
+        printf("%s: Unit2    = %s\n", __PRETTY_FUNCTION__, mbus_data_fixed_unit(data->cnt2_type));
+        if ((data->status & MBUS_DATA_FIXED_STATUS_FORMAT_MASK) == MBUS_DATA_FIXED_STATUS_FORMAT_BCD)
+        {
+            printf("%s: Counter2 = %d\n", __PRETTY_FUNCTION__, (int)mbus_data_bcd_decode(data->cnt2_val, 4));
+        }
+        else
+        {
+            printf("%s: Counter2 = %d\n", __PRETTY_FUNCTION__, mbus_data_int_decode(data->cnt2_val, 4));        
+        }          
+    }
+    
+    return -1;
+}
+
+void
+mbus_hex_dump(const char *label, const char *buff, size_t len)
+{
+    time_t rawtime;
+    struct tm * timeinfo;
+    char timestamp[21];
+    size_t i;
+    
+    if (label == NULL || buff == NULL)
+        return;
+    
+    time ( &rawtime );
+    timeinfo = gmtime ( &rawtime );
+    
+    strftime(timestamp,20,"%Y-%m-%d %H:%M:%S",timeinfo);
+    fprintf(stderr, "[%s] %s (%03d):", timestamp, label, len);
+    
+    for (i = 0; i < len; i++)
+    {
+       fprintf(stderr, " %02X", (u_char) buff[i]);
+    }
+    
+    fprintf(stderr, "\n");
+}
+
+int
+mbus_data_error_print(int error)
+{   
+    printf("%s: Error = %d\n", __PRETTY_FUNCTION__, error);
+    
+    return -1;
+}
+
+//------------------------------------------------------------------------------
+//
+// XML RELATED FUNCTIONS
+//
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+///
+/// Encode string to XML
+///
+//------------------------------------------------------------------------------
+void
+mbus_str_xml_encode(u_char *dst, const u_char *src, size_t max_len)
+{
+    size_t i, len;
+    
+    i = 0;
+    len = 0;
+    
+    if (dst == NULL)
+        return;
+    
+    if (src != NULL)
+    {
+        while((len+6) < max_len) 
+        {
+            if (src[i] == '\0')
+            {
+                break;
+            }
+        
+            switch (src[i])
+            {
+                case '&':
+                    len += snprintf(&dst[len], max_len - len, "&amp;");
+                    break;
+                case '<':
+                    len += snprintf(&dst[len], max_len - len, "&lt;");
+                    break;
+                case '>':
+                    len += snprintf(&dst[len], max_len - len, "&gt;");
+                    break;
+                case '"':
+                    len += snprintf(&dst[len], max_len - len, "&quot;");
+                    break;
+                default:
+                    dst[len++] = src[i];
+                    break;
+            }
+            
+            i++;
+        }
+    }
+    
+    dst[len] = '\0';
+}
+
+//------------------------------------------------------------------------------
+/// Generate XML for the variable-length data header
+//------------------------------------------------------------------------------
+char *
+mbus_data_variable_header_xml(mbus_data_variable_header *header)
+{
+    static char buff[8192];
+    char str_encoded[768];
+    size_t len = 0;
+    int val;
+    
+    if (header)
+    {
+        len += snprintf(&buff[len], sizeof(buff) - len, "    <SlaveInformation>\n");
+
+        val = (int)mbus_data_bcd_decode(header->id_bcd, 4);
+        
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Id>%d</Id>\n", val);
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Manufacturer>%s</Manufacturer>\n",
+                mbus_decode_manufacturer(header->manufacturer[0], header->manufacturer[1]));               
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Version>%d</Version>\n", header->version);
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_product_name(header), sizeof(str_encoded));
+        
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <ProductName>%s</ProductName>\n", str_encoded);
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_variable_medium_lookup(header->medium), sizeof(str_encoded)); 
+        
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Medium>%s</Medium>\n", str_encoded);
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <AccessNumber>%d</AccessNumber>\n", header->access_no);
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Status>%.2X</Status>\n", header->status);
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Signature>%.2X%.2X</Signature>\n", header->signature[1], header->signature[0]);
+
+        len += snprintf(&buff[len], sizeof(buff) - len, "    </SlaveInformation>\n\n");
+
+        return buff;
+    }
+    
+    return ""; 
+}
+
+//------------------------------------------------------------------------------
+/// Generate XML for a single variable-length data record
+//------------------------------------------------------------------------------
+char *
+mbus_data_variable_record_xml(mbus_data_record *record, int record_cnt, int frame_cnt, mbus_data_variable_header *header)
+{
+    static char buff[8192];
+    char str_encoded[768];
+    size_t len = 0;
+    struct tm * timeinfo;
+    char timestamp[21];
+    int val;
+    
+    if (record)
+    {
+        if (frame_cnt >= 0)
+        {
+            len += snprintf(&buff[len], sizeof(buff) - len, 
+                            "    <DataRecord id=\"%d\" frame=\"%d\">\n",
+                            record_cnt, frame_cnt);
+        }
+        else
+        {
+            len += snprintf(&buff[len], sizeof(buff) - len, 
+                            "    <DataRecord id=\"%d\">\n", record_cnt);
+        }
+    
+        if (record->drh.dib.dif == 0x0F) // MBUS_DIB_DIF_VENDOR_SPECIFIC
+        {
+            len += snprintf(&buff[len], sizeof(buff) - len, 
+                            "        <Function>Manufacturer specific</Function>\n");                
+        }
+        else if (record->drh.dib.dif == 0x1F)
+        {
+            len += snprintf(&buff[len], sizeof(buff) - len, 
+                            "        <Function>More records follow</Function>\n");
+        }
+        else
+        {   
+            mbus_str_xml_encode(str_encoded, mbus_data_record_function(record), sizeof(str_encoded)); 
+            len += snprintf(&buff[len], sizeof(buff) - len, 
+                            "        <Function>%s</Function>\n", str_encoded);
+            
+            mbus_str_xml_encode(str_encoded, mbus_data_record_unit(record), sizeof(str_encoded));
+            len += snprintf(&buff[len], sizeof(buff) - len, 
+                            "        <Unit>%s</Unit>\n", str_encoded);
+        }
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_record_value(record), sizeof(str_encoded));
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Value>%s</Value>\n", str_encoded);
+        
+        timeinfo = gmtime ( &(record->timestamp) );
+        strftime(timestamp,20,"%Y-%m-%dT%H:%M:%S",timeinfo);
+        len += snprintf(&buff[len], sizeof(buff) - len, "        <Timestamp>%s</Timestamp>\n", timestamp);
+        
+        len += snprintf(&buff[len], sizeof(buff) - len, "    </DataRecord>\n\n");
+            
+        return buff;
+    }
+    
+    return ""; 
+}
+
+//------------------------------------------------------------------------------
+/// Generate XML for variable-length data 
+//------------------------------------------------------------------------------
+char *
+mbus_data_variable_xml(mbus_data_variable *data)
+{
+    mbus_data_record *record;
+    char *buff = NULL;
+    size_t len = 0, buff_size = 8192;
+    int i;
+    
+    if (data)
+    {
+        buff = (char*) malloc(buff_size);
+        
+        if (buff == NULL)
+            return NULL;
+    
+        len += snprintf(&buff[len], buff_size - len, "<MBusData>\n\n");
+        
+        len += snprintf(&buff[len], buff_size - len, "%s", 
+                        mbus_data_variable_header_xml(&(data->header)));
+    
+        for (record = data->record, i = 0; record; record = record->next, i++)
+        {
+            if ((buff_size - len) < 1024)
+            {
+                buff_size *= 2;
+                buff = (char*) realloc(buff,buff_size);
+                
+                if (buff == NULL)
+                    return NULL;
+            }
+        
+            len += snprintf(&buff[len], buff_size - len, "%s", 
+                            mbus_data_variable_record_xml(record, i, -1, &(data->header)));        
+        }       
+        len += snprintf(&buff[len], buff_size - len, "</MBusData>\n");
+
+        return buff;
+    }
+    
+    return NULL;
+}
+
+//------------------------------------------------------------------------------
+/// Generate XML representation of fixed-length frame.
+//------------------------------------------------------------------------------
+char *
+mbus_data_fixed_xml(mbus_data_fixed *data)
+{
+    char *buff = NULL;
+    char str_encoded[256];
+    size_t len = 0, buff_size = 8192;
+
+    if (data)
+    {
+        buff = (char*) malloc(buff_size);
+        
+        if (buff == NULL)
+            return NULL;
+    
+        len += snprintf(&buff[len], buff_size - len, "<MBusData>\n\n");
+    
+        len += snprintf(&buff[len], buff_size - len, "    <SlaveInformation>\n");
+        len += snprintf(&buff[len], buff_size - len, "        <Id>%d</Id>\n", (int)mbus_data_bcd_decode(data->id_bcd, 4));
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_fixed_medium(data), sizeof(str_encoded)); 
+        len += snprintf(&buff[len], buff_size - len, "        <Medium>%s</Medium>\n", str_encoded);
+        
+        len += snprintf(&buff[len], buff_size - len, "        <AccessNumber>%d</AccessNumber>\n", data->tx_cnt);
+        len += snprintf(&buff[len], buff_size - len, "        <Status>%.2X</Status>\n", data->status);
+        len += snprintf(&buff[len], buff_size - len, "    </SlaveInformation>\n\n");
+             
+        len += snprintf(&buff[len], buff_size - len, "    <DataRecord id=\"0\">\n");
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_fixed_function(data->status), sizeof(str_encoded));
+        len += snprintf(&buff[len], buff_size - len, "        <Function>%s</Function>\n", str_encoded);
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_fixed_unit(data->cnt1_type), sizeof(str_encoded));
+        len += snprintf(&buff[len], buff_size - len, "        <Unit>%s</Unit>\n", str_encoded);
+        if ((data->status & MBUS_DATA_FIXED_STATUS_FORMAT_MASK) == MBUS_DATA_FIXED_STATUS_FORMAT_BCD)
+        {
+            len += snprintf(&buff[len], buff_size - len, "        <Value>%d</Value>\n", (int)mbus_data_bcd_decode(data->cnt1_val, 4));
+        }
+        else
+        {
+            len += snprintf(&buff[len], buff_size - len, "        <Value>%d</Value>\n", mbus_data_int_decode(data->cnt1_val, 4));
+        }
+        len += snprintf(&buff[len], buff_size - len, "    </DataRecord>\n\n");      
+
+        len += snprintf(&buff[len], buff_size - len, "    <DataRecord id=\"1\">\n");
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_fixed_function(data->status), sizeof(str_encoded));
+        len += snprintf(&buff[len], buff_size - len, "        <Function>%s</Function>\n", str_encoded);
+        
+        mbus_str_xml_encode(str_encoded, mbus_data_fixed_unit(data->cnt2_type), sizeof(str_encoded));
+        len += snprintf(&buff[len], buff_size - len, "        <Unit>%s</Unit>\n", str_encoded);
+        if ((data->status & MBUS_DATA_FIXED_STATUS_FORMAT_MASK) == MBUS_DATA_FIXED_STATUS_FORMAT_BCD)
+        {
+            len += snprintf(&buff[len], buff_size - len, "        <Value>%d</Value>\n", (int)mbus_data_bcd_decode(data->cnt2_val, 4));
+        }
+        else
+        {
+            len += snprintf(&buff[len], buff_size - len, "        <Value>%d</Value>\n", mbus_data_int_decode(data->cnt2_val, 4));
+        }
+        len += snprintf(&buff[len], buff_size - len, "    </DataRecord>\n\n");      
+
+        len += snprintf(&buff[len], buff_size - len, "</MBusData>\n");
+
+        return buff;
+    }
+    
+    return NULL;
+}
+
+//------------------------------------------------------------------------------
+/// Generate XML representation of a general application error.
+//------------------------------------------------------------------------------
+char *
+mbus_data_error_xml(int error)
+{
+    char *buff = NULL;
+    char str_encoded[256];
+    size_t len = 0, buff_size = 8192;
+    
+    buff = (char*) malloc(buff_size);
+        
+    if (buff == NULL)
+        return NULL;
+
+    len += snprintf(&buff[len], buff_size - len, "<MBusData>\n\n");
+    
+    len += snprintf(&buff[len], buff_size - len, "    <SlaveInformation>\n");
+
+    mbus_str_xml_encode(str_encoded, mbus_data_error_lookup(error), sizeof(str_encoded)); 
+    len += snprintf(&buff[len], buff_size - len, "        <Error>%s</Error>\n", str_encoded);
+    
+    len += snprintf(&buff[len], buff_size - len, "    </SlaveInformation>\n\n");
+    
+    len += snprintf(&buff[len], buff_size - len, "</MBusData>\n");
+    
+    return buff;
+}
+
+//------------------------------------------------------------------------------
+/// Return a string containing an XML representation of the M-BUS frame data.
+//------------------------------------------------------------------------------
+char *
+mbus_frame_data_xml(mbus_frame_data *data)
+{
+    if (data)
+    {
+        if (data->type == MBUS_DATA_TYPE_ERROR)
+        {
+            return mbus_data_error_xml(data->error);
+        }
+    
+        if (data->type == MBUS_DATA_TYPE_FIXED)
+        {
+            return mbus_data_fixed_xml(&(data->data_fix));
+        }
+        
+        if (data->type == MBUS_DATA_TYPE_VARIABLE)
+        {
+            return mbus_data_variable_xml(&(data->data_var));
+        }
+    }
+    
+    return NULL;
+}
+
+
+//------------------------------------------------------------------------------
+/// Return an XML representation of the M-BUS frame.
+//------------------------------------------------------------------------------
+char *
+mbus_frame_xml(mbus_frame *frame)
+{
+    mbus_frame_data frame_data;
+    mbus_frame *iter;
+     
+    mbus_data_record *record;
+    char *buff = NULL;
+
+    size_t len = 0, buff_size = 8192;
+    int record_cnt = 0, frame_cnt;
+
+    if (frame)
+    {
+        if (mbus_frame_data_parse(frame, &frame_data) == -1)
+        {
+            mbus_error_str_set("M-bus data parse error.");
+            return NULL;
+        }
+    
+        if (frame_data.type == MBUS_DATA_TYPE_ERROR)
+        {
+            //
+            // generate XML for error
+            //
+            return mbus_data_error_xml(frame_data.error);
+        }
+    
+        if (frame_data.type == MBUS_DATA_TYPE_FIXED)
+        {
+            //
+            // generate XML for fixed data 
+            //
+            return mbus_data_fixed_xml(&(frame_data.data_fix));
+        }
+        
+        if (frame_data.type == MBUS_DATA_TYPE_VARIABLE)
+        {
+            //
+            // generate XML for a sequence of variable data frames
+            //
+            
+            buff = (char*) malloc(buff_size);
+        
+            if (buff == NULL)
+                return NULL;        
+
+            // include frame counter in XML output if more than one frame
+            // is available (frame_cnt = -1 => not included in output)        
+            frame_cnt = (frame->next == NULL) ? -1 : 0;
+
+            len += snprintf(&buff[len], buff_size - len, "<MBusData>\n\n");
+            
+            // only print the header info for the first frame (should be 
+            // the same for each frame in a sequence of a multi-telegram 
+            // transfer.
+            len += snprintf(&buff[len], buff_size - len, "%s", 
+                                    mbus_data_variable_header_xml(&(frame_data.data_var.header)));
+                                    
+            // loop through all records in the current frame, using a global
+            // record count as record ID in the XML output
+            for (record = frame_data.data_var.record; record; record = record->next, record_cnt++)
+            {
+                if ((buff_size - len) < 1024)
+                {
+                    buff_size *= 2;
+                    buff = (char*) realloc(buff,buff_size);
+                    
+                    if (buff == NULL)
+                        return NULL;
+                }
+            
+                len += snprintf(&buff[len], buff_size - len, "%s", 
+                                mbus_data_variable_record_xml(record, record_cnt, frame_cnt, &(frame_data.data_var.header)));        
+            }       
+
+            // free all records in the list
+            if (frame_data.data_var.record)
+            {
+                mbus_data_record_free(frame_data.data_var.record); 
+            }
+            
+            frame_cnt++;
+                        
+            for (iter = frame->next; iter; iter = iter->next, frame_cnt++)
+            {
+                if (mbus_frame_data_parse(iter, &frame_data) == -1)
+                {
+                    mbus_error_str_set("M-bus variable data parse error.");
+                    return NULL;
+                }
+            
+                // loop through all records in the current frame, using a global
+                // record count as record ID in the XML output
+                for (record = frame_data.data_var.record; record; record = record->next, record_cnt++)
+                {
+                    if ((buff_size - len) < 1024)
+                    {
+                        buff_size *= 2;
+                        buff = (char*) realloc(buff,buff_size);
+                        
+                        if (buff == NULL)
+                            return NULL;
+                    }
+                
+                    len += snprintf(&buff[len], buff_size - len, "%s", 
+                                    mbus_data_variable_record_xml(record, record_cnt, frame_cnt, &(frame_data.data_var.header)));        
+                }       
+
+                // free all records in the list
+                if (frame_data.data_var.record)
+                {
+                    mbus_data_record_free(frame_data.data_var.record); 
+                }
+            } 
+        
+            len += snprintf(&buff[len], buff_size - len, "</MBusData>\n");
+                
+            return buff;
+        }
+    }
+        
+    return NULL;
+}    
+
+
+//------------------------------------------------------------------------------
+/// Allocate and initialize a new frame data structure
+//------------------------------------------------------------------------------
+mbus_frame_data *
+mbus_frame_data_new()
+{
+    mbus_frame_data *data;
+
+    if ((data = (mbus_frame_data *)malloc(sizeof(mbus_frame_data))) == NULL)
+    {
+        return NULL;
+    }
+    data->data_var.record = NULL;
+
+    return data;
+}
+
+
+//-----------------------------------------------------------------------------
+/// Free up data associated with a frame data structure
+//------------------------------------------------------------------------------
+void
+mbus_frame_data_free(mbus_frame_data *data)
+{
+    if (data)
+    {
+        if (data->data_var.record)
+        {
+            mbus_data_record_free(data->data_var.record); // free's up the whole list
+        }
+
+        free(data);
+    }
+}
+
+
+
+//------------------------------------------------------------------------------
+/// Allocate and initialize a new variable data record
+//------------------------------------------------------------------------------
+mbus_data_record *
+mbus_data_record_new()
+{
+    mbus_data_record *record;
+
+    if ((record = (mbus_data_record *)malloc(sizeof(mbus_data_record))) == NULL)
+    {
+        return NULL;
+    }
+
+    record->next = NULL;
+    return record;    
+}
+
+//------------------------------------------------------------------------------
+/// free up memory associated with a data record and all the subsequent records
+/// in its list (apply recursively)
+//------------------------------------------------------------------------------
+void
+mbus_data_record_free(mbus_data_record *record)
+{
+    if (record)
+    {
+        mbus_data_record *next = record->next;
+
+        free(record);
+
+        if (next)
+            mbus_data_record_free(next);
+    }
+}
+
+//------------------------------------------------------------------------------
+/// Return a string containing an XML representation of the M-BUS frame.
+//------------------------------------------------------------------------------
+void
+mbus_data_record_append(mbus_data_variable *data, mbus_data_record *record)
+{
+    mbus_data_record *iter;
+
+    if (data && record)
+    {
+        if (data->record == NULL)
+        {
+            data->record = record;
+        }
+        else
+        {
+            // find the end of the list
+            for (iter = data->record; iter->next; iter = iter->next);
+
+            iter->next = record;
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+// Extract the secondary address from an M-Bus frame. The secondary address
+// should be a 16 character string comprised of the device ID (4 bytes),
+// manufacturer ID (2 bytes), version (1 byte) and medium (1 byte).
+//------------------------------------------------------------------------------
+char *
+mbus_frame_get_secondary_address(mbus_frame *frame)
+{
+    static char addr[32];
+    mbus_frame_data *data;
+    long id;
+
+    if (frame == NULL || (data = mbus_frame_data_new()) == NULL)
+    {
+        printf("%s: Failed to allocate data structure [%p, %p].\n", __PRETTY_FUNCTION__, (void*)frame, (void*)data);
+        return NULL;
+    }
+
+    if (frame->control_information != MBUS_CONTROL_INFO_RESP_VARIABLE)
+    {
+        snprintf(error_str, sizeof(error_str), "Non-variable data response (can't get secondary address from response).");
+        return NULL;
+    }
+
+    if (mbus_frame_data_parse(frame, data) == -1)
+    {
+        return NULL;
+    }
+
+    id = (long) mbus_data_bcd_decode(data->data_var.header.id_bcd, 4);
+
+    snprintf(addr, sizeof(addr), "%08lu%02X%02X%02X%02X",
+             id,
+             data->data_var.header.manufacturer[0],
+             data->data_var.header.manufacturer[1],
+             data->data_var.header.version,
+             data->data_var.header.medium);
+       
+    // free data
+    mbus_frame_data_free(data);
+ 
+    return addr;
+}
+
+//------------------------------------------------------------------------------
+// Pack a secondary address string into an mbus frame
+//------------------------------------------------------------------------------
+int
+mbus_frame_select_secondary_pack(mbus_frame *frame, char *address)
+{
+    int val, i, j, k;
+    char tmp[16];
+
+    if (frame == NULL || address == NULL || strlen(address) != 16)
+    {
+        snprintf(error_str, sizeof(error_str), "%s: frame or address arguments are NULL or invalid.", __PRETTY_FUNCTION__);
+        return -1;
+    }
+
+    frame->control  = MBUS_CONTROL_MASK_SND_UD | MBUS_CONTROL_MASK_DIR_M2S | MBUS_CONTROL_MASK_FCB;
+    frame->address  = 253;             // for addressing secondary slaves 
+    frame->control_information = 0x52; // mode 1
+
+    frame->data_size = 8;
+
+    // parse secondary_addr_str and populate frame->data[0-7]
+    // ex: secondary_addr_str = "14491001 1057 01 06" 
+    // (excluding the blank spaces)
+
+    strncpy(tmp, &address[14], 2); tmp[2] = 0;
+    val = strtol(tmp, NULL, 16);
+    frame->data[7] = val & 0xFF;
+
+    strncpy(tmp, &address[12], 2); tmp[2] = 0;
+    val = strtol(tmp, NULL, 16);
+    frame->data[6] = val & 0xFF;
+
+    strncpy(tmp,  &address[8], 4); tmp[4] = 0;
+    val = strtol(tmp, NULL, 16);
+    frame->data[4] = (val>>8) & 0xFF;
+    frame->data[5] =  val     & 0xFF;
+
+    // parse the ID string, allowing for F wildcard characters.
+    frame->data[0] = 0;
+    frame->data[1] = 0;
+    frame->data[2] = 0;
+    frame->data[3] = 0;
+    j = 3; k = 1;
+    for (i = 0; i < 8; i++)
+    {
+        if (address[i] == 'F' || address[i] == 'f')
+        {
+            frame->data[j] |= 0x0F << (4 * k--);
+        }
+        else
+        {
+            frame->data[j] |= (0x0F & (address[i] - '0')) << (4 * k--);
+        }
+
+        if (k < 0)
+        {
+            k = 1; j--;
+        }
+    }
+
+    return 0;
+}
diff --git a/cube/libmbus/mbus-protocol.h b/cube/libmbus/mbus-protocol.h
new file mode 100644
index 0000000..5fa5298
--- /dev/null
+++ b/cube/libmbus/mbus-protocol.h
@@ -0,0 +1,624 @@
+//------------------------------------------------------------------------------
+// Copyright (C) 2010-2011, Robert Johansson, Raditex AB
+// All rights reserved.
+//
+// rSCADA 
+// http://www.rSCADA.se
+// info@rscada.se
+//
+//------------------------------------------------------------------------------
+
+/**
+ * @file   mbus-protocol.h
+ * 
+ * @brief  Functions and data structures for M-Bus protocol parsing.
+ *
+ */
+
+#ifndef _MBUS_PROTOCOL_H_
+#define _MBUS_PROTOCOL_H_
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+//
+// Packet formats:
+//
+// ACK: size = 1 byte
+//
+//   byte1: ack   = 0xE5
+//
+// SHORT: size = 5 byte
+//
+//   byte1: start   = 0x10
+//   byte2: control = ...
+//   byte3: address = ...
+//   byte4: chksum  = ...
+//   byte5: stop    = 0x16
+//
+// CONTROL: size = 9 byte
+//
+//   byte1: start1  = 0x68
+//   byte2: length1 = ...
+//   byte3: length2 = ...
+//   byte4: start2  = 0x68
+//   byte5: control = ...
+//   byte6: address = ...
+//   byte7: ctl.info= ...
+//   byte8: chksum  = ...
+//   byte9: stop    = 0x16
+//
+// LONG: size = N >= 9 byte
+//
+//   byte1: start1  = 0x68
+//   byte2: length1 = ...
+//   byte3: length2 = ...
+//   byte4: start2  = 0x68
+//   byte5: control = ...
+//   byte6: address = ...
+//   byte7: ctl.info= ...
+//   byte8: data    = ...
+//          ...     = ...
+//   byteN-1: chksum  = ...
+//   byteN: stop    = 0x16
+//
+//
+//
+
+typedef struct _mbus_frame {
+
+    u_char start1;
+    u_char length1;
+    u_char length2;
+    u_char start2;
+    u_char control;
+    u_char address;
+    u_char control_information;
+    // variable data field
+    u_char checksum;
+    u_char stop;
+    
+    u_char   data[252]; 
+    size_t data_size;
+    
+    int type;
+    time_t timestamp;
+    
+    //mbus_frame_data frame_data;
+
+    void *next; // pointer to next mbus_frame for multi-telegram replies
+
+} mbus_frame;
+
+typedef struct _mbus_slave_data {
+
+    int state_fcb;
+    int state_acd;
+    
+} mbus_slave_data;
+
+#define NITEMS(x) (sizeof(x)/sizeof(x[0]))
+
+//
+// Supported handle types
+//
+#define MBUS_HANDLE_TYPE_TCP    0
+#define MBUS_HANDLE_TYPE_SERIAL 1
+
+//------------------------------------------------------------------------------
+// MBUS FRAME DATA FORMATS
+//
+
+// DATA RECORDS
+#define MBUS_DIB_DIF_EXTENSION_BIT  0x80
+#define MBUS_DIB_VIF_EXTENSION_BIT  0x80
+
+typedef struct _mbus_data_information_block {
+        
+        u_char dif;
+        u_char dife[10];
+        size_t  ndife;
+
+} mbus_data_information_block;
+
+typedef struct _mbus_value_information_block {
+        
+        u_char vif;
+        u_char vife[10];
+        size_t  nvife;
+
+        u_char custom_vif[128];
+
+} mbus_value_information_block;
+
+typedef struct _mbus_data_record_header {
+    
+    mbus_data_information_block  dib;
+    mbus_value_information_block vib;      
+
+} mbus_data_record_header;
+
+typedef struct _mbus_data_record {
+        
+    mbus_data_record_header drh;
+
+    u_char data[234];
+    size_t data_len;    
+
+    time_t timestamp;
+
+    void *next;
+
+} mbus_data_record;
+
+// 
+// HEADER FOR VARIABLE LENGTH DATA FORMAT
+// 
+typedef struct _mbus_data_variable_header {
+    
+    //Ident.Nr. Manufr. Version Medium Access No. Status  Signature
+    //4 Byte    2 Byte  1 Byte  1 Byte   1 Byte   1 Byte  2 Byte
+    
+    // ex
+    // 88 63 80 09 82 4D 02 04 15 00 00 00
+    
+    u_char id_bcd[4];         // 88 63 80 09
+    u_char manufacturer[2];   // 82 4D 
+    u_char version;           // 02
+    u_char medium;            // 04
+    u_char access_no;         // 15
+    u_char status;            // 00
+    u_char signature[2];      // 00 00
+    
+} mbus_data_variable_header;
+
+//
+// VARIABLE LENGTH DATA FORMAT
+//
+typedef struct _mbus_data_variable {
+  
+    mbus_data_variable_header header;
+    
+    mbus_data_record *record; 
+    size_t nrecords;
+    
+    u_char *data;
+    size_t  data_len;
+    
+    u_char more_records_follow;
+    
+    // are these needed/used?
+    u_char  mdh;
+    u_char *mfg_data;
+    size_t  mfg_data_len;    
+        
+} mbus_data_variable;
+
+//
+// FIXED LENGTH DATA FORMAT
+//
+typedef struct _mbus_data_fixed {
+
+    // ex
+    // 35 01 00 00 counter 2 = 135 l (historic value)
+    //
+    // e.g.
+    //
+    // 78 56 34 12 identification number = 12345678
+    // 0A          transmission counter = 0Ah = 10d
+    // 00          status 00h: counters coded BCD, actual values, no errors
+    // E9 7E       Type&Unit: medium water, unit1 = 1l, unit2 = 1l (same, but historic)
+    // 01 00 00 00 counter 1 = 1l (actual value)
+    // 35 01 00 00 counter 2 = 135 l (historic value)
+
+    u_char id_bcd[4];
+    u_char tx_cnt;
+    u_char status;
+    u_char cnt1_type;
+    u_char cnt2_type;
+    u_char cnt1_val[4];
+    u_char cnt2_val[4];
+
+} mbus_data_fixed;
+
+//
+// ABSTRACT DATA FORMAT (error, fixed or variable length)
+//
+#define MBUS_DATA_TYPE_FIXED    1
+#define MBUS_DATA_TYPE_VARIABLE 2
+#define MBUS_DATA_TYPE_ERROR    3
+
+typedef struct _mbus_frame_data {
+
+    mbus_data_variable data_var;
+    mbus_data_fixed    data_fix;
+
+    int type;
+    int error;
+
+} mbus_frame_data;
+
+// 
+// HEADER FOR SECONDARY ADDRESSING
+// 
+typedef struct _mbus_data_secondary_address {
+    
+    //Ident.Nr. Manufr. Version Medium 
+    //4 Byte    2 Byte  1 Byte  1 Byte 
+    
+    // ex
+    // 14 49 10 01 10 57 01 06
+    
+    u_char id_bcd[4];         // 14 49 10 01
+    u_char manufacturer[2];   // 10 57
+    u_char version;           // 01
+    u_char medium;            // 06
+    
+} mbus_data_secondary_address;
+
+
+//
+// for compatibility with non-gcc compilers:
+//
+//#ifndef __PRETTY_FUNCTION__
+//#define __PRETTY_FUNCTION__ "libmbus"
+//#endif
+
+//------------------------------------------------------------------------------
+// FRAME types
+//
+#define MBUS_FRAME_TYPE_ANY     0x00
+#define MBUS_FRAME_TYPE_ACK     0x01
+#define MBUS_FRAME_TYPE_SHORT   0x02
+#define MBUS_FRAME_TYPE_CONTROL 0x03
+#define MBUS_FRAME_TYPE_LONG    0x04
+
+#define MBUS_FRAME_ACK_BASE_SIZE        1
+#define MBUS_FRAME_SHORT_BASE_SIZE      5
+#define MBUS_FRAME_CONTROL_BASE_SIZE    9
+#define MBUS_FRAME_LONG_BASE_SIZE       9
+
+#define MBUS_FRAME_BASE_SIZE_ACK       1
+#define MBUS_FRAME_BASE_SIZE_SHORT     5
+#define MBUS_FRAME_BASE_SIZE_CONTROL   9
+#define MBUS_FRAME_BASE_SIZE_LONG      9
+
+#define MBUS_FRAME_FIXED_SIZE_ACK      1
+#define MBUS_FRAME_FIXED_SIZE_SHORT    5
+#define MBUS_FRAME_FIXED_SIZE_CONTROL  6 
+#define MBUS_FRAME_FIXED_SIZE_LONG     6
+
+//
+// Frame start/stop bits
+//
+#define MBUS_FRAME_ACK_START     0xE5
+#define MBUS_FRAME_SHORT_START   0x10
+#define MBUS_FRAME_CONTROL_START 0x68
+#define MBUS_FRAME_LONG_START    0x68
+#define MBUS_FRAME_STOP          0x16
+
+//
+//
+//
+#define MBUS_MAX_PRIMARY_SLAVES 256
+
+//
+// Control field
+//
+#define MBUS_CONTROL_FIELD_DIRECTION    0x07
+#define MBUS_CONTROL_FIELD_FCB          0x06
+#define MBUS_CONTROL_FIELD_ACD          0x06
+#define MBUS_CONTROL_FIELD_FCV          0x05
+#define MBUS_CONTROL_FIELD_DFC          0x05
+#define MBUS_CONTROL_FIELD_F3           0x04
+#define MBUS_CONTROL_FIELD_F2           0x03
+#define MBUS_CONTROL_FIELD_F1           0x02
+#define MBUS_CONTROL_FIELD_F0           0x01
+
+#define MBUS_CONTROL_MASK_SND_NKE       0x40
+#define MBUS_CONTROL_MASK_SND_UD        0x53
+#define MBUS_CONTROL_MASK_REQ_UD2       0x5B
+#define MBUS_CONTROL_MASK_REQ_UD1       0x5A
+#define MBUS_CONTROL_MASK_RSP_UD        0x08
+
+#define MBUS_CONTROL_MASK_FCB           0x20
+#define MBUS_CONTROL_MASK_FCV           0x10
+
+#define MBUS_CONTROL_MASK_ACD           0x20
+#define MBUS_CONTROL_MASK_DFC           0x10
+
+#define MBUS_CONTROL_MASK_DIR           0x40
+#define MBUS_CONTROL_MASK_DIR_M2S       0x40
+#define MBUS_CONTROL_MASK_DIR_S2M       0x00
+
+//
+// Address field
+//
+#define MBUS_ADDRESS_BROADCAST_REPLY    0xFE
+#define MBUS_ADDRESS_BROADCAST_NOREPLY  0xFF
+#define MBUS_ADDRESS_NETWORK_LAYER      0xFD
+
+//
+// Control Information field
+//
+//Mode 1 Mode 2                   Application                   Definition in
+// 51h    55h                       data send                    EN1434-3
+// 52h    56h                  selection of slaves           Usergroup July  ́93
+// 50h                          application reset           Usergroup March  ́94
+// 54h                          synronize action                 suggestion
+// B8h                     set baudrate to 300 baud          Usergroup July  ́93
+// B9h                     set baudrate to 600 baud          Usergroup July  ́93
+// BAh                    set baudrate to 1200 baud          Usergroup July  ́93
+// BBh                    set baudrate to 2400 baud          Usergroup July  ́93
+// BCh                    set baudrate to 4800 baud          Usergroup July  ́93
+// BDh                    set baudrate to 9600 baud          Usergroup July  ́93
+// BEh                   set baudrate to 19200 baud              suggestion
+// BFh                   set baudrate to 38400 baud              suggestion
+// B1h           request readout of complete RAM content     Techem suggestion
+// B2h          send user data (not standardized RAM write) Techem suggestion
+// B3h                 initialize test calibration mode      Usergroup July  ́93
+// B4h                           EEPROM read                 Techem suggestion
+// B6h                         start software test           Techem suggestion
+// 90h to 97h              codes used for hashing           longer recommended
+
+#define MBUS_CONTROL_INFO_DATA_SEND          0x51
+#define MBUS_CONTROL_INFO_DATA_SEND_MSB      0x55
+#define MBUS_CONTROL_INFO_SELECT_SLAVE       0x52
+#define MBUS_CONTROL_INFO_SELECT_SLAVE_MSB   0x56
+#define MBUS_CONTROL_INFO_APPLICATION_RESET  0x50
+#define MBUS_CONTROL_INFO_SYNC_ACTION        0x54
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_300   0xB8
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_600   0xB9
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_1200  0xBA
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_2400  0xBB
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_4800  0xBC
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_9600  0xBD
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_19200 0xBE
+#define MBUS_CONTROL_INFO_SET_BAUDRATE_38400 0xBF
+#define MBUS_CONTROL_INFO_REQUEST_RAM_READ   0xB1
+#define MBUS_CONTROL_INFO_SEND_USER_DATA     0xB2
+#define MBUS_CONTROL_INFO_INIT_TEST_CALIB    0xB3
+#define MBUS_CONTROL_INFO_EEPROM_READ        0xB4
+#define MBUS_CONTROL_INFO_SW_TEST_START      0xB6
+
+//Mode 1 Mode 2                   Application                   Definition in
+// 70h             report of general application errors     Usergroup March 94
+// 71h                      report of alarm status          Usergroup March 94
+// 72h   76h                variable data respond                EN1434-3
+// 73h   77h                 fixed data respond                  EN1434-3
+#define MBUS_CONTROL_INFO_ERROR_GENERAL      0x70
+#define MBUS_CONTROL_INFO_STATUS_ALARM       0x71
+
+#define MBUS_CONTROL_INFO_RESP_FIXED         0x73
+#define MBUS_CONTROL_INFO_RESP_FIXED_MSB     0x77
+
+#define MBUS_CONTROL_INFO_RESP_VARIABLE      0x72
+#define MBUS_CONTROL_INFO_RESP_VARIABLE_MSB  0x76
+
+//
+// DATA BITS
+//
+#define MBUS_DATA_FIXED_STATUS_FORMAT_MASK  0x80
+#define MBUS_DATA_FIXED_STATUS_FORMAT_BCD   0x00
+#define MBUS_DATA_FIXED_STATUS_FORMAT_INT   0x80
+#define MBUS_DATA_FIXED_STATUS_DATE_MASK    0x40
+#define MBUS_DATA_FIXED_STATUS_DATE_STORED  0x40
+#define MBUS_DATA_FIXED_STATUS_DATE_CURRENT 0x00
+
+
+//
+// data record fields
+//
+#define MBUS_DATA_RECORD_DIF_MASK_INST      0x00
+#define MBUS_DATA_RECORD_DIF_MASK_MIN       0x10
+
+#define MBUS_DATA_RECORD_DIF_MASK_TYPE_INT32     0x04
+#define MBUS_DATA_RECORD_DIF_MASK_DATA           0x0F
+#define MBUS_DATA_RECORD_DIF_MASK_FUNCTION       0x30
+#define MBUS_DATA_RECORD_DIF_MASK_STORAGE_NO     0x40
+#define MBUS_DATA_RECORD_DIF_MASK_EXTENTION      0x80
+#define MBUS_DATA_RECORD_DIF_MASK_NON_DATA       0xF0
+
+//
+// GENERAL APPLICATION ERRORS
+//
+#define MBUS_ERROR_DATA_UNSPECIFIED        0x00
+#define MBUS_ERROR_DATA_UNIMPLEMENTED_CI   0x01
+#define MBUS_ERROR_DATA_BUFFER_TOO_LONG    0x02
+#define MBUS_ERROR_DATA_TOO_MANY_RECORDS   0x03
+#define MBUS_ERROR_DATA_PREMATURE_END      0x04
+#define MBUS_ERROR_DATA_TOO_MANY_DIFES     0x05
+#define MBUS_ERROR_DATA_TOO_MANY_VIFES     0x06
+#define MBUS_ERROR_DATA_RESERVED           0x07
+#define MBUS_ERROR_DATA_APPLICATION_BUSY   0x08
+#define MBUS_ERROR_DATA_TOO_MANY_READOUTS  0x09
+
+//
+// FIXED DATA FLAGS
+//
+
+//
+// VARIABLE DATA FLAGS
+// 
+#define MBUS_VARIABLE_DATA_MEDIUM_OTHER         0x00
+#define MBUS_VARIABLE_DATA_MEDIUM_OIL           0x01
+#define MBUS_VARIABLE_DATA_MEDIUM_ELECTRICITY   0x02
+#define MBUS_VARIABLE_DATA_MEDIUM_GAS           0x03
+#define MBUS_VARIABLE_DATA_MEDIUM_HEAT_OUT      0x04
+#define MBUS_VARIABLE_DATA_MEDIUM_STEAM         0x05
+#define MBUS_VARIABLE_DATA_MEDIUM_HOT_WATER     0x06
+#define MBUS_VARIABLE_DATA_MEDIUM_WATER         0x07
+#define MBUS_VARIABLE_DATA_MEDIUM_HEAT_COST     0x08
+#define MBUS_VARIABLE_DATA_MEDIUM_COMPR_AIR     0x09
+#define MBUS_VARIABLE_DATA_MEDIUM_COOL_OUT      0x0A
+#define MBUS_VARIABLE_DATA_MEDIUM_COOL_IN       0x0B
+#define MBUS_VARIABLE_DATA_MEDIUM_HEAT_IN       0x0C
+#define MBUS_VARIABLE_DATA_MEDIUM_HEAT_COOL     0x0D
+#define MBUS_VARIABLE_DATA_MEDIUM_BUS           0x0E
+#define MBUS_VARIABLE_DATA_MEDIUM_UNKNOWN       0x0F
+#define MBUS_VARIABLE_DATA_MEDIUM_COLD_WATER    0x16
+#define MBUS_VARIABLE_DATA_MEDIUM_DUAL_WATER    0x17
+#define MBUS_VARIABLE_DATA_MEDIUM_PRESSURE      0x18
+#define MBUS_VARIABLE_DATA_MEDIUM_ADC           0x19
+
+#define MBUS_VARIABLE_DATA_MAN_ACW              0x0477
+#define MBUS_VARIABLE_DATA_MAN_ABB              0x0442
+#define MBUS_VARIABLE_DATA_MAN_AMT              0x05B4
+#define MBUS_VARIABLE_DATA_MAN_EFE              0x14C5
+#define MBUS_VARIABLE_DATA_MAN_ELS              0x1593
+#define MBUS_VARIABLE_DATA_MAN_EMH              0x15A8
+#define MBUS_VARIABLE_DATA_MAN_HYD              0x2324
+#define MBUS_VARIABLE_DATA_MAN_KAM              0x2C2D
+#define MBUS_VARIABLE_DATA_MAN_LSE              0x3265
+#define MBUS_VARIABLE_DATA_MAN_LUG              0x32A7
+#define MBUS_VARIABLE_DATA_MAN_NZR              0x3B52
+#define MBUS_VARIABLE_DATA_MAN_PAD              0x4024
+#define MBUS_VARIABLE_DATA_MAN_QDS              0x4493
+#define MBUS_VARIABLE_DATA_MAN_SLB              0x4D82
+#define MBUS_VARIABLE_DATA_MAN_SON              0x4DEE
+#define MBUS_VARIABLE_DATA_MAN_SPX              0x4E18
+#define MBUS_VARIABLE_DATA_MAN_SVM              0x4ECD
+#define MBUS_VARIABLE_DATA_MAN_TCH              0x5068
+#define MBUS_VARIABLE_DATA_MAN_ZRM              0x6A4D
+
+//
+// Event callback functions
+//
+extern void (*_mbus_recv_event)(u_char src_type, const char *buff, size_t len);
+extern void (*_mbus_send_event)(u_char src_type, const char *buff, size_t len);
+
+void mbus_dump_recv_event(u_char src_type, const char *buff, size_t len);
+void mbus_dump_send_event(u_char src_type, const char *buff, size_t len);
+
+//
+// Event register functions
+//
+void mbus_register_recv_event(void (*event)(u_char src_type, const char *buff, size_t len));
+void mbus_register_send_event(void (*event)(u_char src_type, const char *buff, size_t len));
+
+//
+// variable length records
+//
+mbus_data_record *mbus_data_record_new();
+void              mbus_data_record_free(mbus_data_record *record);
+void              mbus_data_record_append(mbus_data_variable *data, mbus_data_record *record);
+
+
+// XXX: Add application reset subcodes
+
+mbus_frame *mbus_frame_new(int frame_type);
+int         mbus_frame_free(mbus_frame *frame);
+
+mbus_frame_data *mbus_frame_data_new();
+void             mbus_frame_data_free(mbus_frame_data *data);
+
+//
+//
+//
+int mbus_frame_calc_checksum(mbus_frame *frame);
+int mbus_frame_calc_length  (mbus_frame *frame);
+
+//
+// Parse/Pack to bin
+//
+int mbus_parse(mbus_frame *frame, u_char *data, size_t data_size);
+
+int mbus_data_fixed_parse   (mbus_frame *frame, mbus_data_fixed    *data);
+int mbus_data_variable_parse(mbus_frame *frame, mbus_data_variable *data);
+
+int mbus_frame_data_parse   (mbus_frame *frame, mbus_frame_data *data);
+  
+int mbus_frame_pack(mbus_frame *frame, u_char *data, size_t data_size);
+
+int mbus_frame_verify(mbus_frame *frame);
+
+int mbus_frame_internal_pack(mbus_frame *frame, mbus_frame_data *frame_data);
+
+//
+// data parsing
+//
+const char *mbus_data_record_function(mbus_data_record *record);
+const char *mbus_data_fixed_function(int status);
+
+//
+// M-Bus frame data struct access/write functions
+//
+int mbus_frame_type(mbus_frame *frame);
+
+//
+// Slave status data register.
+//
+mbus_slave_data *mbus_slave_data_get(size_t i);
+
+//
+// XML generating functions
+//
+void  mbus_str_xml_encode(u_char *dst, const u_char *src, size_t max_len);
+char *mbus_data_xml(mbus_frame_data *data);
+char *mbus_data_variable_xml(mbus_data_variable *data);
+char *mbus_data_fixed_xml(mbus_data_fixed *data);
+char *mbus_data_error_xml(int error);
+char *mbus_frame_data_xml(mbus_frame_data *data);
+
+char *mbus_data_variable_header_xml(mbus_data_variable_header *header);
+
+char *mbus_frame_xml(mbus_frame *frame);
+
+//
+// Debug/dump
+//
+int mbus_frame_print(mbus_frame *frame);
+int mbus_frame_data_print(mbus_frame_data *data);
+int mbus_data_fixed_print(mbus_data_fixed *data);
+int mbus_data_error_print(int error);
+int mbus_data_variable_header_print(mbus_data_variable_header *header);
+int mbus_data_variable_print(mbus_data_variable *data);
+
+char *mbus_error_str();
+void  mbus_error_str_set(char *message);
+void  mbus_error_reset();
+
+void  mbus_parse_set_debug(int debug);
+void  mbus_hex_dump(const char *label, const char *buff, size_t len);
+
+//
+// data encode/decode functions
+//
+int mbus_data_manufacturer_encode(u_char *m_data, u_char *m_code);
+const char *mbus_decode_manufacturer(u_char byte1, u_char byte2);
+const char *mbus_data_product_name(mbus_data_variable_header *header);
+ 
+int mbus_data_bcd_encode(u_char *bcd_data, size_t bcd_data_size, int value);
+int mbus_data_int_encode(u_char *int_data, size_t int_data_size, int value);
+
+long long mbus_data_bcd_decode(u_char *bcd_data, size_t bcd_data_size);
+int  mbus_data_int_decode(u_char *int_data, size_t int_data_size);
+long mbus_data_long_decode(u_char *int_data, size_t int_data_size);
+long long mbus_data_long_long_decode(u_char *int_data, size_t int_data_size);
+
+float mbus_data_float_decode(u_char *float_data);
+
+void mbus_data_tm_decode(struct tm *t, u_char *t_data, size_t t_data_size);
+
+void mbus_data_str_decode(u_char *dst, const u_char *src, size_t len);
+
+void mbus_data_bin_decode(u_char *dst, const u_char *src, size_t len, size_t max_len);
+
+const char *mbus_data_fixed_medium(mbus_data_fixed *data);
+const char *mbus_data_fixed_unit(int medium_unit_byte);
+const char *mbus_data_variable_medium_lookup(u_char medium);
+const char *mbus_unit_prefix(int exp);
+
+const char *mbus_data_error_lookup(int error);
+
+const char *mbus_vib_unit_lookup(mbus_value_information_block *vib);
+const char *mbus_vif_unit_lookup(u_char vif);
+
+u_char mbus_dif_datalength_lookup(u_char dif);
+
+char *mbus_frame_get_secondary_address(mbus_frame *frame);
+int   mbus_frame_select_secondary_pack(mbus_frame *frame, char *address);
+
+#endif /* _MBUS_PROTOCOL_H_ */
+