--[[ Protocol : Profibus Template by : IFTOOLS GmbH Version : 2013.07.17 Initial commit Filter code for SC affects other telegrams - fixed Version : 2013.08.07 Make template ready for export and fix wrong function field display. Add more error checks (to avoid empty telegram lines and export errors) Version : 2014.04.29 Add description and comments to the supported templates Version : 2014.12.03 Adapt template to newest ProtocolView version 4.1.9 Version : 2019.03.26 Cover unknown/invalid index in GetSAP function Version : 2020.04.29 Rename obsolete/changed module names for cfg, bit and protocol Version : 2023.07.06 Update version history Brief: Profibus is a fieldbus communication standard first promoted in 1989 by the German department of education and research (BMBF) and then used by Siemens. A Profibus system uses a bus master to cyclically poll slave devices distributed in multi-drop fashion on a RS485 serial bus. Slaves are not allowed to send data without request by the master. As like in Modbus telegrams are separated by a specified time gap (sending or sync pause) of 33 bits. Frame format: Various telegram types are used, distinguishable by their start delimiter SD. SD1 No data (hex 10) SD2 Variable length data (hex 68) SD3 Fixed length data (hex A2) SD4 Token (hex DC) SC Brief acknowledge (hex E5) Filter: The template offers a telegram type filter. You can show all types or select one in the filter control. --]] -- define a filter list for all telegram types function filters() return "All,SD1,SD2,SD3,SD4,SC" end -- function split is called every time a data byte is received -- with the following parameters: -- data : the data value (9 bit) (unused) -- intval : the time distance to the former data -- alter : is true when the data source (direction) was changed (unused) -- str : all received data bytes of the current telegram as a string -- filter : contains the text selected or inputed in the filter control -- returns the telegram state: UNCHANGED, STARTED, MODIFIED, COMPLETED function split( data, intval, alter, str ,filter) local result = MODIFIED if intval > transmission.bitpause( 20 ) then -- SD telegrams consists of only one byte and has to handle in front of all for filtering --if #str > 1 and str:sub(1,1) == string.char("0xE5") and filter:match("SD") then return REMOVED end if #str > 1 and str:byte(1) == 0xE5 and filter:match("SD") then return REMOVED end return STARTED end if #str > 1 then if filter == "SD1" and str:byte(1) ~= 0x10 then result = REMOVED elseif filter == "SD2" and str:byte(1) ~= 0x68 then result = REMOVED elseif filter == "SD3" and str:byte(1) ~= 0xA2 then result = REMOVED elseif filter == "SD4" and str:byte(1) ~= 0xDC then result = REMOVED elseif filter == "SC" and str:byte(1) ~= 0xE5 then result = REMOVED end end return result end function out() -- the telegram corresponding to the current line local telegram = telegrams.this() if telegram:data(1) == 0x10 then SD1( telegram ) elseif telegram:data(1) == 0x68 then SD2( telegram ) elseif telegram:data(1) == 0xA2 then SD3( telegram ) elseif telegram:data(1) == 0xDC then SD4( telegram ) elseif telegram:data(1) == 0xE5 then SC( telegram ) else Error( telegram ) end end function SD1( telegram ) if telegram:size() < 6 then Error() do return end end box.text{ caption="Type", text="SD1", bg=GetColor("SD1") } GetAddrBox( telegram:data(2), telegram:data(3), telegram:data(4) ) GetFuncBox( telegram:data(4) ) --do return end -- checksum GetChecksumBox( telegram:data(5), Chksum( telegram:string():sub(2,4) ) ) box.text{ caption="ED", text=string.format("0x%x",telegram:data(6)), bg=GetColor("ED") } end function SD2( telegram ) local datapos = 8 if telegram:size() < 10 then Error() do return end end box.text{ caption="Type", text="SD2", bg=GetColor("SD2") } box.text{ caption="LE", text=telegram:data(2), bg=GetColor("LE") } box.text{ caption="LEr", text=telegram:data(3), bg=GetColor("LEr") } box.text{ caption="Type", text="SD2", bg=GetColor("SD2") } -- request/response with addresses GetAddrBox( telegram:data(5), telegram:data(6), telegram:data(7) ) GetFuncBox( telegram:data(7) ) -- check for SAP if telegram:data(5) >= 0x80 then box.text{ caption="DSAP", text=GetSAP( telegram:data(8) ).." ("..telegram:data(8)..")", bg=GetColor("DSAP")} datapos = datapos + 1 end if telegram:data(6) >= 0x80 then box.text{ caption="SSAP", text=GetSAP( telegram:data(9) ).." ("..telegram:data(9)..")", bg=GetColor("SSAP")} datapos = datapos + 1 end if datapos < telegram:size() - 1 then box.text{ caption="Data", text=telegram:dump{ first=datapos, last=-3, width=2 }, bg=GetColor("PDU") } end -- checksum GetChecksumBox( telegram:data(-2),Chksum( telegram:string():sub( 5,-3 ) ) ) box.text{ caption="ED", text=string.format("0x%x",telegram:data(-1)), bg=GetColor("ED")} end function SD3( telegram ) box.text{ caption="Type", text="SD3" } GetAddrBox( telegram:data(2), telegram:data(3), telegram:data(4) ) GetFuncBox( telegram:data(4) ) -- data length is always 8 byte box.text{ caption="Data", text=telegram:dump{ first=5, last=12, width=2 }, GetColor("PDU") } box.text{ caption="FCS", text=telegram:size()-1, bg=GetColor("FCS") } box.text{ caption="ED", text=string.format("0x%x",telegram:data(-1)), bg=GetColor("ED")} end function SD4( telegram ) if telegram:size() < 2 then Error() do return end end box.text{ caption="Type", text="SD4", bg=GetColor("SD4") } box.text{ caption="DA", text=telegram:data(2), bg=GetColor("DA") } box.text{ caption="SA", text=telegram:data(3), bg=GetColor("SA") } end function SC() box.text{ caption="Type", text="SC", bg=GetColor("SC") } end function Chksum( data ) local sum = 0 for i=1,#data do sum = sum + data:byte(i) end return sum % 256 end function Error() box.text{ caption="Error", text="Unknown telegram!", bg=GetColor("Error") } end function GetAddrBox( da, sa, fnc ) if fnc >= 0x40 then box.text{ caption="Addr", text=sa % 128 .."->".. da % 128, bg=GetColor("DA") } else box.text{ caption="Addr", text=da % 128 .."<-".. sa % 128, bg=GetColor("DA") } end end function GetChecksumBox( is, must ) if is == must then box.text{ caption="FCS", text=is, bg=GetColor("FCSOK") } else box.text{ caption="FCS", text=is.." must "..must, bg=GetColor("FCSERR") } end end function GetFuncBox( fnc ) local text = "Req>:" if fnc < 0x40 then text = "Resp<:" end box.text{ caption="FC", text=text..GetFunc(fnc), bg=GetColor("FC") } end function GetColor( field ) local t = { ["LE"] = 0xFFFFFF, ["DA"] = 0xFFD0A0, ["SA"] = 0xFFD0A0, ["FC"] = 0xC0FFFF, ["LE"] = 0xFFDC84, ["LEr"] = 0xFFDC84, ["PDU"] = 0xFFFFC0, ["FCSOK"] = 0xC0FF80, ["FCSERR"] = 0xFF8080, ["ED"] = 0xFFDD00, ["SD1"] = 0x1ED0FF, ["SD2"] = 0x87AFD4, ["SD3"] = 0x9188E0, ["SD4"] = 0xA974CA, ["SC"] = 0xCE7777, ["Error"] =0xFF6600 } return t[field] end function GetFunc(no) -- bit 6:1 = request, response otherwise if no >= 0x40 then local t = { [0] = "Time Event", [1] = "(1)?", [2] = "(2)?", [3] = "(3)?", [4] = "SDN low", [5] = "(5)?", [6] = "SDN high", [7] = "DDB", [8] = "(8)?", [9] = "Request FDL Status", [10] = "TE", -- Actual time event [11] = "CE", -- Actual counter event [12] = "SRD low", -- send request data low [13] = "SRD high", -- send request data high [14] = "Request ID", [15] = "Request LSAP Status" } return t[no % 16 ] else local t = { [0] = "OK", [1] = "UE", -- user error [2] = "RR", -- no resource/memory)", [3] = "RS", -- SAP not active, wrong state [4] = "(4)?", [5] = "(5)?", [6] = "(6)?", [7] = "(7)?", [8] = "DL", -- data low [9] = "NR", -- no response data available [10] = "DH", -- data high [11] = "(11)?", [12] = "RDL", -- no data received and data low [13] = "RDH", -- no data received and data high [14] = "(14)?", [15] = "(15)?" } return t[no % 16] end end function GetSAP(byte) local t = { [0] = "Write_Read_Data", [48] = "Communication", [49] = "Initiate", [50] = "Alarm SAP", [54] = "M-M Communication", [55] = "Set_Slave_Add", [56] = "Rd_Inp", [57] = "Rd_Outp", [58] = "Global_Control", [59] = "Get_Cfg", [60] = "Slave_Diagnosis", [61] = "Set_Prm", [62] = "Chk_Cfg" } if t[byte] then return t[byte] else return "UNKNOWN" end end