LiteJam Bluetooth LE Protocol Capture and Analysis

Getting started: tooling, capture methods, and interpretation of iPhone-to-guitar traffic

Project: LiteJam guitar client software and firmware
Document date: September 1, 2026
Analysis host: Windows 11, C:\Users\david\litejam
Link under study: Bluetooth LE, iOS companion app to LiteJam guitar

Executive summary

The LiteJam guitar is a Bluetooth LE peripheral controlled by an iOS application. The objective is to characterize its GATT interface through ordinary observation of communication with the user's own guitar, supplementing the interface already visible in LiteJam's public web clients.

Provenance and scope: This guide concerns standard Bluetooth diagnostics on hardware and accounts owned by the user, together with materials LiteJam made publicly available. It does not require bypassing encryption, authentication, access controls, firmware protection, device security, or any private system.

Two standard diagnostic methods are available, and they see different things. iOS HCI logging is the primary source, because the operating system records the user's own phone-to-guitar traffic at the host-controller interface. The nRF52840 BLE monitor is the corroborating source, providing a view of advertising payloads, connection parameters, link-layer control, and the pairing exchange for the user's own devices.

The controlling qualification is that neither method is passive and free. The RF sniffer can only follow a connection whose setup it witnessed; if it is not already locked onto the guitar when the iPhone connects, the resulting capture is an undecodable encrypted stream. The iOS method has no such race, but it is batch-only: each iteration costs a sysdiagnose and roughly ten minutes of turnaround, and the log contains every radio event on the phone, not just the guitar's.

The practical consequence is that capture order matters. Enumerate the GATT tree from the phone first, with no capture running at all. That map — handles, UUIDs, and properties — is what makes every subsequent packet trace readable, and it costs ten minutes with a free app.

Bluetooth LE is a layered stack, and the two capture tools tap it at different points. Understanding which layer each tool sees is the difference between a productive capture and an unreadable one.

The iPhone is the central: it scans, initiates the connection, and drives the exchange. The LiteJam guitar is the peripheral: it advertises, accepts the connection, and exposes its functionality as a GATT server. All application-level traffic is therefore GATT operations carried over the Attribute Protocol (ATT), which rides on L2CAP, which rides on the link layer.

Layer What it carries nRF52840 RF sniffer iOS HCI log (.pklg)
Physical / link layer Advertising packets, connection events, channel hopping, retransmissions Yes No
Link-layer control Connection parameter updates, PHY changes, encryption start Yes Partially, as HCI events
SMP (pairing) Key exchange, bonding Yes, if the pairing is captured Yes
L2CAP Channel framing, fragmentation Yes, when the monitored session is readable Yes
ATT / GATT Reads, writes, notifications — the actual protocol Only when the monitored session is readable Yes, as logged by the user's own phone

The asymmetry in the last two rows is the whole argument for using both tools. The BLE monitor sees more of the radio stack, while the operating-system HCI log records the user's own phone-to-guitar traffic in the form available to the phone itself.

2. Tooling installed on this machine

The following is installed and verified working as of the document date.

Component Version Location Purpose
Wireshark 4.6.8 C:\Program Files\Wireshark Analysis of both capture types
nRF Sniffer for BLE extcap 4.1.1 %APPDATA%\Wireshark\extcap\ Live RF capture into Wireshark
Sniffer Wireshark profile %APPDATA%\Wireshark\profiles\ Sniffer-specific columns and coloring
nRF Connect for Desktop 5.4.0 %LOCALAPPDATA%\Programs\nrfconnect Host for the Programmer app
nrfutil 8.2.1 %LOCALAPPDATA%\Microsoft\WinGet\Packages\ Device discovery, DFU packaging
Dongle firmware sniffer 4.1.1 .hex tools/nrf_sniffer_ble_4.1.1/hex/ Sniffer image for the nRF52840
Python 3.13.2, pyserial 3.5, psutil 7.2.2 user install Runs the extcap plugin

Two verification results are worth recording, because they were checked rather than assumed. Wireshark's libwiretap.dll contains the Apple PacketLogger reader and libwireshark.dll contains the matching dissector, so iOS .pklg files open natively on this Windows machine — a Mac is required only for live streaming, never for analysis. Separately, an actual extcap capture was run and produced a valid pcapng with no Npcap installed, confirming that the wpcap.dll warning Wireshark prints at startup is benign for this workflow.

Setup and flashing steps live in tools/SNIFFER_SETUP.md. The Programmer app inside nRF Connect for Desktop still needs its one-click install before the dongle can be flashed.

3. Order of operations

