How to Enable the Zephyr BLE Shell for Debugging Your Hubble Firmware

Enabling the Zephyr BLE shell over UART to debug Bluetooth firmware on an embedded device

You’ve got your Hubble firmware building on Zephyr. It flashes cleanly. You open a serial terminal, type bt, and… nothing. Maybe a terse “command not found.” Maybe just silence.

People run bt init and bt advertise on in forum posts. The Zephyr docs show an interactive Bluetooth shell. But your project doesn’t have it, and it’s not obvious why.

Zephyr’s BLE shell exists, it’s powerful, and it’s probably 4 lines of config away from working in your project. Zephyr compiles in only what you ask for, though. If you didn’t ask for the BLE shell, it doesn’t exist in your binary.

This guide gets you from “bt command not found” to running bt init and bt advertise on from your serial console. The whole fix takes about 5 minutes.

What You’ll Need Before Starting

  • A working Zephyr dev environment (west build runs without errors)
  • A Hubble firmware project that builds and flashes. If you’re starting fresh, the Hubble Zephyr reference application is a good baseline.
  • A BLE-capable board (nRF52840-based boards are the most common)
  • A serial terminal (minicom, PuTTY, screen, or similar) connected to your board’s UART at 115200 baud

One quick check: if you don’t see any shell prompt over UART yet, you’ll need CONFIG_SHELL=y and CONFIG_UART_CONSOLE=y in your config first. The steps below assume you at least have a uart:~$ prompt.

Why You’re Seeing “bt command not found”

Zephyr’s build system is driven by Kconfig, the same configuration system the Linux kernel uses. Every feature, subsystem, and driver is a config symbol that defaults to off. If you don’t flip the switch, the code doesn’t get compiled into your binary.

The bt command group comes from CONFIG_BT_SHELL. But it doesn’t work alone; it depends on two other symbols being set first:

CONFIG_SHELL=y
    └── CONFIG_BT=y
          └── CONFIG_BT_SHELL=y   ← registers the "bt" command

If any link in that chain is missing, the bt command doesn’t exist at runtime. There’s no helpful error telling you why. It just isn’t there.

Zephyr’s own samples/bluetooth/shell sample has all of these set in its prj.conf, which is why it works out of the box. Your custom Hubble project almost certainly doesn’t inherit those settings.

Step-by-Step: Enabling the BLE Shell

This is purely a configuration change. You won’t touch any C code.

Step 1: Edit Your prj.conf

Open your project’s prj.conf and add these lines:

# Enable the shell subsystem (may already be set)
CONFIG_SHELL=y

# Enable the Bluetooth stack
CONFIG_BT=y

# Set a device name (shows up in advertising)
CONFIG_BT_DEVICE_NAME="HubbleDev"

# Enable roles you need — pick one or both
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_CENTRAL=y

# Enable the BLE shell command group
CONFIG_BT_SHELL=y

A few of these might already be in your config (especially CONFIG_BT=y if you’ve got a Hubble project). Don’t duplicate them; just make sure they’re present.

Two optional but useful additions:

# Enables "bt scan on" — needed for scanning nearby devices
CONFIG_BT_OBSERVER=y

# Enables "bt advertise on" — needed for broadcasting
CONFIG_BT_BROADCASTER=y

On the dependency side: CONFIG_BT_PERIPHERAL=y automatically enables CONFIG_BT_BROADCASTER, and CONFIG_BT_CENTRAL pulls in CONFIG_BT_OBSERVER. You can list them explicitly without any harm, and it makes your config easier to read.

Step 2: Verify the UART Shell Backend

The shell needs a transport to talk to you. UART is the default, and it’s almost always what you want. Confirm this line exists (or add it):

CONFIG_SHELL_BACKEND_SERIAL=y

On most boards this is already the default, but I’ve seen projects where it got turned off by a board-level defconfig. Worth checking.

Step 3: Pristine Rebuild and Flash

This is the step people skip, and then spend 20 minutes confused. Kconfig changes require a pristine build. Zephyr’s CMake cache can hold onto stale config values if you do an incremental build.

west build -p -b <your_board>
west flash

The -p flag tells west to wipe the build directory and start fresh. Don’t skip it.

If you’re targeting an nRF52840 DK, that looks like:

west build -p -b nrf52840dk_nrf52840
west flash

Step 4: Connect and Verify

Open your serial terminal at 115200 baud. You should see the uart:~$ prompt. Type help and press Enter.

uart:~$ help
Available commands:
  bt         : Bluetooth shell commands
  clear      : Clear screen
  device     : Device commands
  help       : Display help
  history    : Command history
  kernel     : Kernel commands
  shell      : Useful shell commands
  ...

If bt shows up in that list, the hard part is done.

┌──────────────────────────────────────────────────┐
│  TROUBLESHOOTING                                 │
│                                                  │
│  Still no "bt" command?                          │
│  1. Confirm you used a pristine build (-p flag)  │
│  2. Check build log for "BT_SHELL" warnings      │
│  3. Run: west build -t menuconfig                │
│     Search for BT_SHELL — verify it shows [*]    │
│  4. Ensure your board's devicetree has a BLE     │
│     controller node enabled                      │
│                                                  │
└──────────────────────────────────────────────────┘

The menuconfig trick (step 3 above) is especially useful. It shows the resolved state of every Kconfig symbol, including dependencies. If BT_SHELL shows as [ ] with unmet dependencies, menuconfig will tell you exactly which ones.

Running Your First BLE Shell Commands

Two commands to confirm everything works.

bt init

This initializes the BLE stack at runtime:

uart:~$ bt init
Bluetooth initialized

If you see “Bluetooth initialized,” the HCI transport to your radio is working and the stack is live.

A failure here (something like “HCI driver not found” or a hang) usually means the board’s BLE controller isn’t properly defined in the devicetree, or there’s a hardware issue. That’s a separate problem from the shell config.

bt advertise on

This starts connectable advertising using the device name you set in CONFIG_BT_DEVICE_NAME:

uart:~$ bt advertise on
Advertising started

Grab your phone, open nRF Connect (free from Nordic Semiconductor, available on iOS and Android), and scan. You should see “HubbleDev” (or whatever name you chose) broadcasting.

To stop advertising:

uart:~$ bt advertise off
Advertising stopped

Here’s a quick reference for other commands you’ll probably want:

CommandWhat it does
bt initInitialize the BLE stack
bt advertise onStart connectable advertising
bt scan onStart scanning for nearby devices
bt scan offStop scanning
bt disconnectDrop the current connection
bt infoShow local device info and address

All of these are subcommands, so bt help will list everything available. The set of commands you see depends on which CONFIG_BT_* options are enabled.

Flash, RAM, and Why You Shouldn’t Ship This

The BLE shell isn’t free. Enabling CONFIG_BT_SHELL along with its dependencies adds roughly 10 to 30 KB of flash, depending on how many subcommands get pulled in. RAM overhead is smaller but still meaningful on constrained devices.

The shell is also an open command interface over UART. Anyone with physical access to the serial port can poke at your BLE stack. Fine on a dev bench; a security hole in a production device.

The cleanest approach: keep a separate config file for debug builds. Create a debug_shell.conf next to your prj.conf:

# debug_shell.conf — BLE shell overlay for development only
CONFIG_SHELL=y
CONFIG_SHELL_BACKEND_SERIAL=y
CONFIG_BT_SHELL=y

Then build with it as an overlay:

west build -p -b nrf52840dk_nrf52840 -- -DOVERLAY_CONFIG=debug_shell.conf

Your project structure looks something like this:

hubble-firmware/
├── prj.conf                 # Base config (no shell)
├── debug_shell.conf         # Adds shell + BT_SHELL
├── boards/
│   └── nrf52840dk_nrf52840.overlay
└── src/
    └── main.c

Production builds use prj.conf alone. Debug builds layer on the overlay. No risk of accidentally shipping the shell, and your base config stays clean. You can read more about structuring firmware integration in the Hubble device integration guide.

Using the Shell Beyond Basic Debugging

The fix for “bt command not found” is almost always the same: add CONFIG_BT_SHELL=y (and its dependencies) to your prj.conf, do a pristine rebuild, and you’re set.

Once the shell is running, it becomes one of the most useful tools in your Zephyr workflow:

  • Inspect GATT services with bt gatt-show
  • Test connection parameters
  • Debug pairing
  • Examine the advertising data format

No throwaway test code required. If you’re working with Hubble’s BLE advertising packet structure, the advertising packet documentation pairs well with what you can observe through shell commands.


Hubble Network lets you connect BLE devices directly to satellite networks—no gateways, no extra infrastructure. See how it works →