Skip to content

Commit 376f47c

Browse files
authored
Add initial version of embobj wireshark dissector (#111)
1 parent 774a093 commit 376f47c

3 files changed

Lines changed: 378 additions & 0 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,20 @@ So far, the repository `icub-firmware-shared` is used to compile only on the pc1
3535
* Run `make` to generate the correct dependencies required by `icub-main` (actually this step is not strictly required).
3636

3737
At this point a run of `ccmake ../` in `icub-main` build folder will be able to find the suitable dependencies for `icub-firmware-shared`.
38+
39+
## Wireshark dissector for embObj
40+
41+
The repository also contains a [Wireshark Dissector](https://www.wireshark.org/docs/wsdg_html_chunked/ChapterDissection.html) for simplifying the reading of dumps of embObj data in [Wireshark](https://www.wireshark.org/), contained in the source code at [`eth/embobj_wireshark.lua`](./eth/embobj_wireshark.lua) and that is installed at `share/icub_firwmware_shared/embobj_wireshark.lua` .
42+
43+
To install the Wireshark dissector, locate the "Personal Lua Plugins" used by your WireShark installation by opening Wireshark and navigating to Help -> About Wireshark -> Folders, and find the "Personal Lua Plugins" location, then copy the `embobj_wireshark.lua` file to it.
44+
45+
For example on linux, you can just copy the `embobj_wireshark.lua` file to `~/.local/lib/wireshark/plugins`:
46+
47+
~~~bash
48+
mkdir -p ~/.local/lib/wireshark/plugins
49+
cp ./eth/embobj_wireshark.lua ~/.local/lib/wireshark/plugins
50+
~~~
51+
3852

3953
## Maintainers
4054
This repository is maintained by:

eth/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,7 @@ if(WITH_EMBOBJ)
251251
PATTERN "*.h") #TODO check if we need only the header
252252
install(DIRECTORY robotconfig
253253
DESTINATION ${icub_firmware_shared_INSTALL_INCLUDE_DIR})
254+
255+
install(FILES embobj_wireshark.lua
256+
DESTINATION ${CMAKE_INSTALL_DATADIR}/icub_firmware_shared)
254257
endif()

eth/embobj_wireshark.lua

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
-- embObj ROP dissector
2+
-- SPDX-FileCopyrightText: Fondazione Istituto Italiano di Tecnologia (IIT)
3+
-- SPDX-License-Identifier: BSD-3-Clause
4+
5+
-- The starting point for this dissector was these ChatGPT conversations:
6+
-- https://chatgpt.com/share/6811f209-2edc-8006-bb06-93580b399e61
7+
-- https://chatgpt.com/share/6812222c-fbe4-8006-9215-f175e661af3d
8+
-- https://chatgpt.com/share/68123fba-2654-8006-8d2a-a330a31d4f28
9+
-- The code has been then modified based on tests.
10+
11+
local p_rop = Proto("embobj_rop","embObj ROP")
12+
13+
-- Frame header
14+
local f_start = ProtoField.uint32 ("embobj_rop.start" , "Start-of-Frame", base.HEX)
15+
local f_size = ProtoField.uint16 ("embobj_rop.ropsize" , "ROPs byte-count", base.DEC)
16+
local f_nrops = ProtoField.uint16 ("embobj_rop.nrops" , "# ROPs", base.DEC)
17+
local f_age = ProtoField.uint64 ("embobj_rop.age" , "Frame age [µs]" , base.DEC)
18+
local f_seq = ProtoField.uint64 ("embobj_rop.seq" , "Sequence #", base.DEC)
19+
local f_end = ProtoField.uint32 ("embobj_rop.end" , "End-of-Frame", base.HEX)
20+
21+
-- ROP header
22+
local f_ctrl = ProtoField.uint8 ("embobj_rop.ctrl" , "CTRL" , base.HEX)
23+
local f_ctrl_cinfo = ProtoField.uint8 ("embobj_rop.ctrl.cinfo","Confirmation",base.DEC,nil,0x03)
24+
local f_ctrl_ptime = ProtoField.bool ("embobj_rop.ctrl.ptime","+TIME" ,8,{"yes","no"},0x04)
25+
local f_ctrl_psign = ProtoField.bool ("embobj_rop.ctrl.psign","+SIGN" ,8,{"yes","no"},0x08)
26+
local f_ctrl_rtime = ProtoField.bool ("embobj_rop.ctrl.rqtime","Req.TIME" ,8,nil,0x10)
27+
local f_ctrl_rconf = ProtoField.bool ("embobj_rop.ctrl.rqconf","Req.CONF" ,8,nil,0x20)
28+
local f_ctrl_ver = ProtoField.uint8 ("embobj_rop.ctrl.ver","Ver" ,base.DEC,nil,0xC0)
29+
local f_opcode = ProtoField.uint8 ("embobj_rop.opc" , "OpCode",base.DEC,{
30+
[1]="ASK",[2]="SAY",[3]="SET",[4]="SIG",[5]="RST"})
31+
local f_dsize = ProtoField.uint16 ("embobj_rop.dsize" , "Data len",base.DEC)
32+
local f_id32 = ProtoField.uint32 ("embobj_rop.id32" , "ID32", base.HEX)
33+
34+
-- sub-fields for ID32 (one byte each)
35+
local f_id32_ep = ProtoField.uint8 ("embobj_rop.id32.ep" , "Endpoint",base.DEC)
36+
local f_id32_ent = ProtoField.uint8 ("embobj_rop.id32.ent" , "Entity" ,base.DEC)
37+
local f_id32_idx = ProtoField.uint8 ("embobj_rop.id32.idx" , "Index" ,base.DEC)
38+
local f_id32_tag = ProtoField.uint8 ("embobj_rop.id32.tag" , "Tag" ,base.HEX)
39+
40+
local f_sign = ProtoField.uint32 ("embobj_rop.sign" , "Signature",base.HEX)
41+
local f_time = ProtoField.uint64 ("embobj_rop.time" , "Time [µs]",base.DEC)
42+
43+
p_rop.fields = {f_start,f_size,f_nrops,f_age,f_seq,f_end,
44+
f_ctrl,f_ctrl_cinfo,f_ctrl_ptime,f_ctrl_psign,
45+
f_ctrl_rtime,f_ctrl_rconf,f_ctrl_ver,
46+
f_opcode,f_dsize,f_id32,f_id32_ep,f_id32_ent,
47+
f_id32_idx,f_id32_tag,f_sign,f_time}
48+
49+
-- These are endpoint names, as defined in eOprot_endpoint_t
50+
-- see eth/embobj/plus/comm-v2/protocol/api/EoProtocol.h
51+
local function endpoint_name(val)
52+
local lookup = {
53+
[0] = "management",
54+
[1] = "motioncontrol",
55+
[2] = "analogsensors",
56+
[3] = "skin",
57+
[254] = "all",
58+
[255] = "none",
59+
}
60+
return lookup[val] or "?"
61+
end
62+
63+
-- These are entity names, as defined in eOprot_entity_t
64+
-- see eth/embobj/plus/comm-v2/protocol/api/EoProtocol.h
65+
local function entity_name(endpoint_name, val)
66+
if val == 255 then return "none" end
67+
68+
local ent_names = {
69+
management = { [0]="comm",[1]="appl",[2]="info",[3]="service" },
70+
motioncontrol = { [0]="joint",[1]="motor",[2]="controller" },
71+
analogsensors = { [0]="strain",[1]="mais",[2]="temperature",[3]="inertial3",[4]="psc",[5]="pos",[6]="ft",[7]="battery" },
72+
skin = { [0]="skin" },
73+
}
74+
75+
-- Defensive lookup
76+
local endpoint = ent_names[endpoint_name]
77+
return endpoint and endpoint[val] or "?"
78+
end
79+
80+
-- These are tag names, as defined in the header of each end point, see inline comments :
81+
local function tag_name(endpoint_name, entity_name, val)
82+
if val == 255 then return "none" end
83+
84+
local tag_names = {
85+
86+
-- eth/embobj/plus/comm-v2/protocol/api/EoProtocolMN.h
87+
management = {
88+
-- eOprot_tag_mn_comm_t
89+
comm = {
90+
[0] = "wholeitem",
91+
[1] = "status",
92+
[2] = "status_managementprotocolversion",
93+
[3] = "cmmnds_command_querynumof",
94+
[4] = "cmmnds_command_queryarray",
95+
[5] = "cmmnds_command_replynumof",
96+
[6] = "cmmnds_command_replyarray",
97+
[7] = "cmmnds_command_config",
98+
},
99+
-- eOprot_tag_mn_appl_t
100+
appl = {
101+
[0] = "wholeitem",
102+
[1] = "config",
103+
[2] = "config_txratedivider",
104+
[3] = "status",
105+
[4] = "cmmnds_go2state",
106+
[5] = "cmmnds_timeset",
107+
},
108+
-- eOprot_tag_mn_info_t
109+
info = {
110+
[0] = "wholeitem",
111+
[1] = "config",
112+
[2] = "config_enabled",
113+
[3] = "status",
114+
[4] = "status_basic",
115+
},
116+
-- eOprot_tag_mn_service_t
117+
service = {
118+
[0] = "wholeitem",
119+
[1] = "status_commandresult",
120+
[2] = "cmmnds_command",
121+
},
122+
},
123+
124+
-- eth/embobj/plus/comm-v2/protocol/api/EoProtocolMC.h
125+
motioncontrol = {
126+
-- eOprot_tag_mc_joint_t
127+
joint = {
128+
[0] = "wholeitem",
129+
[1] = "config",
130+
[2] = "config_pidposition",
131+
[3] = "config_pidvelocity",
132+
[4] = "config_pidtorque",
133+
[5] = "config_userlimits",
134+
[6] = "config_impedance",
135+
[7] = "config_motor_params",
136+
[8] = "config_tcfiltertype",
137+
[9] = "status",
138+
[10] = "status_core",
139+
[11] = "status_target",
140+
[12] = "status_core_modes_controlmodestatus",
141+
[13] = "status_core_modes_interactionmodestatus",
142+
[14] = "status_core_modes_ismotiondone",
143+
[15] = "status_addinfo_multienc",
144+
[16] = "status_debug",
145+
[17] = "inputs",
146+
[18] = "inputs_externallymeasuredtorque",
147+
[19] = "cmmnds_calibration",
148+
[20] = "cmmnds_setpoint",
149+
[21] = "cmmnds_stoptrajectory",
150+
[22] = "cmmnds_controlmode",
151+
[23] = "cmmnds_interactionmode",
152+
},
153+
-- eOprot_tag_mc_motor_t
154+
motor = {
155+
[0] = "wholeitem",
156+
[1] = "config",
157+
[2] = "config_currentlimits",
158+
[3] = "config_gearbox_M2J",
159+
[4] = "config_rotorencoder",
160+
[5] = "config_pwmlimit",
161+
[6] = "config_temperaturelimit",
162+
[7] = "config_pidcurrent",
163+
[8] = "config_pidspeed",
164+
[9] = "status",
165+
[10] = "status_basic",
166+
},
167+
-- eOprot_tag_mc_controller_t
168+
controller = {
169+
[0] = "wholeitem",
170+
[1] = "config",
171+
[2] = "status",
172+
},
173+
},
174+
175+
-- eth/embobj/plus/comm-v2/protocol/api/EoProtocolAS.h
176+
analogsensors = {
177+
-- eOprot_tag_as_strain_t
178+
strain = {
179+
[0] = "wholeitem",
180+
[1] = "config",
181+
[2] = "status",
182+
[3] = "status_fullscale",
183+
[4] = "status_calibratedvalues",
184+
[5] = "status_uncalibratedvalues",
185+
},
186+
-- eOprot_tag_as_mais_t
187+
mais = {
188+
[0] = "wholeitem",
189+
[1] = "config",
190+
[2] = "config_mode",
191+
[3] = "config_datarate",
192+
[4] = "config_resolution",
193+
[5] = "status",
194+
[6] = "status_the15values",
195+
},
196+
-- eOprot_tag_as_temperature_t
197+
temperature = {
198+
[0] = "wholeitem",
199+
[1] = "config",
200+
[2] = "status",
201+
[3] = "cmmnds_enable",
202+
},
203+
-- eOprot_tag_as_inertial3_t
204+
inertial3 = {
205+
[0] = "wholeitem",
206+
[1] = "config",
207+
[2] = "status",
208+
[3] = "cmmnds_enable",
209+
},
210+
-- eOprot_tag_as_psc_t
211+
psc = {
212+
[0] = "wholeitem",
213+
[1] = "config",
214+
[2] = "status",
215+
[3] = "cmmnds_enable",
216+
},
217+
-- eOprot_tag_as_pos_t
218+
pos = {
219+
[0] = "wholeitem",
220+
[1] = "config",
221+
[2] = "status",
222+
[3] = "cmmnds_enable",
223+
},
224+
-- eOprot_tag_as_ft_t
225+
ft = {
226+
[0] = "config",
227+
[1] = "cmmnds_enable",
228+
[2] = "status",
229+
[3] = "status_timedvalue",
230+
[4] = "status_fullscale",
231+
},
232+
-- eOprot_tag_as_battery_t
233+
battery = {
234+
[0] = "config",
235+
[1] = "cmmnds_enable",
236+
[2] = "status",
237+
[3] = "status_timedvalue",
238+
},
239+
},
240+
241+
-- =========================== skin ==============================
242+
-- eth/embobj/plus/comm-v2/protocol/api/EoProtocolSK.h
243+
skin = {
244+
-- eOprot_tag_sk_skin_t
245+
skin = {
246+
[0] = "wholeitem",
247+
[1] = "config_sigmode",
248+
[2] = "status_arrayofcandata",
249+
[3] = "cmmnds_boardscfg",
250+
[4] = "cmmnds_trianglescfg",
251+
},
252+
},
253+
}
254+
255+
-- Defensive lookup
256+
local ep = tag_names[endpoint_name]
257+
local ent = ep and ep[entity_name]
258+
return ent and ent[val] or "?"
259+
end
260+
261+
262+
263+
-- constants
264+
local START_MAGIC = 0x12345678
265+
local END_MAGIC = 0x87654321
266+
local HEADER_LEN = 24
267+
local ROP_HEADLEN = 8
268+
269+
-- helpers
270+
local function name_or(tbl,val) -- small helper: returns a string or "?"
271+
local v = tbl[val]
272+
return v and v or "?"
273+
end
274+
275+
-- ROP dissection
276+
local function dissect_single_rop(tvb,off,tree,idx)
277+
local sub = tree:add(p_rop,string.format("ROP [%d]",idx))
278+
279+
-- CTRL
280+
sub:add_le(f_ctrl ,tvb(off,1))
281+
sub:add_le(f_ctrl_cinfo ,tvb(off,1))
282+
sub:add_le(f_ctrl_ptime ,tvb(off,1))
283+
sub:add_le(f_ctrl_psign ,tvb(off,1))
284+
sub:add_le(f_ctrl_rtime ,tvb(off,1))
285+
sub:add_le(f_ctrl_rconf ,tvb(off,1))
286+
sub:add_le(f_ctrl_ver ,tvb(off,1))
287+
288+
local ctrl = tvb(off,1):le_uint()
289+
local ptime = bit.band(ctrl,0x04)~=0
290+
local psign = bit.band(ctrl,0x08)~=0
291+
off = off+1
292+
293+
-- OPC / DSIZE
294+
sub:add(f_opcode,tvb(off,1)); off=off+1
295+
local dlen = tvb(off,2):le_uint()
296+
sub:add_le(f_dsize ,tvb(off,2)); off=off+2
297+
298+
-- ID32
299+
sub:add_le(f_id32 ,tvb(off,4))
300+
301+
-- break the 32 bits into 4 bytes (little-endian on the wire)
302+
local id32_le = tvb(off,4):le_uint()
303+
local ep = bit.rshift(id32_le,24) & 0xFF
304+
local ent = bit.rshift(id32_le,16) & 0xFF
305+
local idx = bit.rshift(id32_le, 8) & 0xFF
306+
local tag = bit.band (id32_le,0xFF)
307+
308+
local epname = endpoint_name(ep)
309+
local entname = entity_name(epname,ent)
310+
local tagname = tag_name(epname,entname,tag)
311+
312+
local idnode = sub:add(p_rop, tvb(off,4),
313+
string.format("ID32 → EP:%d (%s) ENT:%d (%s) IDX:%d TAG:0x%02X (%s)",
314+
ep,epname, ent,entname, idx, tag, tagname))
315+
316+
idnode:add_le(f_id32_ep ,tvb(off+3,1)):append_text(" ("..epname..")")
317+
idnode:add_le(f_id32_ent,tvb(off+2,1)):append_text(" ("..entname..")")
318+
idnode:add_le(f_id32_idx,tvb(off+1,1))
319+
idnode:add_le(f_id32_tag,tvb(off ,1)):append_text(" ("..tagname..")")
320+
321+
off = off+4
322+
323+
-- optional SIGN / TIME
324+
if psign then sub:add_le(f_sign ,tvb(off,4)); off=off+4 end
325+
if ptime then sub:add_le(f_time ,tvb(off,8)); off=off+8 end
326+
327+
-- DATA
328+
if dlen>0 then
329+
sub:add(tvb(off,dlen),"Data ("..dlen.." B)")
330+
off = off + dlen
331+
end
332+
333+
-- 32-bit padding
334+
if off %4 ~=0 then off = off + (4 - (off%4)) end
335+
return off
336+
end
337+
338+
-- dissector (frame) / registration
339+
function p_rop.dissector(tvb,pinfo,tree)
340+
pinfo.cols.protocol = "embObj-ROP"
341+
local len = tvb:len()
342+
if len < HEADER_LEN+4 then return end
343+
if tvb(0,4):le_uint() ~= START_MAGIC then return end
344+
345+
local roptree = tree:add(p_rop, tvb(),"embObj ROP Frame")
346+
roptree:add_le(f_start,tvb(0,4))
347+
roptree:add_le(f_size ,tvb(4,2))
348+
roptree:add_le(f_nrops,tvb(6,2))
349+
roptree:add_le(f_age ,tvb( 8,8))
350+
roptree:add_le(f_seq ,tvb(16,8))
351+
352+
local off = HEADER_LEN
353+
for i=1,tvb(6,2):le_uint() do
354+
off = dissect_single_rop(tvb,off,roptree,i)
355+
end
356+
357+
roptree:add_le(f_end,tvb(off,4))
358+
end
359+
360+
local udp_table = DissectorTable.get("udp.port")
361+
udp_table:add(12345,p_rop)

0 commit comments

Comments
 (0)