scylar
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
scylar [2019/01/08 17:22] – emozolyak | scylar [2022/01/15 15:44] (current) – ↷ Links adapted because of a move operation 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Connecting to Scylar heat meters ====== | + | ====== Connecting to Scylar |
- | Пользовательский протокол для работы с счетчиком SCYLAR INT 8 по протоколу M-Bus | + | These heatmeters use Mbus protocol and can be added with the help of [[custom_protocols|custom protocols]]. |
+ | Create custom protocol and copy the lua code below into the code block of that custom protocol. | ||
- | Протокол предназначен для чтения показаний из расходомера типа SCYLAR INT 8 с установленным модулем | + | If the meter has RS-485 |
- | Схема подключения счетчика к WebHMI: | + | |
- | {{:: | + | Wiring diagram: |
+ | {{ network: | ||
+ | Setup the connection for the meter as follows: | ||
+ | |||
+ | {{ network: | ||
+ | |||
+ | The bus address | ||
+ | {{ network: | ||
+ | |||
+ | {{ network: | ||
+ | |||
+ | This protocol can read the following heatmeter' | ||
+ | |||
+ | ^ Parameter ^ Register address in project ^ Units ^ | ||
+ | |Energy | ||
+ | |Energy tariff1 | ||
+ | |Energy tariff2 | ||
+ | |Volume | ||
+ | |Power | PWR0 | 0.001 kW | | ||
+ | |Flow | CFL0 | 0.001 m3/h | | ||
+ | |Forward temperature | FWT0 | 0.1 °C | | ||
+ | |Return temperature | RET0 | 0.1 °C | | ||
+ | |Operational days | OPD0 | days | | ||
+ | |Error run hours| ERH0 | hours | | ||
+ | |||
+ | The driver returns integer values. You should scale them appropriately to get exact values using Multiplier field | ||
+ | in register properties. | ||
+ | | ||
+ | |||
+ | == Driver test procedure == | ||
+ | |||
+ | Before testing the protocol in the field, it's preferrable to test it in a lab (if you have a meter on-hand). | ||
+ | To simulate temperatures you can use resistors or potentiometers, | ||
+ | |||
+ | {{ network: | ||
+ | |||
+ | == Custom protocol (driver) code block == | ||
+ | |||
+ | <code lua> | ||
+ | -- app reset template, modified then with addr and crc | ||
+ | local app_reset = {0x68, 0x04, 0x04, 0x68, 0x53, 0x5D, 0x50, 0x50, 0x01, 0x16}; | ||
+ | -- get data packet | ||
+ | local REQ_UD2 = {0x10, 0x7B, 0x5D, 0xD8, 0x16}; | ||
+ | |||
+ | local first_scan = true; | ||
+ | local alreadyRead = false; | ||
+ | local timeStmp = 0; | ||
+ | local POLLTIME = 20; | ||
+ | |||
+ | local err_cnt = 0; | ||
+ | local dif_err_mask = 0x30; -- errorneous data read mask | ||
+ | local resp3 = {}; -- response storage table | ||
+ | |||
+ | AppRST = false; | ||
+ | |||
+ | -- | ||
+ | function createDevices () | ||
+ | -- prefix | ||
+ | |||
+ | | ||
+ | | ||
+ | -- addDevice({name = " | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | -- addDevice({name = " | ||
+ | |||
+ | end -- of createDevices | ||
+ | |||
+ | |||
+ | function readRegister (reg, device, unitId) | ||
+ | -- substitution with correct flag | ||
+ | app_reset[6] = unitId; | ||
+ | REQ_UD2[3] = unitId; | ||
+ | | ||
+ | | ||
+ | -- and now CRC | ||
+ | app_reset[9] = getCRC(app_reset, | ||
+ | REQ_UD2[4] = getCRC(REQ_UD2, | ||
+ | | ||
+ | | ||
+ | -- Application reset | ||
+ | if not AppRST then -- was app reset made ? | ||
+ | | ||
+ | --------- App reset ----------- | ||
+ | res = sendBytes(app_reset); | ||
+ | | ||
+ | if (res == false) then | ||
+ | | ||
+ | return false; | ||
+ | end | ||
+ | local response = readBytes(1); | ||
+ | if (response == false) then | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | else | ||
+ | TRACE(" | ||
+ | AppRST = true; | ||
+ | err_cnt = 0; | ||
+ | end | ||
+ | end | ||
+ | |||
+ | |||
+ | local CRCvar, CRCinPacket = 0,0; | ||
+ | | ||
+ | -- reset flag and do app reset againg | ||
+ | if err_cnt > 100 then | ||
+ | AppRST = false; | ||
+ | TRACE(" | ||
+ | return false; | ||
+ | end -- err check | ||
+ | | ||
+ | -- after polltime ask againg | ||
+ | local now = os.time(); | ||
+ | if (not alreadyRead) then | ||
+ | timeStmp = now; | ||
+ | | ||
+ | -- REQ_UD2 -- | ||
+ | local resp2 = sendBytes(REQ_UD2); | ||
+ | if (resp2 == false) then | ||
+ | TRACE(" | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | -- get first 4 bytes | ||
+ | local response2 = readBytes(4); | ||
+ | if (response2 == false) | ||
+ | ERROR(" | ||
+ | err_cnt = err_cnt + 1; | ||
+ | return false; | ||
+ | elseif not ((response2[1] == 0x68) and (response2[4] == 0x68)) then | ||
+ | ERROR(" | ||
+ | err_cnt = err_cnt + 1; | ||
+ | return false; | ||
+ | elseif (#response2 ~= 4 ) then | ||
+ | | ||
+ | return false; | ||
+ | else | ||
+ | -- normal read | ||
+ | local length = response2[2]; | ||
+ | | ||
+ | -- get everything | ||
+ | resp3 = readBytes(length+2); | ||
+ | if (resp3 == false) | ||
+ | ERROR(" | ||
+ | err_cnt = err_cnt + 1; | ||
+ | return false; | ||
+ | elseif | ||
+ | ERROR(" | ||
+ | return false; | ||
+ | | ||
+ | else | ||
+ | -- glue packets | ||
+ | for i,v in pairs(resp3) do | ||
+ | table.insert(response2, | ||
+ | end | ||
+ | -- crc check | ||
+ | CRCinPacket = tonumber(tostring(response2[# | ||
+ | | ||
+ | CRCvar = getCRC(response2, | ||
+ | | ||
+ | | ||
+ | -- TRACE(" | ||
+ | |||
+ | if CRCinPacket == CRCvar then | ||
+ | TRACE(" | ||
+ | else | ||
+ | TRACE(" | ||
+ | ERROR(" | ||
+ | err_cnt = err_cnt + 1; | ||
+ | return false; | ||
+ | end | ||
+ | | ||
+ | | ||
+ | -- debug print | ||
+ | TRACE(" | ||
+ | TRACE(" | ||
+ | TRACE(" | ||
+ | -- ID 4 bytes -- | ||
+ | local ID_str = ""; | ||
+ | for i = 4, 7 do | ||
+ | if resp3[i] < 10 then | ||
+ | ID_str = " | ||
+ | else | ||
+ | ID_str = string.format(" | ||
+ | end | ||
+ | end | ||
+ | TRACE(" | ||
+ | -- Manufacrutre 2 bytes -- | ||
+ | local m_str = ""; | ||
+ | for i = 8, 9 do | ||
+ | if resp3[i] < 10 then | ||
+ | m_str = " | ||
+ | else | ||
+ | m_str = string.format(" | ||
+ | end | ||
+ | end | ||
+ | TRACE(" | ||
+ | -- Version -------------------- | ||
+ | TRACE(" | ||
+ | TRACE(" | ||
+ | TRACE(" | ||
+ | TRACE(" | ||
+ | ---------------------------------------- | ||
+ | -- signature | ||
+ | local m_str = ""; | ||
+ | for i = 14, 15 do | ||
+ | if resp3[i] < 10 then | ||
+ | m_str = " | ||
+ | else | ||
+ | m_str = string.format(" | ||
+ | end | ||
+ | end | ||
+ | TRACE(" | ||
+ | alreadyRead = true; | ||
+ | end | ||
+ | end | ||
+ | end -- if not alreadyRead | ||
+ | | ||
+ | -- set polling time | ||
+ | if (now - timeStmp) >= POLLTIME then | ||
+ | alreadyRead = false; | ||
+ | end | ||
+ | -- | ||
+ | local position = 0; | ||
+ | local resp_num = 0; | ||
+ | local resp_tab = {}; | ||
+ | local err_flag = false; | ||
+ | local DataLength = 0; | ||
+ | local FloatFlag = 0; | ||
+ | | ||
+ | TRACE(" | ||
+ | position, DataLength, FloatFlag, err_flag = ExtFind(resp3, | ||
+ | | ||
+ | if (not err_flag) and (position ~= nil) then | ||
+ | | ||
+ | | ||
+ | | ||
+ | resp_tab, resp_num = getBCD(resp3, | ||
+ | return resp_num; | ||
+ | else | ||
+ | | ||
+ | return false; | ||
+ | end | ||
+ | |||
+ | |||
+ | end -- of function | ||
+ | |||
+ | -- extra -- | ||
+ | function getBCD(a, pos, len) -- reverse BCD table and make a number | ||
+ | if (pos == nil) then | ||
+ | | ||
+ | return 0xffff; | ||
+ | end | ||
+ | local str = ""; | ||
+ | local result_table = {}; | ||
+ | local bcd_sign = 1; | ||
+ | local i, max_i = pos, pos+len-1; | ||
+ | TRACE(" | ||
+ | while i <= max_i do | ||
+ | -- make string | ||
+ | if (a[i] <= 0x0F) then | ||
+ | str = " | ||
+ | else | ||
+ | str = string.format(" | ||
+ | end | ||
+ | -- make table | ||
+ | table.insert(result_table, | ||
+ | i = i + 1; | ||
+ | end | ||
+ | | ||
+ | -- check sign | ||
+ | if string.find(str," | ||
+ | bcd_sign = -1; | ||
+ | str = string.gsub(str," | ||
+ | end | ||
+ | TRACE(" | ||
+ | local result = tonumber(str, | ||
+ | TRACE(result); | ||
+ | TRACE(" | ||
+ | return result_table, | ||
+ | end -- getBCD | ||
+ | |||
+ | |||
+ | function getCRC(a, | ||
+ | local sum = 0; | ||
+ | local mask = 0xFF; | ||
+ | |||
+ | for i = pos, pos+len-1 do | ||
+ | sum = sum + a[i]; | ||
+ | sum = bit.band(sum, | ||
+ | end | ||
+ | TRACE(" | ||
+ | return sum; | ||
+ | |||
+ | end -- getCRC | ||
+ | |||
+ | |||
+ | function ExtFind(table, | ||
+ | |||
+ | TRACE(" | ||
+ | local dib_len, | ||
+ | local float_flag = false ; | ||
+ | | ||
+ | |||
+ | local data_LenTab = {[12] = 4, [10] = 2, [11] = 3}; | ||
+ | local data_len = data_LenTab[bit.band(0x0F, | ||
+ | | ||
+ | TRACE(" | ||
+ | |||
+ | if data_len == 0x05 then | ||
+ | | ||
+ | | ||
+ | | ||
+ | end | ||
+ | | ||
+ | local i = 1; | ||
+ | while bit.band(0x80, | ||
+ | i = i + 1; | ||
+ | end -- for | ||
+ | dib_len = i; | ||
+ | TRACE(" | ||
+ | | ||
+ | local j = i + 1; | ||
+ | while bit.band(0x80, | ||
+ | j = j + 1; | ||
+ | end -- for | ||
+ | vib_len = j-i; | ||
+ | TRACE(" | ||
+ | | ||
+ | for i,v in pairs(table) do | ||
+ | |||
+ | |||
+ | local dif = 0; | ||
+ | local err_flag = false; | ||
+ | local dib_match = true; | ||
+ | local vib_match = true; | ||
+ | ------------------------------ | ||
+ | | ||
+ | -- TRACE(" | ||
+ | if (bit.band(0x30, | ||
+ | -- TRACE(" | ||
+ | err_flag = true ; | ||
+ | |||
+ | dif = bit.band((0xFF - 0x30), v); | ||
+ | else | ||
+ | -- TRACE(" | ||
+ | dif = v; | ||
+ | err_flag = false; | ||
+ | end | ||
+ | -- TRACE(" | ||
+ | | ||
+ | local k=0; | ||
+ | repeat | ||
+ | k = k + 1; | ||
+ | dib_match = dib_match and (table[i+k-1] == difvif_Table[k]); | ||
+ | -- TRACE(" | ||
+ | -- string.format(" | ||
+ | -- TRACE(" | ||
+ | until (k==dib_len); | ||
+ | -- TRACE(" | ||
+ | |||
+ | if dib_match then | ||
+ | | ||
+ | local g=0; | ||
+ | repeat | ||
+ | g = g + 1; | ||
+ | vib_match = vib_match and (table[i+dib_len+g-1] == difvif_Table[dib_len+g]); | ||
+ | | ||
+ | until (g==vib_len); | ||
+ | -- TRACE(" | ||
+ | end --if | ||
+ | | ||
+ | if dib_match and vib_match then | ||
+ | |||
+ | TRACE(" | ||
+ | tostring(data_len).." | ||
+ | return i+dib_len+vib_len, | ||
+ | end | ||
+ | end -- for | ||
+ | return nil; | ||
+ | end -- of EXT Find | ||
+ | -- | ||
+ | |||
+ | |||
+ | function onScanStart () | ||
+ | -- AppRST = false; | ||
+ | end | ||
+ | |||
+ | function writeRegister (reg, device, unitId, newValue) | ||
+ | -- Add your code here | ||
+ | end | ||
+ | </ | ||
scylar.1546968168.txt.gz · Last modified: 2019/01/08 17:22 by emozolyak