Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.16)

project(based_connect C)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

add_executable(based-connect
main.c
based.c
bluetooth.c
util.c
log.c
)

target_compile_options(based-connect PRIVATE
-Wall
-Wextra
-Wpedantic
-g
)

target_link_libraries(based-connect PRIVATE bluetooth)

91 changes: 91 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Device Compatibility

Per-device feature status. If you test a feature on a device not listed here,
or test a feature marked `?`, please open a PR updating this table.

## Status codes

| Code | Meaning |
|------|---------|
| `Y` | Confirmed working |
| `N` | Confirmed not available on this device |
| `P` | Partial — works with caveats (see Notes column) |
| `?` | Untested or unknown |

## Feature matrix

| Feature | QC35 | SoundLink II | NC 700 | Notes |
|---------|:----:|:------------:|:------:|-------|
| Basic connection / init | Y | Y | Y | |
| `--firmware-version` | Y | Y | Y | NC 700 returns an 18-byte version string; `VER_STR_LEN` expanded to 32 |
| `--serial-number` | Y | Y | Y | |
| `--battery-level` | Y | Y | Y | NC 700 returns a 3-byte payload; only the first byte (level) is used |
| `--device-id` | Y | Y | Y | |
| `--device-status` | Y | Y | P | NC 700: name/language/auto-off read correctly; NC level step fails (see protocol notes) |
| `--name` | Y | Y | ? | get_name mask was fixed to accept NC 700's encoding byte; set not yet tested |
| `--prompt-language` | Y | Y | Y | NC 700: setting applied, no confirmation response |
| `--voice-prompts` | Y | Y | Y | NC 700: setting applied, no confirmation response |
| `--auto-off` | Y | Y | Y | NC 700 sends a full confirmation response |
| `--noise-cancelling` | Y | N | P | NC 700: set applied (no confirmation response); read-back uses a different subcategory and payload format (see protocol notes) |
| `--pairing` | Y | Y | Y | NC 700: setting applied, no confirmation response |
| `--paired-devices` | Y | Y | P | NC 700: device addresses read correctly; connection status parsing fails (enum values differ) |
| `--connect-device` | Y | Y | ? | |
| `--disconnect-device` | Y | Y | ? | |
| `--remove-device` | Y | Y | ? | |
| Sidetone / self-voice level | ? | N | ? | Not yet implemented; needs packet capture from Bose Music app |
| Conversation mode | ? | N | ? | Not yet implemented |
| Volume control | ? | Y | ? | Not yet implemented; see details.txt |

## Device IDs

Device IDs are returned by `--device-id` and are used internally to gate
features like noise cancelling. Add new entries here as they are discovered.

| Device | Device ID | Noise cancelling |
|--------|-----------|:----------------:|
| QuietComfort 35 (gen 1) | `0x400c` | Y |
| QuietComfort 35 II | `0x4020` | Y |
| SoundLink II | `0x4014` | N |
| NC 700 | `0x4024` | Y |

## Tested firmware versions

| Device | Firmware |
|--------|----------|
| QuietComfort 35 | 1.06, 1.2.9, 1.3.2 |
| SoundLink II | 2.1.1 |
| NC 700 | 1.1.4-1144+be3bf4b |

## Protocol notes by device

### QuietComfort 35 / SoundLink II
Original target devices. All set commands receive a confirmation response that
is read back and verified.

### NC 700 (firmware 1.1.4-1144+be3bf4b)

**Set commands**: The device applies settings but sends no confirmation packet
for most commands (`--voice-prompts`, `--prompt-language`, `--noise-cancelling`,
`--pairing`). `--auto-off` is the exception — it does return a full confirmation.
The program detects missing confirmations via `EAGAIN` on the response read and
treats it as success.

**Response lengths**: Several responses use longer payloads than on QC35.
`--firmware-version` returns 18 bytes (QC35: 5). `--battery-level` returns
3 payload bytes (QC35: 1); the first byte is the level percentage.

