User Tools

Site Tools


diehl-inte

DIEHL INTE heat meter

The meter has integrated Mbus port.

-- MAIN CODE STARTS AT LINE 118 
function onScanStart ()
    now = os.time()
end 
 
POLL_DELAY    = 60 * 15  -- poll rate constant in sec. 
HEX_NUMBERING = 16 -- numbering system conversion 
 
------ convenient print function -----------
function tprint(t, indent)
    if not indent then indent = 0 end 
    for k, v in pairs(t) do 
        local formatting = string.rep(' ', indent) .. k .. ': '
        if type(v) == "table" then 
            ERROR(formatting) 
            tprint(v, indent + 1) -- recursive call 
        else
            if type(v) == "boolean" then 
                v = v and "TRUE" or "FALSE"
            end 
            ERROR(formatting .. v) 
        end 
    end 
end 
 
-- prints any parameters 
function DBG(...) 
    for i = 1, #arg do 
        local curArgument = arg[i]
        if type(curArgument) == 'table' then 
            tprint(curArgument)
        else 
            INFO(tostring(curArgument))
        end 
    end 
end  ;  DEBUG = DBG 
 
------ Table helpers -----------------------------
function table.findPattern(t, pttrn) 
 
    for tabIndex, _ in ipairs(t) do 
        local matchFlag = true 
 
        for seqIndex, sequenceByte in ipairs(pttrn) do  -- checking pttrn inside a t 
            matchFlag = matchFlag and (sequenceByte == t[tabIndex + seqIndex - 1])
            if (not matchFlag) then 
                matchFlag = false 
                break
            end 
        end
        if matchFlag then 
            return (tabIndex + #pttrn) 
        end 
    end 
    return false  
end 
 
function table.sub(t, startIndex, endIndex) 
    local tmpTable = {}
    for k = startIndex, endIndex do 
        table.insert(tmpTable, t[k])
    end  
    return tmpTable
end 
 
function table.hexStr(hextab, spacer) 
    local hex = {} 
    for _, hexbyte in ipairs(hextab) do 
        table.insert(hex, getHexByteAsStr(hexbyte))
    end 
    return table.concat(hex, spacer)
end 
-- converts tab to bcd number 
table.bcd = function(t)
    local hex = table.hexStr(t)
    local out = tonumber(hex) ;  DBG('entered bdc with', t, 'hex view: ' .. hex, 'out: ' .. tostring(out)) 
    return out 
end 
-- integer convertions 
table.int = function(t)
   return tonumber(table.hexStr(t), HEX_NUMBERING) 
end
 
table.float = function(t)
   return t 
end
 
 -- gets 2 - char hex string of a byte 
function getHexByteAsStr(inputByte) 
    local strByte = string.format("%X", inputByte)    
    return (#strByte == 1 and '0' .. strByte) or strByte
end 
 
table.reverse = function (tab) 
    local outTable = {}
    for i = #tab, 1, -1 do 
        table.insert(outTable, tab[i])
    end 
    return outTable
end 
 
-- delimiters & control bytes 
SHORT_FRAME_STX     = 0x10
SEND_NKE            = 0x40
ACK                 = 0xE5
ETX                 = 0x16 
CTRL_LONG_FRAME_STX = 0x68
 
REQ_UD2             = 0x7B -- Request for Class 2 Data  FCB 
REQ_UD2_            = 0x5B -- Request for Class 2 Data  Frame count bit 
 
BROADCAST_ADDR      = 0xFE  -- 254 
CRC_                = 0x59 
 
-- prboe packet 
-- PROBE_PACKET = {SHORT_FRAME_STX, REQ_UD2_, BROADCAST_ADDR, CRC_, ETX}  -- TxD:10 5B FE 59 16 
PROBE_PACKET = {SHORT_FRAME_STX, REQ_UD2, BROADCAST_ADDR, CRC_, ETX}  -- TxD:10 5B FE 59 16 
        -- its indexes  
REQ_UD2_POS              = 2 
PROBE_PCKT_ADDR_POS      = 3  
PROBE_PACKET_CRC_POS     = 4  
PROBE_PACKET_PAYLOAD_LEN = 2 
 
data = {empty = {}}  
 
dataHandlers = {
    -- energy = {pattern = {0x4, 0x07},       length = 4, func = table.float}, 
    energy = {pattern = {0x4, 0x07},       length = 4, func = table.float}, 
 
    volume = {pattern = {0x4, 0x15},       length = 4, func = table.int}, 
 
    power  = {pattern = {0x5, 0x2E},       length = 4, func = table.float}, 
    flow   = {pattern = {0x5, 0x3E},       length = 4, func = table.float},
 
    tint   = {pattern = {0x2, 0x59},       length = 2, func = table.int}, -- fwd.   temp. 
    text   = {pattern = {0x2, 0x5D},       length = 2, func = table.int}, -- return temp. 
 
    err   =  {pattern = {0x2, 0xFD, 0x17}, length = 2, func = table.int},
 
    upt   =  {pattern = {0x4, 0x20},       length = 4, func = table.int}  -- uptime , sec. 
}
 
function createDevices ()                           -- xtraFields are used for comments                
   addDevice{name = "E",     shift = 0, base = 10, xtraFields = {'energy'}           } 
   addDevice{name = "V",     shift = 0, base = 10, xtraFields = {'volume'}           } 
   addDevice{name = "P",     shift = 0, base = 10, xtraFields = {'power'}            } 
   addDevice{name = "Q",     shift = 0, base = 10, xtraFields = {'flow'}             }   
   addDevice{name = "FWD",    shift = 0, base = 10, xtraFields = {'temp. internal'}   }   
   addDevice{name = "RET",    shift = 0, base = 10, xtraFields = {'temp. external'}   }   
   addDevice{name = "ERR",   shift = 0, base = 10, xtraFields = {'error flags'}  }   
   addDevice{name = "UPT",   shift = 0, base = 10, xtraFields = {'error flags'}  }   
end 
 
 
function readRegister (reg, device, unitId)
 
    local function initMeter() 
        DBG("Etnered init packet for inte")
        local INTE_INIT_FRAME = {SHORT_FRAME_STX, 
                                 SEND_NKE, 
                                 BROADCAST_ADDR, 0x00 -- CRC #4 
                                                 , ETX} 
 
        INTE_INIT_FRAME[3] = unitId
        INTE_INIT_FRAME[4] = getCRC(INTE_INIT_FRAME, 2, 2)
 
        if (not sendBytes(INTE_INIT_FRAME) ) then
            ERROR("Could not sent bytes!")
            return false
        end
 
        if (readOneByte() ~= ACK) then  
            ERROR("No ack after init packet!") ; return false
        else 
            DBG("Ack received for INIT_PACKETS")
        end 
 
        local CURRENT_PARAMS_SELECTION = { CTRL_LONG_FRAME_STX, 0x04, 0x04, CTRL_LONG_FRAME_STX
                                           , 0x53, BROADCAST_ADDR, 0x50, 0x0,  0x0 -- CRC #9 
                                           , ETX}
        CURRENT_PARAMS_SELECTION[6] = unitId
        CURRENT_PARAMS_SELECTION[9] = getCRC(CURRENT_PARAMS_SELECTION, 5, 4)
 
        if (not sendBytes(CURRENT_PARAMS_SELECTION) ) then
            ERROR("Could not send bytes for params selection!") ; return false
        end
 
        if (readOneByte() ~= ACK) then  
            ERROR("No ack after cur params selection packet!") ; return false
        else 
            DEBUG("Ack received for params selection") ; return true 
        end 
    end 
 
    if (not lastReadTimeStamp or (now - lastReadTimeStamp) >= POLL_DELAY ) then 
        DBG("Starting inte read by lastReadTimeStamp")
 
        if (not init and not initMeter() ) then 
            ERROR("Could not init INTE meter!")
            init = false ; return false 
        else 
            init = true  -- set global for init meter 
        end 
 
        PROBE_PACKET[PROBE_PCKT_ADDR_POS] = unitId
        PROBE_PACKET[PROBE_PACKET_CRC_POS] = getCRC(PROBE_PACKET
                                                    , REQ_UD2_POS
                                                    , PROBE_PACKET_PAYLOAD_LEN)
 
        if (not sendBytes(PROBE_PACKET) ) then
            ERROR("Could not sent bytes for DATA_QUERY!") 
            return false
        end
 
        local meterDataFrame = readUntil(ETX) 
 
        if (meterDataFrame and (#meterDataFrame > 10) ) then
                                            DBG(table.hexStr(meterDataFrame, ' '))
            -- parsing 
            for param, struc in pairs(dataHandlers) do 
                DBG('trying to read ' .. tostring(param))
                local foundPos = table.findPattern(meterDataFrame, struc.pattern)
 
                if foundPos then
                    DBG('             found data at: '.. foundPos)
                    local foundBytes = table.sub(meterDataFrame
                                                 , foundPos
                                                 , foundPos + struc.length - 1) 
 
                    foundBytes = table.reverse(foundBytes)      
                    local actualData = struc.func(foundBytes)   ; DBG("reversed actualData", '|', table.hexStr(foundBytes, ' '))
                    -- DBG('cur data structure: ', data)
                    data[param] = actualData
 
                    lastReadTimeStamp = now 
                else 
                    ERROR("failed to find data for " .. param)
                    data[param] = false     
                end
            end 
        else 
            ERROR("failed to read long reply, meterDataFrame: " .. tostring(meterDataFrame))
            return false 
        end 
    end 
 
    DBG("going to return cached data: ", data )
    if data then 
        if (device.name == 'E')    then 
            DBG(device.xtraFields[1])
            return data.energy 
        end 
        if (device.name == 'V')    then return data.volume end 
        if (device.name == 'P')    then return data.power  end 
        if (device.name == 'Q')    then return data.flow   end 
        if (device.name == 'FWD')  then return data.tint   end 
        if (device.name == 'RET')  then return data.text   end 
        if (device.name == 'ERR')  then return data.err    end 
        if (device.name == 'UPT')  then return data.upt    end
    else 
        ERROR("the data is empty ... ")
        return false 
    end 
 
end 
 
function writeRegister (reg, device, unitId, newValue)
end
 
-------------------------- Other helpers --------------------------
 
function readUntil(endByte) 
    local buf = {} 
    repeat 
        local b = readOneByte() 
        if b then 
            table.insert(buf, b)
        end 
    until (not b or (b == endByte))
 
    return ((#buf >= 1) and buf) or false 
end 
 
function getCRC(a, pos, len)   
    local sum, mask  = 0, 0xFF
 
    for i = pos, pos + len - 1 do
        sum = sum + a[i] 
        sum = bit.band(sum, mask)
    end 
    return sum
end 
 
function readOneByte() 
    local b = readBytes(1)
    return b and b[1] or false 
end
diehl-inte.txt ยท Last modified: 2023/10/16 11:25 by emozolyak

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki