Here's the video walkthrough, if you'd rather watch it:

Why I Started Using This Tool

The UDPTx task prints a line every time sendto() succeeds, but that only means the kernel accepted the packet. It doesn't prove the packet left the board, reached the PC, or has the bytes I think it has. The log line even shows the header as 0xEFDE0000, which looks wrong. Before writing any PC-side code, I wanted to see the raw packet with a tool that doesn't know or care about my code. That tool is Wireshark.

What It Does

Wireshark captures every packet that reaches a network interface on the PC and shows it three ways: a list of packets, a decoded tree of each protocol layer, and the raw bytes in hex. For this demo, the board runs the program from the last post, and the PC sits on the same subnet with Wireshark capturing. No new C code, just checking the frame from the outside.

Capture and filter

Start a capture on the PC interface that's on the same network as the DE10-Nano (usually the wired Ethernet adapter). A busy network shows a lot of traffic, so type this in the display filter bar:

udp.port == 5001

Now the list only shows the board's frames. One new row appears every UDPTX_INTERVAL_SEC seconds, and the Time column confirms the spacing. If you stop the program on the board, the rows stop. If Wireshark ever labels port 5001 as some other protocol, right-click a packet, choose Decode As, and set it to show the payload as plain data.

The packet list: who sent it and where it went

  • Source is the board's IP address. That's a quick check that the frames come from the DE10-Nano and not something else on the network.
  • Destination is the subnet's broadcast address from UDPTX_BCAST_ADDR, and the Ethernet destination is ff:ff:ff:ff:ff:ff. That's why the PC receives it without the board knowing the PC's address.
  • Protocol is UDP.

The details pane: the UDP header

Expand the User Datagram Protocol layer. The destination port is 5001, matching FRAME_UDP_PORT, which confirms htons() did its job. The source port is some high number picked by the kernel, because the UDPTx socket never calls bind(). That's fine for a sender. The UDP length is 24: 8 bytes of UDP header plus the 16-byte frame. If this number is anything other than 24, the struct isn't the size I think it is.

The hex pane: reading the frame byte by byte

Click the Data line under UDP and Wireshark highlights the 16 bytes of payload:

00 00 de ef  00 00 00 01  00 00 00 00  00 00 00 00
└─ header β”€β”˜ β”” ctrl_code β”˜ β””control_regβ”˜ β””status_regβ”˜
  • 00 00 de ef is header. Read left to right, it's 0x0000DEEF, exactly FRAME_HEADER. The highest byte comes first, which is big-endian network order, so htonl() worked.
  • 00 00 00 01 is ctrl_code, FRAME_CTRL_READ, the board reporting to the PC.
  • 00 00 00 00 twice: control_reg and status_reg. Nothing calls Frame_set yet, so they're still the zeros from the initializer in frame.c.

This is also where the log line from the last post makes sense. On the wire the header is 00 00 de ef, which is correct. The board's log printed 0xEFDE0000 because the little-endian CPU read those same four bytes back as a number after htonl() had already swapped them. The packet was right, the log was wrong. Without Wireshark I might have "fixed" the packet and broken it.

The whole Ethernet frame may show as 60 bytes with a couple of extra zero bytes after the payload. That's Ethernet padding a small frame up to its minimum size, not part of my data. The UDP length of 24 is what tells you where the frame really ends.

When nothing shows up

  • Wrong interface. Make sure Wireshark is capturing on the adapter connected to the board's network, not Wi-Fi or a virtual adapter.
  • Different subnet. A broadcast only reaches machines on the same subnet as UDPTX_BCAST_ADDR. Check that the PC and the board have addresses in the same range.
  • The board is logging errors. If the terminal shows sendto failed: Permission denied, SO_BROADCAST isn't set. Any other error there means the packet never left the board, so there's nothing for Wireshark to see.
  • Firewall. Wireshark usually sees packets before the PC's firewall filters them, but a PC-side program listening on port 5001 won't get them if the firewall blocks it. Keep that in mind for the receiving side.

Final Verdict

Wireshark turned "I think it works" into "I can see every byte": the right source, the broadcast destination, port 5001, a 24-byte UDP payload length, and a frame that starts with 00 00 de ef 00 00 00 01 at a steady interval. It also settled the confusing log line in one look. That's the send side of the network done. Next is the other direction: a UDPRx task on the board that listens for FRAME_CTRL_WRITE frames from the PC and updates the shared frame with Frame_set.