hydrus-flowmeter
This is an old revision of the document!
Highly accurate, lead-free brass ultrasonic smart water meter for residential, commercial and industrial installations. It has Mbus onboard.
Connecting to HYDRUS by DIEHL flowmeter
-- MAIN CODE STARTS AT LINE 118 function onScanStart () now = os.time() end POLL_DELAY = 60 -- 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 function DBG(...) for i = 1, #arg do local curArgument = arg[i] if type(curArgument) == 'table' then tprint(curArgument) else INFO(curArgument) end end end ; DEBUG = DBG ------ Table helpers ----------------------------- function table.findPattern(t, pttrn) -- finds pattern in a table tableFindPattern 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) -- 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 table.hexView = function(hextab, spacer) -- get string of a hex view for a table tabHexView 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.hexView(t) return tonumber(hex) 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 table.reverse = function (tab) -- reverses a table reverseTable 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 REQ_UD2_ = 0x5B -- Request for Class 2 Data BRDCAST_NET_LAYER_ADDR = 0xFD -- 253 BROADCAST_ADDR = 0xFE -- 254 -- prboe packet PROBE_PACKET = {SHORT_FRAME_STX, REQ_UD2_, BROADCAST_ADDR, 0x59, ETX} -- its indexes REQ_UD2_POS = 2 PROBE_PACKET_PAYLOAD_LEN = 2 PROBE_PACKET_CRC_POS = 4 data = {} dataHandlers = { volume = {pattern = {0xC, 0x14}, length = 4, func = table.bcd}, flow = {pattern = {0xB, 0x3C}, length = 3, func = table.bcd}, tint = {pattern = {0xA, 0x5A}, length = 2, func = table.bcd}, text = {pattern = {0xA, 0x66}, length = 2, func = table.bcd} } function createDevices () addDevice{name = "V", shift = 0, base = 10, xtraFields = {'volume'} } addDevice{name = "Q", shift = 0, base = 10, xtraFields = {'flow'} } addDevice{name = "TI", shift = 0, base = 10, xtraFields = {'temp. internal'} } addDevice{name = "TE", shift = 0, base = 10, xtraFields = {'temp. external'} } 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 -- parsing for param, struc in pairs(dataHandlers) do local foundPos = table.findPattern(meterDataFrame, struc.pattern) if foundPos then DBG('found data at: ', foundPos) lastReadTimeStamp = now local foundBytes = table.sub(meterDataFrame, foundPos , foundPos + struc.length - 1) ; DBG('found bytes: ', foundBytes) foundBytes = table.reverse(foundBytes) ; DBG("After reverse: ", foundBytes) data[param] = struc.func(foundBytes) ; DBG("Data - param: ", data[param]) else ERROR("failed to find data !") data = false end end else ERROR("failed to read reply!") data = false end end if (device.name == 'V') then return data["volume"] end if (device.name == 'Q') then return data["flow"] end if (device.name == 'TI') then return data.tint end if (device.name == 'TE') then return data.text end end function writeRegister (reg, device, unitId, newValue) end -------------------------- Other helpers -------------------------- function readUntil(endByte) -- read input buffer untils endByte or timeout local ONE_BYTE = 1 local buf = {} repeat local rx = readBytes(ONE_BYTE) if rx then rx = rx[1] table.insert(buf, rx) end until (not rx or (rx == endByte)) return ((#buf >= ONE_BYTE) and buf) or false 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
hydrus-flowmeter.1692269076.txt.gz ยท Last modified: 2023/08/17 10:44 by emozolyak