User Tools

Site Tools


heat_meter_sks-3_axis_industries

Heat meters SKS-3 by Axis Industries


These meters support the M-Bus exchange protocol. In WebHMI, you can work with them using custom protocols. This protocol can be used with appropriate adjustments for other M-Bus devices as well.

The text of the protocol:

-- app_reset packet addr and CRC should be changed
local app_reset = {0x68, 0x04, 0x04, 0x68, 0x53, 0x5D, 0x50, 0x00, 0x01, 0x16} 
-- get data packet 
local REQ_UD2 = {0x10, 0x7B, 0x5D, 0xD8, 0x16}
 
local err_cnt = 0            -- error counter 
local dif_err_mask = 0x30    -- parameter error mask 
local resp3 = {}            -- table for response bytes 
local alreadyRead = false   -- flag of full read 
local timeStmp = 0          -- timestamp of full read 
AppRST = false              -- flag of app reset done 
--
function createDevices ()
  -- prefix                                                     dif dife vif vife len bytes 
   addDevice({name = "VOL",  shift = 0, base = 16, xtraFields = {0x4, 0, 0x14, 0, 0x4}})  -- current volume
   addDevice({name = "VOLB",  shift = 0, base = 16, xtraFields = {0x84, 0x40, 0x14, 0, 0x4}})  -- current volume 2 
   addDevice({name = "HPWR",  shift = 0, base = 16, xtraFields = {0x05, 0x00, 0x2E, 0, 0x4}})  --   power  1
   addDevice({name = "PWRB",  shift = 0, base = 16, xtraFields = {0x85, 0x40, 0x2E, 0, 0x4}})  --   power 2
 
   addDevice({name = "TA",  shift = 0, base = 16, xtraFields = {0x02, 0x00, 0x59, 0, 0x2}})  -- Temperature 1 
   addDevice({name = "TB",  shift = 0, base = 16, xtraFields = {0x02, 0x00, 0x5D, 0, 0x2}})  -- Temperature 2
   addDevice({name = "TC",  shift = 0, base = 16, xtraFields = {0x02, 0x00, 0x65, 0, 0x2}})  -- Temperature 3
   addDevice({name = "TCC",  shift = 0, base = 16, xtraFields = {0x82, 0x40, 0x59, 0, 0x2}})  -- Temperature 3
   addDevice({name = "TD",  shift = 0, base = 16, xtraFields = {0x82, 0x40, 0x5D, 0, 0x2}})  -- Temperature 4
 
   addDevice({name = "ENA",  shift = 0, base = 16, xtraFields = {0x04, 0x00, 0x06, 0, 0x4}})  --   energy
   addDevice({name = "ENC",  shift = 0, base = 16, xtraFields = {0x04, 0x00, 0x07, 0, 0x4}})  --   energy
   addDevice({name = "END",  shift = 0, base = 16, xtraFields = {0x84, 0x40, 0x05, 0, 0x4}})  --   energy
   addDevice({name = "EAA",  shift = 0, base = 16, xtraFields = {0x04, 0x00, 0x05, 0, 0x4}})  --   energy
 
   addDevice({name = "Q",  shift = 0, base = 16, xtraFields = {0x05, 0x00, 0x3E, 0, 0x4}})  --   flow 1 
   addDevice({name = "QB",  shift = 0, base = 16, xtraFields = {0x85, 0x40, 0x3E, 0, 0x4}})  --   flow 2 
 
end 
-- of createDevices
 
function onScanStart ()
    AppRST = false
end 
 
function readRegister (reg, device, unitId)
 
 -- put addr into packet 
  app_reset[6] = unitId
  REQ_UD2[3] = unitId
 
  -- now put CRC 
  app_reset[9] = getCRC(app_reset,5,4)
  REQ_UD2[4] = getCRC(REQ_UD2,2,2)
 
  -- Application reset 
    if not AppRST then -- not yet ? 
        local res = sendBytes(app_reset) --  send app_reset 
                                    DEBUG("Request was sent!")
        if (res == false) then
                                    DEBUG("app reset was sent send bytes")
            return false
        end
        local response = readBytes(1) -- get reply 
        if (response == false) then
            ERROR("Can't read response") 
            return false
        else 
                                    DEBUG("App reset was successfull")
            AppRST = true
            err_cnt = 0
        end
    end
 
-- after 10 errors reset again 
if err_cnt > 10 then
    AppRST = false
                                    TRACE("Repeated AppRST after 10 errors")
    return false
end -- err check
 
