The BLE Debugging Commands Your SDK Doesn't Tell You About

Your nRF Logger shows “advertising started.” Your code compiles cleanly. Your advertising interval is set to 100ms, TX power to 0 dBm, exactly what you specified. But your phone sees nothing. You move closer. Still nothing. You restart the device, restart the phone, question your career choices. The SDK insists everything is fine, yet the RF silence suggests otherwise.
The problem isn’t your code. It’s that you’re debugging at the wrong layer.
Between your application code and the radio hardware sits the Host Controller Interface (HCI), a standardized protocol that lets the Bluetooth host communicate with the controller. Your SDK wraps this layer in convenient abstractions, which is great for building products quickly but terrible for understanding why things break. A handful of HCI commands can give you direct visibility into what the controller is actually doing, bypassing the SDK’s helpful opacity. These commands have saved me countless hours of debugging, and they’ll do the same for you.
Why SDK Abstractions Hide What You Need to Know
Nordic’s SDKs, whether the legacy nRF5 SDK or the newer nRF Connect SDK built on Zephyr, are engineered for portability and developer productivity. They handle edge cases, enforce Bluetooth specification compliance, and shield you from hardware complexity. This is genuinely valuable, right up until you need to debug something.
Here’s what the SDK won’t tell you:
You request a 20ms advertising interval, but the Bluetooth specification mandates a minimum of 20ms for connectable advertising and 100ms for non-connectable scannable advertising. The SDK silently clamps your value. You set TX power to +8 dBm, but regulatory compliance tables for your region limit it to +4 dBm. The SDK adjusts without notification. You expect RSSI data during advertising, but the SDK only exposes signal strength during active connections.
None of these behaviors are bugs. They’re features, protecting you from spec violations and regulatory issues. But when you’re trying to figure out why your device isn’t discoverable at the expected range, or why your power consumption exceeds your calculations by 40%, these silent adjustments become invisible obstacles.
HCI commands let you query the controller directly for ground truth. No abstractions, no helpful modifications, just what’s actually configured.
The Five Commands That Solve Most Debugging Problems
You don’t need to memorize the entire HCI specification. These five commands cover roughly 80% of the debugging scenarios you’ll encounter with advertising and power issues.
Read RSSI (0x1405)
This command returns the signal strength for a specific connection handle, measured in dBm. Values typically range from -90 dBm (weak) to -20 dBm (strong, very close proximity).
Use this when you’re diagnosing range issues, validating antenna placement on a new board design, or investigating why connections drop at distances that should work fine. The command requires an active connection handle. It won’t work during advertising, which catches people off guard.
In the nRF5 SDK, you don’t send raw HCI. Instead, use sd_ble_gap_rssi_start() to begin continuous RSSI reporting for a connection, then handle BLE_GAP_EVT_RSSI_CHANGED events. In Zephyr, enable RSSI reporting through the connection parameters.
The critical gotcha: RSSI readings are inherently noisy. A single reading means almost nothing. Sample continuously and look at the trend, not individual values. If you’re seeing -85 dBm at one meter, you have an antenna problem, not a software problem.
LE Read Advertising Channel TX Power (0x2007)
This command returns the actual transmit power the controller is using for advertisements, not what you requested, but what’s actually happening.
Send this command when your power measurements don’t match your calculations, or when you’re verifying regulatory compliance before certification testing. Your call to sd_ble_gap_tx_power_set() is a request, not a guarantee. The controller may honor it, clamp it, or ignore it entirely depending on regulatory tables and hardware capabilities.
The response is a single signed byte representing power in dBm. If you requested +4 dBm but the response shows 0 dBm, you’ve found your discrepancy. This five-second check has resolved “mysterious” power budget overruns more times than I can count.
LE Set Advertising Parameters Verification (0x2006)
Here’s a debugging technique that isn’t in any manual: re-issue your advertising parameters and examine what the controller actually accepts.
The LE_Set_Advertising_Parameters command (0x2006) configures interval min/max, advertising type, own address type, channel map, and filter policy. When you send this command, the controller either accepts the parameters or returns an error. By sending it with your intended values and checking the response, you can verify what’s actually configured.
Key parameters to check:
| Parameter | Common Issue |
|---|---|
| Advertising Interval Min/Max | Clamped to spec-compliant minimum (20ms connectable, 100ms non-connectable scannable) |
| Channel Map | Restricted channels you didn’t request (often due to regulatory or coexistence settings) |
| Advertising Type | Changed based on data length or feature availability |
If you set a channel map of 0x07 (all three advertising channels) but the controller is only using 0x05, you’ve just found why your device is less discoverable than expected. It’s only advertising on two channels instead of three.
LE Transmitter Test (0x201E)
This command puts the controller into continuous transmit mode on a specific channel at a specific power level. It’s essential for antenna tuning, EMC pre-compliance testing, and validating RF output with a spectrum analyzer.
You specify a channel (0-39), PHY type, and payload pattern. The controller then transmits continuously until you issue LE_Test_End (0x201F).
Critical warning: This command disables normal BLE operation entirely. The stack stops. Advertisements stop. Connections drop. Use this only in a dedicated test harness or manufacturing fixture, never in production firmware. Nordic’s Direct Test Mode (DTM) sample application provides a safe wrapper around these commands.
When to use it: You’re bringing up a new board and need to verify the antenna is actually radiating. You hook up a spectrum analyzer, issue the transmitter test command, and look for a signal at the expected frequency and power. If you see nothing, your problem is hardware, not firmware.
Read Transmit Power Level (0x2B77)
While LE_Read_Advertising_Channel_TX_Power tells you advertising power, this command reports transmit power for active connections. It returns both the current power level and the maximum power level the controller can use on that connection.
This distinction matters because BLE supports power control during connections. The controller may reduce TX power to save energy when the link quality is good, or increase it when RSSI drops. If you’re seeing unexpected battery drain during connections, this command tells you whether the controller is running at maximum power (possibly due to poor link quality) or operating efficiently.
The current vs. maximum comparison is particularly useful: if current equals maximum, the controller is working as hard as it can. That’s either normal (long range application) or a sign that something is wrong with your RF path.
Debugging an Advertising Problem: A Practical Workflow
Let’s walk through a real scenario. Your device should advertise at 100ms intervals, 0 dBm power, on all three advertising channels. But the phone only sees it intermittently, and sometimes not at all.
Step 1: Verify actual TX power. Issue LE_Read_Advertising_Channel_TX_Power. If the response shows -12 dBm instead of 0 dBm, you’ve found a 12 dB reduction in output power. That’s a 16x reduction in radiated power, which dramatically affects range. Check your TX power configuration path.
Step 2: Verify advertising parameters. Re-issue your advertising parameters and confirm they’re accepted without modification. Check especially the channel map. If you’re only advertising on channel 37 instead of all three, your phone has fewer chances to hear you.
Step 3: Validate RF output directly. Put the device in transmitter test mode, connect a spectrum analyzer, and verify you see signal at the expected frequency (2402 MHz for channel 37, 2426 MHz for channel 38, 2480 MHz for channel 39) at the expected power level. If the RF looks correct here but advertising doesn’t work, the problem is protocol-level, not RF-level.
Step 4: Consider the phone. If all three steps check out, the problem might be phone-side. iOS aggressively filters duplicate advertisements in background mode and may not report devices it’s already seen.
Accessing HCI Commands in Nordic SDKs
In the legacy nRF5 SDK, you typically don’t send raw HCI commands. The SoftDevice exposes higher-level APIs that map to HCI internally: sd_ble_gap_rssi_start(), sd_ble_gap_tx_power_set(), and so on. For commands that aren’t exposed, you’re limited.
In the nRF Connect SDK with Zephyr, you have more direct access. The bt_hci_cmd_send_sync() function lets you construct and send raw HCI commands. You’ll build the command buffer manually, specifying the opcode and parameters per the Bluetooth specification, then parse the response event.
Some commands remain unavailable when the SoftDevice or Zephyr Bluetooth stack controls the controller. The stack doesn’t expose everything to avoid conflicts with its internal state management. DTM mode is a notable exception, designed specifically for raw RF testing.
Building HCI Debugging Into Your Workflow
SDK abstractions are the right tool for building products. HCI access is the right tool for debugging them. These aren’t competing philosophies; they’re complementary layers that serve different purposes.
The five commands covered here address the vast majority of advertising and power debugging scenarios. They’re not arcane incantations reserved for Bluetooth stack developers. They’re practical tools that reveal what your hardware is actually doing.
Try this today: on whatever BLE project you’re currently working on, read the advertising channel TX power and compare it to what you configured. If they match, you’ve confirmed your setup. If they don’t, you’ve just learned something important about your device that the SDK never would have told you.
Hubble Network connects directly to satellites from standard BLE chips—the same ones you’re debugging now. See how it works →