Each step below produces the interpretive key for the step after it. Working out of order wastes capture iterations.

  1. Enumerate the GATT tree from the iPhone. No dongle, no capture, ten minutes. Produces the handle-and-UUID map.
  2. Record the user's own session with iOS HCI logging. Produces the command and response bytes as logged by the phone, read against the map from step 1.
  3. Capture RF with the nRF52840. Produces advertising data, connection setup, and link-layer behavior that step 2 cannot show.
  4. Static analysis of the official IPA. Optional, but frequently the fastest route to opcode constants and UUID strings. Not yet set up on this machine.

4. Step 1 — Enumerate the GATT tree

Install nRF Connect for Mobile (free, App Store) on the iPhone. Connect to the LiteJam and let it walk the attribute database. Record what it reports.

What this yields:

Record it in this form, because captures reference handles, not UUIDs:

Handle UUID Properties Working hypothesis
0x000e 6E400002-... write-without-response Command channel, phone to guitar
0x0010 6E400003-... notify Status channel, guitar to phone

The properties alone constrain the protocol before a single packet is captured. A write-without-response characteristic paired with a notify characteristic is a bidirectional pipe, not a set of individually meaningful attributes — the real protocol will be framed inside those payloads. A characteristic that is read-only and small is more likely to be a version, serial number, or battery value.

What this step cannot tell you: what the official app actually writes. Enumeration reveals the shape of the interface, not its use. That requires capture.

5. Step 2 — Operating-system capture via iOS HCI logging

This is the primary diagnostic source. HCI logging is an Apple-provided facility that records the user's own Bluetooth session at the boundary between the phone's host stack and controller.

5.1 Procedure

  1. Install Apple's Bluetooth logging profile on the iPhone from the Apple profiles and logs page. A free Apple ID is sufficient. Install it under Settings, General, VPN & Device Management.
  2. Run the LiteJam session to be captured. Keep it short and deliberate.
  3. Trigger a sysdiagnose: hold Volume Up, Volume Down, and the Side button together for about 1.5 seconds, until the phone vibrates. Wait roughly ten minutes for generation to complete.
  4. Retrieve it under Settings, Privacy & Security, Analytics & Improvements, Analytics Data. Find sysdiagnose_<date>.tar.gz and share it to this PC.
  5. Extract the archive. The bluetooth/ directory contains .pklg files. Open them directly in Wireshark.

5.2 Capture hygiene

The log covers every Bluetooth event on the phone across the whole logging window, so discipline at capture time saves hours at analysis time.

5.3 Limits

The profile expires after a few days and does not survive a reboot, so it must be reinstalled before each capture session; a profile that has silently expired produces an empty bluetooth/ directory, which is the usual explanation for a capture that appears to have failed for no reason. The method is also strictly batch — there is no live view, and a sysdiagnose triggered too long after the event of interest may find the relevant window already rotated out of the log.

6. Step 3 — RF capture with the nRF52840 dongle

Flash the dongle per tools/SNIFFER_SETUP.md, then select the nRF Sniffer for Bluetooth LE COMx interface in Wireshark and switch to the Profile_nRF_Sniffer_Bluetooth_LE profile. Enable the interface toolbar under View, Interface Toolbars.

6.1 The capture-window constraint

The sniffer follows a single device at a time. Select the LiteJam in the toolbar's device list while it is advertising and before the iPhone connects. The sniffer must observe the connection request to learn the access address, CRC init, and hop sequence; without those it cannot follow the connection at all.

The practical sequence is: start the capture, put the guitar into advertising, select it in the toolbar, and only then open the iOS app. If the app connects first, disconnect and start over because the monitor must observe connection setup.

6.2 What this uniquely provides

6.3 The encryption constraint

If the link is encrypted and the pairing was not captured, ATT content is unreadable. Keys can be supplied through the toolbar when known, but in practice this is the reason the iOS HCI log is the primary source for payloads and the RF capture is the source for everything structural around them.

7. Interpreting the capture

7.1 Display filters

Wireshark's BLE dissectors are the working surface. Useful starting filters:

btatt                          all ATT traffic
btatt.opcode == 0x52           write commands (write without response)
btatt.opcode == 0x1b           handle value notifications
btatt.handle == 0x000e         one characteristic only
btatt.value                    presence of a payload
btle.advertising_address       filter RF captures to one device
btcommon.eir_ad.entry.device_name

Add btatt.handle and btatt.value as custom columns. Once those two are visible, a capture reads as a conversation rather than a packet list.

7.2 ATT opcodes worth recognizing

Opcode Operation Significance
0x02 / 0x03 Exchange MTU Request / Response Sets the maximum payload; determines fragmentation behavior
0x0a / 0x0b Read Request / Response Attribute polling
0x12 / 0x13 Write Request / Response Acknowledged command
0x52 Write Command Unacknowledged command — the common case for control pipes
0x1b Handle Value Notification Peripheral-initiated data, the usual status and streaming path
0x1d / 0x1e Indication / Confirmation Acknowledged peripheral-initiated data
0x16 / 0x18 Prepare / Execute Write Long-attribute writes, split across packets

