Electronics Design AU
Bluetooth & BLE

How Do You Debug BLE with a Packet Sniffer?

Last updated 2 July 2026 · 7 min read

Direct Answer

The standard free tool for over-the-air BLE debugging is Nordic's nRF Sniffer for Bluetooth LE — an nRF52 dongle or dev kit running Nordic's sniffer firmware, feeding captured packets into a Wireshark plugin that decodes advertising, connection establishment, and GATT/ATT traffic. When you control the connecting phone or app instead, Android's built-in Bluetooth HCI snoop log (`btsnoop_hci.log`, enabled via Developer Options) gives a software-only capture of the same protocol layers without extra hardware, though it captures at the host side rather than over the air. Reach for a sniffer when you need to see what's actually happening on the radio or on a device you don't control firmware for — for debugging your own firmware's internal state, vendor SDK debug logs (nRF Connect app, Zephyr/nRF Connect SDK BLE traces) are usually faster.

Detailed Explanation

Reading BLE traffic off the air is the debugging step that answers questions vendor SDK logs can't: what a different device actually transmitted, whether advertising intervals and channel use match what firmware requested, and the exact sequence of PDUs during a connection or pairing failure. This page assumes familiarity with BLE's basic architecture — see What Is Bluetooth Low Energy? for GAP/GATT fundamentals if you need that background first.

nRF Sniffer for Bluetooth LE + Wireshark

Nordic's nRF Sniffer for Bluetooth LE is the standard free over-the-air capture tool. It runs on an nRF52-series dongle or development kit (nRF52840 Dongle, nRF52-DK) flashed with Nordic's sniffer firmware, and streams captured packets to a Wireshark plugin (nRF Sniffer for Bluetooth LE Python extcap) that decodes them using Wireshark's built-in Bluetooth LE dissector. Once running, the sniffer's own UI (a Wireshark-integrated device list) shows nearby advertisers; selecting one and following its connection captures the full exchange from CONNECT_IND onward.

In the resulting capture, Wireshark decodes each layer independently — Link Layer PDU type, L2CAP fragmentation, ATT opcode (Read Request, Write Command, Handle Value Notification, and so on), and SMP pairing PDUs when a pairing procedure is captured. This layered decode is what makes a capture more useful than a vendor log for cross-device issues: you can see a Write Request leave one device and confirm whether the peer actually sent back the expected response, rather than trusting either side's internal account of what happened.

The Channel-Hopping Problem

BLE advertising uses three fixed channels (37, 38, 39 — chosen for minimal overlap with typical Wi-Fi channel placement), which is why passively monitoring advertising traffic is straightforward for any sniffer. Once a connection forms, the Link Layer switches to hopping across the full set of data channels according to a hop increment and channel map negotiated in the CONNECT_IND PDU that establishes the connection. A sniffer has to extract those hopping parameters from the CONNECT_IND it captures and then actively track the same hop sequence in real time to keep receiving packets — this is why a capture that misses the initial CONNECT_IND (sniffer started late, or joining an already-established connection) goes silent after advertising: it has no way to resynchronise to a hop pattern it never learned. If a capture stops seeing data after connection, restart it and power-cycle the peripheral so a fresh CONNECT_IND is generated for the sniffer to lock onto — don't assume the link itself failed.

Android's Built-In HCI Snoop Log

When you control the connecting phone or tablet — the common case when debugging a companion app rather than a third-party device — Android's Bluetooth HCI snoop log is a software-only alternative that needs no sniffer hardware. Enable it via Settings → Developer Options → Enable Bluetooth HCI snoop log, reproduce the issue, then pull btsnoop_hci.log from the device (via adb bugreport or the file directly, depending on Android version) and open it in Wireshark, which decodes it with the same Bluetooth LE dissector used for a live capture.

This is a host-side capture — it logs the HCI command/event stream between Android's Bluetooth stack and the phone's Bluetooth controller, not the literal over-the-air radio signal. Two practical consequences follow: it cannot show genuine radio-layer anomalies (a channel actually not being used as configured, real-world timing jitter), but it does show ATT/GATT traffic already decrypted, since the Android stack logs it after its own encryption/decryption step — no LTK extraction required, unlike a hardware sniffer capturing an encrypted connection from the air.

When to Sniff vs. Read SDK Logs

A packet sniffer is not the first tool to reach for. If the question is "what is my own firmware doing," the fastest path is almost always the vendor SDK's own tooling — the nRF Connect app for interactive GATT browsing and manual read/write/subscribe testing, or Zephyr/nRF Connect SDK's built-in BLE logging (CONFIG_BT_DEBUG_LOG and similar) for a firmware-side trace of what the stack believes happened. These tools are faster to set up and directly show your code's internal state.

