MeshCore Wireshark Dissector
March 15, 2026 ยท View on GitHub
A Wireshark protocol dissector and pcapng converter for MeshCore, a LoRa mesh networking protocol.

meshcore_dissector.lua-- Wireshark Lua dissector that decodes MeshCore packets with full protocol tree, display filters, and info column summaries (download)meshcore_json2pcap.py-- Python script that converts JSON radio logs to pcapng files (download)
Features
- Decodes all MeshCore packet types: ADVERT, ACK, GRP_TXT, GRP_DATA, TXT_MSG, REQ, RESPONSE, ANON_REQ, PATH, TRACE, MULTIPART, CONTROL, RAW_CUSTOM
- Header bitmask dissection (route type, payload type, payload version)
- ADVERT appdata parsing: node type, GPS coordinates, node name
- CONTROL sub-type parsing: DISCOVER_REQ, DISCOVER_RESP
- Two-layer dissection (like 802.11 Radiotap): radio metadata and wire protocol are separate protocol entries
- Radio metadata (SNR) from the JSON-to-pcapng converter, filterable via
meshcore_radio.snr_raw - Display filter support (e.g.
meshcore.payload_type == 4for ADVERTs)
Installation
The dissector is a single Lua file -- no compilation required. Copy meshcore_dissector.lua to your Wireshark personal plugins folder and it auto-loads on startup.
Linux
mkdir -p ~/.local/lib/wireshark/plugins
cp meshcore_dissector.lua ~/.local/lib/wireshark/plugins/
macOS
mkdir -p ~/.local/lib/wireshark/plugins
cp meshcore_dissector.lua ~/.local/lib/wireshark/plugins/
Alternatively, if you installed Wireshark as an app bundle:
mkdir -p "$HOME/.config/wireshark/plugins"
cp meshcore_dissector.lua "$HOME/.config/wireshark/plugins/"
Windows
Copy meshcore_dissector.lua to:
%APPDATA%\Wireshark\plugins\
Typically this is C:\Users\<YourName>\AppData\Roaming\Wireshark\plugins\. Create the plugins folder if it doesn't exist.
Verifying the plugin directory
In Wireshark, go to Help > About Wireshark > Folders to see the "Personal Plugins" path for your system. You can also find it via Edit > Preferences > Appearance > Folders.
Reloading
After copying the file, restart Wireshark or press Ctrl+Shift+L (Analyze > Reload Lua Plugins) to load the dissector without restarting.
Getting a capture
From the companion app
In the official MeshCore Companion app, go to Tools > Rx Log > Save Log to export a JSON radio log from your node. This JSON file can be converted to pcapng for analysis in Wireshark.
Sample capture
A sample pcapng capture is included at docs/samples/sample_1.pcapng so you can try the dissector without needing a radio.
Converting JSON logs to pcapng
The converter requires Python 3.6+ with no external dependencies.
python3 meshcore_json2pcap.py capture.json
This writes capture.pcapng in the same directory. You can also specify an output path:
python3 meshcore_json2pcap.py capture.json output.pcapng
Input format
The JSON input is an array of packet objects:
[
{
"timestamp": 1773364730762,
"snr": 8.25,
"packet": "150220ee81fbaf4d..."
}
]
| Field | Type | Description |
|---|---|---|
timestamp | integer | Milliseconds since Unix epoch |
snr | float | Signal-to-noise ratio in dB |
packet | string | Hex-encoded raw packet bytes |
Radio metadata header
By default, a radio metadata header is prepended to each packet in the pcapng.
The format follows the same extensibility pattern as 802.11 Radiotap: a fixed
version/length prefix followed by fields. The length field tells the dissector
how many bytes to skip, so older dissectors can still find the start of the wire
protocol even if new fields are added in a future version.
| Bytes | Type | Description |
|---|---|---|
| 0 | uint8 | Version (currently 0) |
| 1 | uint8 | Pad (0) |
| 2-3 | uint16 LE | Header length in bytes (currently 6) |
| 4-5 | int16 LE | SNR * 100 (e.g. 8.25 dB = 825) |
The dissector uses a two-layer architecture (like 802.11 Radiotap + 802.11) where the radio metadata and the MeshCore wire protocol appear as separate protocol entries in Wireshark's packet tree. This makes it clear which bytes are capture metadata vs. actual over-the-air data.
Link-layer type (LINKTYPE)
Pcapng files produced by meshcore_json2pcap.py use DLT_USER0 (147), a
link-layer type from the "user-defined" range reserved for private use.
This value is temporary and will change. DLT_USER0-USER15 is a 16-slot global namespace shared by every custom protocol tool. If two unrelated dissector plugins claim the same slot, the last one loaded wins silently -- there is no conflict detection. We plan to register an official LINKTYPE with tcpdump.org to eliminate this risk. When that happens, the assigned value will replace DLT_USER0 in both the converter and the dissector. Existing pcapng files will need to be regenerated from the original JSON logs.
Usage in Wireshark
Open a converted pcapng file. Packets are automatically decoded as MeshCore. The dissector provides protocol tree dissection and display filters. Wireshark's built-in I/O graphs can visualize mesh traffic over time:

Display filters
| Filter | Description |
|---|---|
meshcore.payload_type == 4 | ADVERT packets |
meshcore.payload_type == 5 | GRP_TXT packets |
meshcore.route_type == 1 | FLOOD routed packets |
meshcore.advert.name contains "Repeater" | ADVERTs with "Repeater" in node name |
meshcore.channel_hash == 0x81 | Group messages on a specific channel |
meshcore_radio.snr_raw > 0 | Packets with positive SNR |
Command-line with tshark
tshark -X lua_script:meshcore_dissector.lua -r capture.pcapng
tshark -X lua_script:meshcore_dissector.lua -r capture.pcapng -Y "meshcore.payload_type == 4" -V
Running tests
Tests require Python 3.6+ and pytest. Dissector integration tests additionally require tshark (they are skipped if tshark is not available).
pip install pytest
python3 -m pytest tests/ -v
License
GPLv2 -- see LICENSE for the full text. This license is compatible with the main Wireshark project.