User Tools

Site Tools


apator-apt-mbu-na-1c

Apator APT-MBU-NA-1C water meter head

This head is used for turbine type water flowmeters which is put on the top of the water meter device. You can use custom protocol feature for reading this device.

Connection setup

Please setup connection in WebHMI as follows:

Custom protocol source code

POLL_DELAY = 60 -- poll rate constant in sec. 
 
function DBG(...) ------ convenient print function -----------
    for i = 1, #arg do 
        local curArgument = arg[i]
        if type(curArgument) == 'table' then 
            tprint(curArgument)
        else 
            INFO(curArgument)
        end 
    end 
end 
DEBUG = DBG 
 
HEX_NUMBERING = 16 -- numbering system conversion 
 
-- mbus control bytes 
BRDCAST_NET_LAYER_ADDR = 0xFD  -- 253 
BROADCAST_ADDR         = 0xFE  -- 254 
 
-- delimiters 
SHORT_FRAME_STX     = 0x10
SEND_NKE            = 0x40
ACK                 = 0xE5
ETX                 = 0x16 
CTRL_LONG_FRAME_STX = 0x68
REQ_UD2             = 0x7B -- Request for Class 2 Data 
REQ_UD2_            = 0x5B -- Request for Class 2 Data 
 
 
-- PROBE_PACKET = {SHORT_FRAME_STX, REQ_UD2_, 0xFE, 0x59, 0x16}  original 
PROBE_PACKET = {SHORT_FRAME_STX, REQ_UD2_, BROADCAST_ADDR, 0x59, ETX}
 
REQ_UD2_POS              = 2 
PROBE_PACKET_PAYLOAD_LEN = 2 
PROBE_PACKET_CRC_POS    = 4  
 
-- 04 13               dif vif signature 
-- 1F 42 0F 00         vol. data example in INT32 
VOL_DIF_VIF = {0x04, 0x13}
VOL_DATA_LEN = 4 
 
function onScanStart ()
    now = os.time()
end 
 
function createDevices ()
   addDevice{name = "V",     shift = 0, base = 10, xtraFields = {} }
end 
 
function readRegister (reg, device, unitId)
 
    if (not lastReadTimeStamp or (now - lastReadTimeStamp) >= POLL_DELAY ) then 
 
        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 > 10) then
            local foundDataPos = tableFindPattern(meterDataFrame, VOL_DIF_VIF)
            if foundDataPos then
                lastReadTimeStamp = now 
                local dataChunk = table.sub(meterDataFrame
                                            ,foundDataPos
                                            , foundDataPos + VOL_DATA_LEN - 1)
 
                local revDataHexStr = tabHexView(reverseTable(dataChunk) )  
                data = tonumber(revDataHexStr, HEX_NUMBERING) 
            else 
                ERROR("failed to find data !")
                data = false     
            end 
        else 
            ERROR("failed to read reply!")
            data = false 
        end 
    end 
 
    return data 
 
end 
 
function writeRegister (reg, device, unitId, newValue)
end
 
-------------------------- Helpers --------------------------
 
function tableFindPattern(inputTab, sequence) -- finds pattern in a table 
 
    for tabIndex, _ in ipairs(inputTab) do 
 
        local matchFlag = true 
 
        for seqIndex, sequenceByte in ipairs(sequence) do  -- checking sequence inside a inputTab 
            matchFlag = matchFlag and (sequenceByte == inputTab[tabIndex + seqIndex - 1])
            if (not matchFlag) then 
                matchFlag = false 
                break
            end 
        end
 
        if matchFlag then 
            return (tabIndex + #sequence) -- start of data 
        end 
    end 
 
    return false  
end 
 
function table.sub(t, startIndex, endIndex) -- picks sub-table from a table 
    local tmpTable = {}
 
    for k = startIndex, endIndex do 
        table.insert(tmpTable, t[k])
    end  
 
    return tmpTable
end 
 
function tabHexView(hextab, spacer) -- get string of a hex view for a table 
    local hex = {} 
 
    for _, hexbyte in ipairs(hextab) do 
        table.insert(hex, getHexByteAsStr(hexbyte))
    end 
 
    return table.concat(hex, spacer)
end 
 
function getHexByteAsStr(inputByte) -- gets 2 - char hex string of a byte 
    local strByte = string.format("%X", inputByte)    
    return (#strByte == 1 and '0' .. strByte) or strByte
end 
 
function getCRC(a, pos, len)   -- calc. CRC sum for the mbus packet 
    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 reverseTable(tab) -- reverses a table 
    local outTable = {}
    for i = #tab, 1, -1 do 
        table.insert(outTable, tab[i])
    end 
    return outTable
end 
 
function readUntil(endByte) -- read input buffer untils endByte or timeout 
    local ONE_BYTE = 1 
    local buf = {}
    repeat 
        local rxSymbol = readBytes(ONE_BYTE)[ONE_BYTE]
        if rxSymbol then 
            table.insert(buf, rxSymbol)
        end 
    until (not rxSymbol or (rxSymbol == endByte))
 
    return ((#buf >= ONE_BYTE) and buf) or false 
end 
apator-apt-mbu-na-1c.txt ยท Last modified: 2023/05/17 08:40 by emozolyak

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki