BLE Inspector Documentation

Installation

MacTools BLE Inspector is a native macOS app built with Swift and Tauri on top of CoreBluetooth. It runs natively on Apple Silicon and Intel Macs — no Rosetta, no Parallels, no Android Studio, no nRF Connect install failures. One-time purchase, no subscription.

Download

System Requirements

RequirementMinimum
macOS12 Monterey or newer (Apple Silicon & Intel)
BluetoothBuilt-in Bluetooth 4.0+ (all Macs since 2012). External BT dongles generally unsupported — CoreBluetooth requires the internal radio.
Privacy permissionOn first launch, grant Bluetooth access when macOS prompts. Without it, scanning returns nothing.
For background scanningLocation may be required if the app needs to continue scanning while backgrounded (macOS policy).

First Launch

If you downloaded the direct build outside the App Store, macOS Gatekeeper may block the unsigned binary on first run. Right-click the app, choose Open, then confirm. This is only required once.

macOS requires explicit Bluetooth permission. If you deny the prompt, the app opens but every scan returns empty. Fix it under System Settings → Privacy & Security → Bluetooth, and toggle the app back on.

Quick Start

1. Scan for BLE peripherals (1 minute)

This is the most common path — finding the BLE device you're developing against (an ESP32, an nRF52 dev kit, a sensor beacon, a fitness tracker).

  1. Open the app and click Start Scanning (or press ⌘R).
  2. The peripheral list fills with every advertiser in range, showing name, MAC-ish UUID, RSSI, and advertised service UUIDs.
  3. Sort by RSSI to bring the closest devices to the top. Your target is usually the strongest signal.
  4. Click a peripheral to open its detail view.

If the list stays empty, jump to Troubleshooting — the most frequent cause is a denied Bluetooth permission or the peripheral not advertising.

2. Connect and browse the GATT hierarchy

  1. Click Connect on the chosen peripheral. The app performs a connection and service discovery.
  2. The GATT tree appears: Services (128-bit UUIDs) expand into Characteristics, which expand into Descriptors.
  3. Standard services show their decoded name (e.g. Device Information (0x180A)) alongside the raw UUID.

3. Read and write a characteristic

  1. Find a characteristic with the Read property. Click Read — the value and its length appear in hex and ASCII.
  2. For a writable characteristic (Write property), click Write, enter the value (hex or text), choose write-with-response or write-without-response, and send.
  3. The read-back confirms the write took.

4. Subscribe to notifications

  1. For a characteristic with the Notify property, enable the Client Characteristic Configuration Descriptor (CCCD) by clicking Subscribe.
  2. Live values stream into the characteristic's value log as the peripheral pushes them — typically every second for sensor data.
  3. Click Unsubscribe to stop; Disconnect to tear down.

Configuration

Scan Settings

ParameterOptions
Scan modePassive (listen only, lower power, misses some data) / Active (requests scan-response data, default)
Service filterOnly show peripherals advertising a specific 16-bit or 128-bit service UUID — essential in a crowded 2.4 GHz environment
RSSI floorHide peripherals weaker than -90 dBm (default) to declutter the list
Duplicate filteringShow each peripheral once (update RSSI in place) vs. log every advertisement packet

UUID Types

BLE UUIDs are either 16-bit (a registered short that maps to a 128-bit base) or full 128-bit. The app decodes the Bluetooth SIG assigned numbers and shows both forms.

UUIDNameWhere You'll See It
0x180ADevice InformationAlmost every peripheral — manufacturer name, model, firmware
0x180FBattery ServiceBattery level percentage
0x2A00Device NameThe friendly name advertised
0x2A29Manufacturer Name StringUnder Device Information
0x2A37Heart Rate MeasurementHeart-rate belt / fitness devices
0x2902CCCDOn every notifiable/indicate characteristic — the subscribe toggle
custom 128-bitVendor-definedYour firmware's custom service — not decoded, shown as the raw UUID

Characteristic Properties

PropertyMeaning
ReadSupports read requests
WriteSupports write with response (acknowledged)
Write Without ResponseSupports fire-and-forget write (faster, unacknowledged)
NotifyServer pushes updates; no acknowledgment (default for sensor data)
IndicateServer pushes updates with acknowledgment (more reliable, slightly slower)
BroadcastCan be included in advertising data
Authenticated Signed WriteWrite requires a signed (paired) connection

Connection Parameters

Once connected, the peripheral and Mac negotiate connection parameters: interval, slave latency, supervision timeout. The app displays the negotiated values. Faster intervals (e.g. 7.5–15 ms) mean lower latency and higher power draw; slower intervals (100–1000 ms) save battery. The peripheral requests a set; the Mac (central) accepts or adjusts.

ParameterTypical Range
Connection interval7.5 ms – 4 s (multiples of 1.25 ms)
Slave latency0–499 (slave may skip this many intervals)
Supervision timeout100 ms – 32 s (link assumed dead after this idle)
MTU23 (default) up to 247 bytes after MTU exchange