Reach for a sniffer specifically when the question can't be answered from one side alone: verifying the actual advertising interval and channel behaviour rather than the configured value, diagnosing an interoperability issue between your device and someone else's (a phone app you don't control, a third-party gateway), or confirming the precise sequence of PDUs during a pairing or connection failure where each side's logs individually look correct but the overall exchange still fails. Zeus Design debugs and brings up BLE firmware for embedded products, including interoperability issues that only show up in an over-the-air capture.

Design Considerations

  • Start the capture before powering on or reconnecting the peripheral being debugged. Missing the initial advertising and CONNECT_IND means the sniffer never learns the connection's channel hop parameters and cannot resynchronise mid-connection.
  • Supply the LTK to Wireshark when debugging an encrypted connection from a hardware sniffer capture, or use Android's HCI snoop log instead if you control the phone side — it logs already-decrypted ATT/GATT traffic with no key extraction step.
  • Cross-check a sniffer capture's connection interval and slave latency against what firmware requested — see BLE connection parameters and power optimisation for what those values should look like; a captured connection interval that doesn't match firmware's request usually means the central device negotiated it down, which a capture is the most reliable way to confirm.
  • A pairing failure visible in an SMP-layer capture (see BLE pairing and security for the pairing procedure) shows exactly which PDU in the exchange failed or was rejected — far more diagnostic than either side's "pairing failed" error code alone.
  • A GATT/ATT-layer capture directly diagnoses CCCD-related notification bugs — see the forum discussion on notifications stopping after reconnect for a real case where a capture would show the client never re-writing the CCCD, versus the peripheral failing to send.

Common Mistakes

  • Starting the sniffer capture after the connection is already established, missing the CONNECT_IND and its channel hop parameters — the capture will show advertising (if any) but go silent once data channel hopping begins.
  • Assuming an encrypted hardware sniffer capture is unreadable and giving up, rather than extracting the LTK from your own device's bonding storage and supplying it to Wireshark's BLE decryption settings.
  • Reaching for a sniffer to debug firmware-internal logic (a state machine bug, an incorrect characteristic value being written) when SDK-level debug logging or a debugger breakpoint would answer the question faster and more directly.
  • Not distinguishing a host-side capture (Android HCI snoop) from an over-the-air capture when interpreting results — a timing anomaly that doesn't appear in an HCI snoop log doesn't rule out a real radio-layer timing problem, since the snoop log is logged above the radio.
  • Debugging a third-party device's behaviour without a hardware sniffer, when the interoperability question specifically requires seeing what that device transmitted — an HCI snoop log on your own phone only shows the far end from your stack's perspective, not the third-party device's actual over-the-air behaviour.

Frequently Asked Questions

Why does my sniffer capture show the advertising packets fine but goes silent after the connection forms?
This is the channel-hopping problem, and it's the single most common confusion for anyone sniffing BLE for the first time. Advertising happens on three fixed channels (37, 38, 39), which any sniffer can passively monitor without extra effort. Once a connection forms, the Link Layer moves to data channel hopping across up to 37 channels, using a hop increment and channel map both established in the CONNECT_IND PDU. A sniffer has to extract those parameters from the CONNECT_IND it just captured and actively follow the same hop sequence in real time — it isn't optional, and if the sniffer misses the CONNECT_IND (started capturing too late, or the connection was already established), it cannot resynchronise to an in-progress connection's hop pattern. Restart the capture and power-cycle the peripheral so a fresh CONNECT_IND is generated for the sniffer to lock onto.
Should I use a hardware sniffer or Android's HCI snoop log?
It depends on what side of the link you control and what you're trying to see. Use the nRF Sniffer (or another over-the-air hardware sniffer) when you need to see the actual radio traffic — including traffic between two devices neither of which is yours, or to verify real advertising intervals and channel behaviour rather than what firmware believes it configured. Use Android's btsnoop_hci.log when you control the phone or app side of the connection and don't want to buy or set up sniffer hardware — it captures the full HCI command/event stream between the Android Bluetooth stack and the controller, including data that's already been decrypted by the time it's logged, which an over-the-air sniffer cannot show you without also having the LTK. The trade-off is that it's a host-side capture — it shows what Android's Bluetooth stack did, not necessarily what physically went over the air, so radio-layer timing anomalies won't show up in it.
Do I need the encryption key to read a sniffed capture, or does Wireshark handle it automatically?
If the connection uses BLE encryption (established during pairing), a hardware sniffer capturing the raw radio traffic sees only encrypted ATT/SMP payloads unless you also provide Wireshark with the Long Term Key (LTK) for that specific bonded pair — Wireshark's BLE dissector supports LTK-based decryption, but you have to extract and supply the key yourself (from your own firmware's bonding storage, since you presumably control at least one side of the link during bring-up). This is one reason Android's HCI snoop log is often more convenient for encrypted-connection debugging: because it captures above the radio's encryption layer, the logged ATT/GATT traffic is already in plaintext, with no key extraction step required.

References

Related Questions

Related Forum Discussions