-- after 20 seconds getdata again 
local now = os.time()
if (not alreadyRead) then 
    timeStmp = now -- store time 
       -- REQ_UD2 --
    local resp2 = sendBytes(REQ_UD2) --  send packet 
    if (resp2 == false) then
                                    DEBUG("Can't send bytes")
        return false
    end
 
    -- get first 4 bytes 
    local response2 = readBytes(4) 
    if (response2 == false) then
                                    ERROR("Can't read response") 
       err_cnt = err_cnt + 1
       return false
    elseif not ((response2[1] == 0x68) and (response2[4] == 0x68)) then 
                                    ERROR("Wrong response format!") 
        err_cnt = err_cnt + 1
        return false
    else 
        -- Ok reading full packet 
        local length = response2[2] -- get len from packet 
 
        -- get ALL 
        resp3 = readBytes(length+2) 
        if (resp3 == false) then
                                    ERROR("Can't read response") 
           err_cnt = err_cnt + 1
           return false
        else 
                                    -- debug print 
                                    DEBUG("C field = "..tostring(resp3[1]))
                                    DEBUG("Addr field = "..tostring(resp3[2]))
                                    DEBUG("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 
            DEBUG("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 
                                    DEBUG("Manufacture 2 bytes hex = "..m_str)
                                    -- Version --------------------
                                    DEBUG("Version = "..string.format("%X",resp3[10]))
                                    DEBUG("Medium = "..string.format("%X",resp3[11]))
                                    DEBUG("Access No. = "..string.format("%X",resp3[12]))
                                    DEBUG("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",resp3[i])..m_str
                 end 
            end 
                                    DEBUG("Signature  2 bytes hex = "..m_str)
            alreadyRead = true 
        end 
    end
end        
-- adjust real polling interval here  
if (now - timeStmp) >= 15 then 
    alreadyRead = false
end 
-- 
local position = 0
local resp_num = 0    
local err_flag = false
 
-- find and check value using aux function 
 position, err_flag = efindpos(resp3, device.xtraFields[1], device.xtraFields[2], device.xtraFields[3], device.xtraFields[4]) 
-- convert to number 
 resp_num = getBCD(resp3, position, device.xtraFields[5])
 
 err_cnt = 0
      if not err_flag then 
        return resp_num
     else 
         return 0xffff
     end 
 
end -- of function
 
function writeRegister (reg, device, unitId, newValue)
  -- Add your code here
end
 
-- AUXILARY FUNCTIONS --
function getBCD(a,pos,len) -- 
 
local str = ""
local i,max_i = pos, pos+len-1
                                TRACE("getDCD 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 
        i = i + 1
    end
                                TRACE("getDCD Going to return .."..str.." and number is - ")
    local result = tonumber(str,16)
                                TRACE(result)
return result
end -- getBCD
 
-- calc CRC 
function getCRC(a,pos,len)   
 
 local sum  = 0
 
for i = pos, pos+len-1 do
    sum = sum + a[i]
end 
                                TRACE("CRC sum = "..tostring(sum))
return sum
end -- getCRC
 
-- find data in packet using DIF VIF  
function efindpos(a, dif, dife, vif, vife)
    -- 
    local dife_exists = false
    local vife_exists = false
    local err_flag = false     
    -- check for error 
    if (bit.band(0x30, dif) == 0x30) then
        err_flag = true 
    end
    -- check  dif
    if (bit.band(0x80, dif) == 0x80) then
        dife_exists = true 
    end 
    -- if  vife exist 
    if (bit.band(0x80, vif) == 0x80) then
        vife_exists = true 
    end 
    --
    if (not dife_exists) then 
        if (not vife_exists) then -- dif vif
                for i,v in ipairs (a) do
                    if dif == bit.band(v,0x0f) then -- compare nibble w/o error 
                        if a[i+1] == vif then 
                            return i+2,err_flag
                        end
                    end
                 end -- loop
         else -- vife_exists
                for i,v in ipairs (a) do
                    if dif == bit.band(v,0x0f) then -- compare low nibbel w/o error 
                        if (a[i+1]==vif) and (a[i+2]==vife) then 
                            return i+3,err_flag
                        end
                    end
                 end -- loop
          end 
    else -- dife_exists
        -- INFO("efindpos dife exists"..string.format("%X",dife))
      if (not vife_exists) then -- dif vif
                for i,v in ipairs (a) do
                    if (dif == bit.band(v,0x8f) and a[i+1] == dife ) then -- 
                        if a[i+2] == vif then 
                            return i+3,err_flag
                        end
                    end
                 end -- loop
         else -- dife vife_exists
             -- DEBUG("efindpos Vife exists"..string.format("%X",vife))
                for i,v in ipairs (a) do
                    local condition = ( (dif == bit.band(v,0x8f)) and (dife == a[i+1]))
                    if condition then -- сравниваем мл тетраду без ошибки 
                        if (a[i+2]==vif) and (a[i+3]==vife) then 
                            return i+4,err_flag
                        end
                    end
                 end -- loop
          end 
 
    end -- if
return nil --  found nothing 
end -- of efindpos