**Name encoding**: The name response includes an encoding byte (0x01 on NC 700,
0x00 on QC35) that the original mask incorrectly rejected. Both values are now
accepted.

**Noise cancelling read-back**: The NC 700 uses subcategory `0x05` for the noise
cancelling status packet (QC35 uses `0x06`), and the payload is 3 bytes in a
different order (`0x0b [LEVEL] [?]` vs QC35's `[LEVEL] 0x0b`). This causes
`--device-status` to fail at the NC level step. A Bluetooth HCI capture of the
Bose Music app changing NC levels on an NC 700 is needed to confirm the exact
payload layout before this can be fixed.

**Paired devices**: The device addresses are read correctly but the connection
status byte uses values outside the `DC_ONE`/`DC_TWO` enum range expected by the
original code. The NC 700 supports multipoint (two simultaneous connections) so
additional status values are likely. A capture is needed to map them.
56 changes: 53 additions & 3 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Options:
-h, --help
Print the help message.

-D, --debug
Print debug logging to stderr. Shows raw packet bytes sent and
received. Useful when adding support for a new device.

-n <name>, --name=<name>
Change the name of the device.

Expand Down Expand Up @@ -94,9 +98,11 @@ Dependencies
Disclaimer
----------

This has only been tested on Bose QuietComfort 35's with firmware 1.3.2, 1.2.9,
1.06 and SoundLink II's with firmware 2.1.1. I cannot ensure that this program
works on any other devices.
Originally tested on Bose QuietComfort 35's with firmware 1.3.2, 1.2.9, 1.06
and SoundLink II's with firmware 2.1.1. Partial support for the NC 700 (firmware
1.0.4) has since been added: set operations work, but the NC 700 does not send
confirmation responses so read-back verification is skipped. See
COMPATIBILITY.md for a per-device feature status table.

Todo
----
Expand All @@ -105,11 +111,55 @@ Todo
* Current status of all setters currently implemented
* Date of manufacturing
* Get/set volume
* Sidetone / self-voice level (amount of your own voice heard on calls)
* Conversation mode toggle
* Port to MacOS (and maybe Windows)
* Firmware updates?
* Confirm NC 700 noise cancelling packet format and test --noise-cancelling

Firmware Details
----------------

See details.txt for partly reverse engineered but unimplemented (and unknown)
details.

Contributing / Adding Device Support
-------------------------------------

The Bose RFCOMM protocol is proprietary and undocumented. Every command in this
program was found by capturing traffic between the official Bose app and the
headphones. The same technique can be used to find missing commands or add
support for other devices.

Step 1 -- Capture a Bluetooth HCI snoop log
1. On Android, go to Settings > Developer Options and enable
"Bluetooth HCI snoop log". (The exact path varies by manufacturer.)
2. Pair and connect your headphones to the phone.
3. Open the Bose Music app and exercise the feature you want to reverse
engineer -- adjust the setting up and down a few times so the packets
appear clearly in the capture.
4. Disable the snoop log and pull the file:
adb pull /sdcard/btsnoop_hci.log
The path may differ; check your device's developer documentation.

Step 2 -- Analyse with Wireshark
1. Open btsnoop_hci.log in Wireshark.
2. Filter for your headphone's MAC address and RFCOMM traffic:
bluetooth.src == "XX:XX:XX:XX:XX:XX" && btrfcomm
3. Look for packets that change when you adjust the setting. The byte that
varies with the slider/toggle is the value; the surrounding fixed bytes
are the command opcode.
4. Send commands in both directions at different values to identify the full
range and confirm direction (phone-to-headphone vs headphone-to-phone).

Step 3 -- Probe with --send-packet
Once you have candidate bytes from the capture, you can test them directly:
./based-connect --send-packet 010302011E ADDRESS
The raw response bytes are printed to stdout. Run with -D to also see the
lower-level read/write trace.

Step 4 -- Implement and document
New commands follow the pattern in based.c: a static get_X / set_X function,
a new enum or int for the value range, and a CLI option added in main.c.
Please update COMPATIBILITY.md with the device and firmware version you
tested on.
Loading