Key Features

Real-time advertising scanner

The scanner shows every advertisement with RSSI trend, advertised service UUIDs, manufacturer-specific data, and TX power. Filter by service UUID to isolate your device in a busy trade-show floor or a lab full of beacons.

Full GATT hierarchy browser

Services → Characteristics → Descriptors, with the SIG-assigned names decoded inline. For custom 128-bit UUIDs, you can annotate each with a friendly name in a saved profile, so your next session shows "Motor Speed" instead of a hex string.

Read, write, notify, indicate

Every GATT operation is one click. Notifications and indications stream into a per-characteristic log with timestamps. Write supports both write-with-response (reliable) and write-without-response (fast) — critical for throughput-sensitive firmware testing.

Payload decoder with common formats

Raw bytes are shown in hex and ASCII, and you can apply a decoder: UTF-8 string, little/big-endian integer, IEEE 754 float, or a custom struct layout. Decode a 4-byte temperature payload as a float once, and every subsequent notification renders in engineering units.

MTU exchange and connection tuning

Request a larger MTU after connecting to boost throughput (useful for OTA firmware updates over BLE). The negotiated MTU and connection parameters are displayed live, so you can confirm the peripheral honored your request.

Advertising packet capture

For commissioning and reverse-engineering, the app logs every advertisement packet — raw PDU, type (ADV_IND, ADV_NONCONN_IND, SCAN_RSP), and the manufacturer-data payload. Export the log to dissect a vendor's beacon format or debug intermittent advertising.

Troubleshooting

Common BLE failure modes

SymptomLikely Cause
Scan returns nothingBluetooth permission denied, or radio off, or no peripherals advertising
Peripheral in list but won't connectOut of range, peripheral is bonded/paired-only, or central slot exhausted
Connects then disconnects in ~30 sSupervision timeout — link dropped by interference or the peripheral rejected the connection parameters
Read returns "Not supported"The characteristic lacks the Read property — check the properties bitmask
Write returns "Invalid handle" or fails silentlyWrong write type (write-without-response on a write-with-response-only characteristic, or vice versa)
Notifications never arriveCCCD not enabled, or the characteristic supports Indicate (not Notify) — the subscribe call must match

Scan returns empty (Bluetooth permission)

Symptom: Scan is running, the list stays empty, known BLE devices are nearby.
Fix: macOS denied Bluetooth access. Open System Settings → Privacy & Security → Bluetooth and confirm the app is toggled on. If it's not listed, click the + and re-add it, or re-launch the app to re-trigger the prompt. Also confirm the Mac's Bluetooth radio is on (menu bar icon).

Peripheral seen in scan but connection fails

Symptom: Clicking Connect shows a spinner, then fails or silently disconnects.
Fix: Three common causes: (1) the peripheral is now out of range (BLE advertising ≠ connectable reliably past 10 m). Move closer. (2) The peripheral is bonded and rejects non-bonded centrals — pair first. (3) The Mac's central already holds its max concurrent connections (typically 7–12). Disconnect stale peripherals and retry.

Notifies don't arrive (CCCD / property mismatch)

Symptom: You subscribed but no values stream.
Fix: Check the characteristic's properties. If it has Indicate but not Notify, the subscribe action must request Indicate — the app handles this automatically when you click Subscribe, but a manual CCCD write to the wrong bit (0x0001 Notify vs 0x0002 Indicate) produces silence. Also confirm the CCCD (0x2902) descriptor exists; without it, the characteristic can't be subscribed.

Disconnects after 30 seconds (supervision timeout)

Symptom: Connection holds briefly, then drops.
Fix: The supervision timeout lapsed because no packets were exchanged in the window — usually RF interference or the peripheral sleeping. Confirm the peripheral is awake and transmitting. If you control the firmware, widen the connection interval or raise slave latency so the link survives brief quiet periods.

Write rejected (write-with-response vs without-response)

Symptom: Write fails or returns an error on a known-writable characteristic.
Fix: The characteristic accepts only one write type, not both. Check the properties: if only Write is set, use write-with-response; if only Write Without Response, use the fire-and-forget path. Sending the wrong type returns an error or silently drops the byte. The app selects based on the properties bitmask automatically, but a manual override will trip this.

nRF Connect comparison / install issues

The de facto reference tool, nRF Connect for Desktop, has well-documented install failures on macOS (the BLE module fails with "Failed to get the library" errors on Apple Silicon). This app covers the same scan + connect + GATT browse + read/write workflow natively, without the JRE/JNI dependency chain that breaks nRF Connect. If you're migrating from nRF Connect, the GATT tree and characteristic-property semantics map 1:1.

Support

When reporting a connection or GATT issue, attach the advertising/gatt log export (with any device-identifying UUIDs redacted), the peripheral's model/firmware, and the characteristic UUID + properties you were operating on.