A write of 0x0100 to a CCCD handle is the client subscribing to notifications; 0x0200 subscribes to indications. That write is almost always the last step of connection setup, and it marks the boundary between session establishment and real traffic.

7.3 Recognize the transport before decoding the payload

Two vendor UUIDs are worth checking for immediately, because either one changes the entire interpretation:

7.4 MTU and fragmentation

The default ATT MTU is 23 bytes, leaving 20 bytes of payload. Anything longer is either fragmented by the application across successive writes or sent through the prepare/execute write sequence. Watch the MTU exchange at the start of the connection: if the negotiated MTU is large, single-packet payloads may be far longer than 20 bytes, and a decoder written against a 20-byte assumption will be wrong.

8. From packets to protocol

Capture produces bytes; interpretation produces a specification. The transition is a disciplined differential process.

Vary one thing at a time. Capture the same UI action twice with no change, then once with a single parameter changed. Bytes identical across all three are fixed structure — opcode, length, framing. Bytes that differ between the first two are counters, timestamps, or nonces. Bytes that differ only in the third are the parameter.

Classify each varying field. A field that increments by one per message is a sequence number. A field that changes with every byte of the payload but shows no other pattern is a checksum or CRC. A field that tracks elapsed time is a timestamp. Sequence numbers and checksums must be reproduced correctly by any client, so identifying them early prevents a long stall later.

Separate request-response pairs from streams. A write followed within milliseconds by a notification on a different handle is a command and its reply. A notification arriving at a fixed interval with no preceding write is telemetry. These call for different client architectures, so the distinction should be settled before any client code is written.

Record conclusions as a table, updated as understanding firms up:

Direction Handle Bytes Meaning Confidence
Phone to guitar 0x000e A5 01 03 .. Preset select, index in byte 3 Observed 4 times

Distinguish what was observed from what was inferred. An opcode seen once under one condition is a hypothesis; the same opcode seen across three sessions with a correctly predicted response is a finding.

8.1 Encoding the result as a dissector

Once opcodes are stable, write a Lua dissector and drop it in %APPDATA%\Wireshark\plugins\. Registering it against the vendor characteristic makes every subsequent capture self-documenting, and it converts protocol knowledge into a reusable artifact rather than notes. This is worth doing as soon as three or four commands are confirmed, not at the end.

9. Failure modes and how to recognize them

Symptom Cause Remedy
RF capture shows only encrypted LL data, no ATT Sniffer did not witness connection setup Restart capture; select the device before the app connects
bluetooth/ directory in sysdiagnose is empty Logging profile expired or removed by reboot Reinstall the profile, recapture
Relevant window missing from .pklg Sysdiagnose triggered too late; log rotated Trigger immediately after the action
No sniffer interface in Wireshark Dongle not flashed, or not enumerated as a COM port Reflash via the Programmer app; check Device Manager
Wireshark warns it cannot load wpcap.dll Npcap not installed Benign for this workflow; ignore
Payloads truncate at 20 bytes Default MTU assumption Check the MTU exchange; handle fragmentation

10. What this enables

Once the published and observed interface is characterized, two build paths open. A CoreBluetooth iOS client can be compiled through GitHub Actions macOS runners and installed with Sideloadly, giving an independent app that speaks the same publicly observable interface. Separately, the same nRF52840 dongle can run nRF Connect SDK firmware as an active BLE central, letting this PC drive the user's guitar directly rather than only observe it. That second path requires the nRF Connect SDK toolchain, a substantially larger install deliberately deferred until the interface is known.

Note that a self-built client is a poor discovery tool: it can only log its own traffic, never the official app's. Discovery rests entirely on the capture methods above and on static analysis of the official application.

Conclusion

The decisive asymmetry in this project is that Apple's HCI logging records the user's application-level session while the BLE monitor shows the link-layer structure those messages travel through, and neither substitutes for the other. Treat the HCI log as the interface source and the RF capture as the explanation of how the connection behaves.

The most common way this work stalls is not tooling failure but undisciplined capture. Both methods make each iteration expensive — the RF path through its advertising-window race, the iOS path through its ten-minute batch turnaround — so the value of enumerating the GATT tree first, exercising one behavior per session, and repeating each action for differential comparison is disproportionate to the effort involved.

The remaining constraint is that no capture method reveals intent. Packets show what the official app sends, not why; a byte that never varies across every captured session may be a constant, a protocol version, or a field whose triggering condition was simply never exercised. LiteJam's publicly served web clients provide the appropriate readable cross-check and ultimately supplied the information used by the working controller.