User Tools

Site Tools


scylar

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
scylar [2019/01/08 17:22] emozolyakscylar [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 INT 8 heat meters ======
  
-Пользовательский протокол для работы с счетчиком 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 с установленным модулем RS-485.  +If the meter has RS-485 board, it can be connected directly to WebHMI's RS-485 port. 
-Схема подключения счетчика к WebHMI:+
  
-{{::scylar_scheme_2.png?direct|}}+Wiring diagram:
  
 +{{ network:scylar_scheme_2.png?direct&700 |}}
  
 +Setup the connection for the meter as follows:
 +
 +{{ network:scylar_conn_setup_1.png?direct |}}
 +
 +The bus address  (96) can be found in the meters keypad screen #3, Paramter name 'Pri_Adr1'.
 +{{ network:primary_addr.png?direct&200 |}}
 +
 +{{ network:scylar_conn_setup_2.png?direct |}}
 +
 +This protocol can read the following heatmeter's data:
 +
 +^ Parameter ^ Register address in project ^ Units ^
 +|Energy     | CE0                         | 0.001 Gcal |
 +|Energy tariff1    | CEA0                         | 0.001 Gcal |
 +|Energy tariff2    | CEB0                         | 0.001 Gcal |
 +|Volume    | CV0                         | 0.001 m3 |
 +|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, note that return temperature have to be less than forward for 3 or more degrees (otherwise energy won't work). Flowmeter can be simulated by shorting 10-11 pins of i/o board. 
 +
 +{{ network:scylar_conn2.png?direct&300 |}}
 +
 +== 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                                                     dif dife vif vife len bytes 
 +   
 +   addDevice({name = "FWT",  shift = 0, base = 16, xtraFields = {0xA, 0x5A, 0, 0x2}}); -- forward temperature    
 +   addDevice({name = "RET",  shift = 0, base = 16, xtraFields = {0xA, 0x5E, 0, 0x2}}); -- rev. temp.
 +   -- addDevice({name = "ERH",  shift = 0, base = 16, xtraFields = {0xA, 0xA6, 0x18, 0x2}});  -- error hours
 +   addDevice({name = "CFL",  shift = 0, base = 16, xtraFields = {0xB, 0x3B, 0, 0x3}});  -- current flow rate                                                  
 +   addDevice({name = "CV",  shift = 0, base = 16, xtraFields = {0xC, 0x13, 0, 0x4}});  -- current volume 
 +   addDevice({name = "CE",  shift = 0, base = 16, xtraFields = {0xC, 0xFB, 0x0D, 0x4}});  -- current volume
 +   
 +   addDevice({name = "CEA",  shift = 0, base = 16,   xtraFields = {0x8C, 0x10, 0xFB, 0x0D, 0x4}});  -- current energy T1 mask
 +   addDevice({name = "CEB",  shift = 0, base = 16,   xtraFields = {0x8C, 0x20, 0x13, 0x00, 0x4}});  -- current energy T1 mask
 +   addDevice({name = "PWR",  shift = 0, base = 16,   xtraFields = {0x0C, 0x2B, 0x00, 0x4}});  -- power
 +   -- addDevice({name = "OPD",  shift = 0, base = 16, xtraFields = {0xA, 0x27, 0, 0x2}}); -- op. days
 +   
 +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,5,4);
 +    REQ_UD2[4] = getCRC(REQ_UD2,2,2);
 +    
 +    
 +    -- Application reset 
 +    if not AppRST then -- was app reset made ? 
 +        
 +                        --------- App reset -----------
 +         res = sendBytes(app_reset); -- send it  
 +                                                     TRACE("Request was sent!");
 +        if (res == false) then
 +                                                     TRACE("app reset could not send bytes");
 +            return false;
 +        end
 +        local response = readBytes(1); -- read one byte 
 +        if (response == false) then
 +                                            ERROR("Can't read response") ;
 +            return false;
 +        else 
 +                                                    TRACE("App reset was successfull");
 +            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("Repeated AppRST after 100 errors");
 +        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("Can't send bytes");
 +            return false;
 +        end
 +        
 +        -- get first 4 bytes 
 +        local response2 = readBytes(4); 
 +        if (response2 == false)  then
 +                                ERROR("Can't read response, false returned") ;
 +            err_cnt = err_cnt + 1;
 +            return false;
 +        elseif not ((response2[1] == 0x68) and (response2[4] == 0x68)) then 
 +                                ERROR("Wrong response format!68 ~= 68 ") ;
 +            err_cnt = err_cnt + 1;
 +            return false;
 +        elseif (#response2 ~= 4 ) then 
 +                             ERROR("Wrong response2 len , length = "..tostring(#response2)) ;
 +            return false;
 +        else 
 +            -- normal read 
 +            local length = response2[2]; -- get length
 +    
 +            -- get everything 
 +            resp3 = readBytes(length+2); 
 +            if (resp3 == false)  then  
 +                        ERROR("Can't read response, false returned  ") ;
 +                err_cnt = err_cnt + 1;
 +                return false;
 +            elseif  (#resp3 ~= (length+2)) then
 +                            ERROR("Wrong resp3 len , length = "..tostring(#resp3)) ;
 +                return false;
 +                
 +            else 
 +                -- glue packets
 +                for i,v in pairs(resp3) do 
 +                    table.insert(response2,v);
 +                end 
 +                -- crc check
 +                CRCinPacket = tonumber(tostring(response2[#response2-1]));
 +                                     TRACE("CRCinPacket is "..CRCinPacket);
 +                CRCvar = getCRC(response2,5,length);
 +                                     TRACE("Calculated CRCvar is "..CRCvar);
 +                
 +                -- TRACE("And CRCvar "..string.format("%X",CRCvar));
 +               
 +                if CRCinPacket == CRCvar then 
 +                    TRACE("They are equal");
 +                else 
 +                                TRACE("They are NOT equal");
 +                                ERROR("Can't read response, false returned because of CRC !!!  ") ;
 +                    err_cnt = err_cnt + 1;
 +                    return false;
 +                end 
 +                
 +                
 +                -- debug print 
 +                TRACE("C field = "..tostring(resp3[1]));
 +                TRACE("Addr field = "..tostring(resp3[2]));
 +                TRACE("CI field hex= "..string.format("%X",resp3[3]));
 +                -- ID 4 bytes --
 +                local ID_str = "";
 +                for i = 4, 7 do 
 +                    if resp3[i] < 10 then
 +                        ID_str = "0"..string.format("%X",resp3[i])..ID_str;
 +                    else
 +                        ID_str = string.format("%X",resp3[i])..ID_str;
 +                    end 
 +                end 
 +                TRACE("ID hex = "..ID_str);
 +                -- Manufacrutre 2 bytes -- 
 +                local m_str = "";
 +                for i = 8, 9 do 
 +                    if resp3[i] < 10 then
 +                        m_str = "0"..string.format("%X",resp3[i])..m_str;
 +                    else
 +                        m_str = string.format("%X",resp3[i])..m_str;
 +                    end 
 +                end 
 +                TRACE("Manufacture 2 bytes hex = "..m_str);
 +                -- Version --------------------
 +                TRACE("Version = "..string.format("%X",resp3[10]));
 +                TRACE("Medium = "..string.format("%X",resp3[11]));
 +                TRACE("Access No. = "..string.format("%X",resp3[12]));
 +                TRACE("Status = "..string.format("%X",resp3[13]));
 +                ----------------------------------------
 +                -- signature  2 bytes -- 
 +                local m_str = "";
 +                for i = 14, 15 do 
 +                    if resp3[i] < 10 then
 +                        m_str = "0"..string.format("%X",resp3[i])..m_str;
 +                    else
 +                        m_str = string.format("%X",resp[i])..m_str;
 +                    end 
 +                end 
 +                TRACE("Signature  2 bytes hex = "..m_str);
 +                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("Parsing register "..device.name);
 +    position, DataLength, FloatFlag, err_flag = ExtFind(resp3,device.xtraFields);
 +  
 +  if (not err_flag) and (position ~= nil) then 
 +                                         TRACE("parsed .. the following "); 
 +                                         TRACE(reg.addr); 
 +                                         TRACE("And device name = "..device.name);
 +        resp_tab, resp_num = getBCD(resp3, position, DataLength);
 +        return resp_num;
 +  else 
 +                                         TRACE("Error while parsing.. ");
 +        return false;
 +  end
 +
 +
 +end -- of function
 +
 +-- extra --
 +function getBCD(a, pos, len) --  reverse BCD table and make a number 
 +    if (pos == nil) then 
 +                                 TRACE("Pos = nil at entering to getBCD");
 +        return 0xffff; 
 +    end
 +    local str = "";
 +    local result_table = {};
 +    local bcd_sign = 1;
 +    local i, max_i = pos, pos+len-1;
 +                                TRACE("getBCD pos is "..tostring(pos).." till "..tostring(max_i)); 
 +    while i <= max_i do
 +        -- make string 
 +        if (a[i] <= 0x0F) then
 +          str = "0"..string.format("%X",a[i])..str;
 +        else 
 +          str = string.format("%X",a[i])..str;
 +        end 
 +        -- make table 
 +        table.insert(result_table,1,a[i]);
 +        i = i + 1;
 +    end
 +    
 +    -- check sign 
 +    if string.find(str,"(F)") ~= nil then
 +        bcd_sign = -1;
 +        str = string.gsub(str,"F","0",1);
 +    end 
 +                                TRACE("getBCD Going to return .."..str.." and number is - ");
 +    local result = tonumber(str,10)*bcd_sign; 
 +                                TRACE(result);
 +                                TRACE("table concat "..table.concat(result_table));
 +    return result_table, result;
 +end -- getBCD
 +
 +
 +function getCRC(a,pos,len)   
 +local sum  = 0;
 +local mask = 0xFF;
 +
 +for i = pos, pos+len-1 do
 +    sum = sum + a[i];
 +    sum = bit.band(sum,mask);
 +end 
 +TRACE("CRC sum in the end of function = "..tostring(sum));
 +return sum;
 +
 +end -- getCRC
 +
 +
 +function ExtFind(table, difvif_Table)
 +
 +                                                        TRACE("Entered ExtFind function ");
 +    local dib_len,vib_len = 1,1;
 +    local float_flag = false ;  
 +    
 +
 +    local data_LenTab = {[12] = 4, [10] = 2, [11] = 3};   
 +    local data_len = data_LenTab[bit.band(0x0F, difvif_Table[1])];
 +    
 +                                                        TRACE("data length is "..tostring(data_len));
 +   
 +    if data_len == 0x05 then   
 +         data_len = (data_len - 1) ;
 +         float_flag = true;
 +                                                         TRACE("Float data found");
 +    end 
 +    
 +    local i = 1; 
 +    while bit.band(0x80, difvif_Table[i]) == 0x80 do 
 +        i = i + 1;
 +    end -- for 
 +    dib_len = i;
 +                                                        TRACE("DIB length = "..tostring(dib_len));
 +    
 +    local j = i + 1; 
 +        while bit.band(0x80, difvif_Table[j]) == 0x80 do 
 +        j = j + 1;
 +    end -- for 
 +    vib_len = j-i;
 +                                                        TRACE("VIB length = "..tostring(vib_len));
 +    
 +   for i,v in pairs(table) do 
 +
 +
 +            local dif = 0; 
 +            local err_flag = false;    
 +            local dib_match = true;    
 +            local vib_match = true;    
 +        ------------------------------
 +        
 +                --  TRACE("v in Dib search is"..string.format("%X",v));
 +        if (bit.band(0x30, v) == 0x30) then
 +                 -- TRACE("After bit.band v is with error mask "..string.format("%X",v));
 +            err_flag = true ; 
 +
 +            dif = bit.band((0xFF - 0x30), v); 
 +        else 
 +                -- TRACE("No error , now v is "..string.format("%X",v));
 +            dif = v;
 +            err_flag = false;
 +        end
 +            -- TRACE("dif found as "..string.format("%X",v));
 +        
 +        local k=0;
 +        repeat
 +          k = k + 1;
 +          dib_match = dib_match and (table[i+k-1] == difvif_Table[k]);
 +                             -- TRACE("table element ="..string.format("%X",table[i+k-1]).." difvif elmnt = "..
 +                           -- string.format("%X",difvif_Table[k]));
 +                            -- TRACE("K ="..tostring(k).." dib_match = "..tostring(dib_match));
 +        until (k==dib_len); 
 +                             -- TRACE("dib_match "..tostring(dib_match));
 +
 +        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]);
 +                               --  TRACE("g = "..tostring(g).." Vib_match = "..tostring(vib_match));
 +            until (g==vib_len); 
 +                               -- TRACE("vib_match "..tostring(vib_match));
 +        end --if 
 +    
 +        if dib_match and vib_match then 
 +
 +            TRACE("Going to return pos. len, float err flags "..tostring(i+dib_len+vib_len).." "..
 +                                            tostring(data_len).." "..tostring(float_flag).." "..tostring(err_flag));
 +            return i+dib_len+vib_len, data_len, float_flag, err_flag;
 +        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
 +</code>
  
scylar.1546968168.txt.gz · Last modified: 2019/01/08 17:22 by emozolyak

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki