Теплосчетчик SCYLAR INT 8

Материал из WebHMI Wiki
(перенаправлено с «Теплосчетчик SCAYLAR INT 8»)
Перейти к: навигация, поиск

Пользовательский протокол для работы с счетчиком SCYLAR INT 8 по протоколу M-Bus

Протокол предназначен для чтения показаний из расходомера типа SCYLAR INT 8 с установленным модулем RS-485.
Схема подключения счетчика к WebHMI:
Connection.png

Настройки соединения в WebHMI:

Чтобы в списке “Тип устройства» появился протокол , его надо добавить в систему. Текст протокола – см. в конце статьи.
Custom settings.png
Свойства соединения для работы с данным устройством:
Conn1.png
Адрес (96) можно узнать из меню прибора №3, индикация на дисплее Pri_Adr1
Conn2.png

Читаемые регистры:
Table regs.png

Для получения точного отображения числа, необходимо в свойствах регистра, в вкладке «Значение»
указать Множитель и Точность, например для температуры множитель = 0.1 и точность 1 разряд. Драйвер
возвращает целочисленное значение.
ПРОВЕРКА РАБОТЫ:

На входы температур подключить резисторы ~ 500 Ом, резистор обратки должен быть меньше, чтобы разница температур составила более 3 градусов (тогда будет работать счетчик энергии). Импульсы можно подать кнопкой, замыкая контакты 10-11.
Test wiring.png


ТЕКСТ ПРОТОКОЛА
-- строка инициализации, сюда надо поставить адрес и контрольную сумму 
local app_reset = {0x68, 0x04, 0x04, 0x68, 0x53, 0x5D, 0x50, 0x50, 0x01, 0x16}; 
-- запрос на получение данных 
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;    -- маска ошибки значения параметра 
local resp3 = {}; -- Таблица для хранения значимой части ответа 

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)
    -- подставить адреса в пакеты из адреса 
    app_reset[6] = unitId;
    REQ_UD2[3] = unitId;
    
    
    -- теперь поставить контрольные СУММЫ
    app_reset[9] = getCRC(app_reset,5,4);
    REQ_UD2[4] = getCRC(REQ_UD2,2,2);
    
    
    -- Application reset 
    if not AppRST then -- была ли инициализация ? 
        
                        --------- App reset -----------
         res = sendBytes(app_reset); --  отправка запроса на инициализацию
                                                     TRACE("Request was sent!");
        if (res == false) then
                                                     TRACE("app reset could not send bytes");
            return false;
        end
        local response = readBytes(1); -- прием ответа
        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;
    
    -- сделать инициализацию
    if err_cnt > 100 then
        AppRST = false;
                                            TRACE("Repeated AppRST after 100 errors");
        return false;
    end -- err check
    
    -- если прошло 20 сек. тогда отправить полный запрос 
    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
        
        -- прием первых 4 байт с ответом 
        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 
            -- нормальное чтение читаем весь пакет 
            local length = response2[2]; -- длину берем из пакета 
    
            -- прием ВСЕГО ответа 
            resp3 = readBytes(length+2); 
            if (resp3 == false)  then  -- стремное место TODO: добавить проверку КОЛИЧЕСТВА прочитанных байт
                        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 
                -- склеиваем пакет 
                for i,v in pairs(resp3) do 
                    table.insert(response2,v);
                end 
                -- проверяем контрольную сумму
                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 
                
                
                -- Отладочная печать
                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
    
    -- настройка частоты опроса 
    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

-- Вспомогатеьные функции --
function getBCD(a, pos, len) --  переворачивает в обратном порядке табличку BCD и делает число         
    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
        -- делаем строку
        if (a[i] <= 0x0F) then
          str = "0"..string.format("%X",a[i])..str;
        else 
          str = string.format("%X",a[i])..str;
        end 
        -- делаем табличку
        table.insert(result_table,1,a[i]);
        i = i + 1;
    end
    
    -- проверяем знак 
    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; -- если BCD то должно быть число
                                TRACE(result);
                                TRACE("table concat "..table.concat(result_table));
    return result_table, result;
end -- getBCD

-- GetCRC посчитать контрольную сумму
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

-- Расширенный поиск 3-ая версия 
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    -- проверка на float результата 
         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;     -- если есть ошибка в dif
            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));
        -- проверяем dib
        
        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));
         -- проверяем ib
        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