Теплосчетчик SCYLAR INT 8
Материал из WebHMI Wiki
(перенаправлено с «Теплосчетчик SCAYLAR INT 8»)
Пользовательский протокол для работы с счетчиком SCYLAR INT 8 по протоколу M-Bus
Протокол предназначен для чтения показаний из расходомера типа SCYLAR INT 8 с установленным модулем RS-485.
Схема подключения счетчика к WebHMI:
Настройки соединения в WebHMI:
Чтобы в списке “Тип устройства» появился протокол , его надо добавить в систему. Текст протокола – см. в конце статьи.
Свойства соединения для работы с данным устройством:
Адрес (96) можно узнать из меню прибора №3, индикация на дисплее Pri_Adr1
Для получения точного отображения числа, необходимо в свойствах регистра, в вкладке «Значение» указать Множитель и Точность, например для температуры множитель = 0.1 и точность 1 разряд. Драйвер возвращает целочисленное значение.
- ПРОВЕРКА РАБОТЫ:
На входы температур подключить резисторы ~ 500 Ом, резистор обратки должен быть меньше,
чтобы разница температур составила более 3 градусов (тогда будет работать счетчик энергии).
Импульсы можно подать кнопкой, замыкая контакты 10-11.
- ТЕКСТ ПРОТОКОЛА
-- строка инициализации, сюда надо поставить адрес и контрольную сумму
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