{{ :custom_protocols:apator-head-view1.png?250|}} ====== 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_protocols|custom protocol]] feature for reading this device. ===== Connection setup ===== Please setup connection in WebHMI as follows: {{:custom_protocols:apator-conn-setup-page-1.png?800px}} {{:custom_protocols:apator-conn-setup-page-2.png?800px}} ===== 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