User Tools

Site Tools


scylar

Connecting to Scylar INT 8 heat meters

These heatmeters use Mbus protocol and can be added with the help of custom protocols. Create custom protocol and copy the lua code below into the code block of that custom protocol.

If the meter has RS-485 board, it can be connected directly to WebHMI's RS-485 port.

Wiring diagram:

Setup the connection for the meter as follows:

The bus address (96) can be found in the meters keypad screen #3, Paramter name 'Pri_Adr1'.

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.

Custom protocol (driver) code block
-- 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