LinuxのBluetooth:不安定なGUIを捨ててbluetoothctlをマスターする

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

ヘッドレス環境でのBluetoothの悩み

ヘッドレスのRaspberry Pi 4にBluetoothスピーカーを接続しようとして、日曜日を丸一日無駄にしてしまいました。30秒ごとにフリーズするリモートデスクトップのGUIをクリックし続けていたのです。

接続は失敗し続けましたが、表示されるのは最終的に赤くなるだけの汎用的なプログレスバーのみでした。裏側では単純なタイムアウトエラーが発生していたのです。i3のようなミニマリストな環境を使っている場合や、リモートサーバーを管理している場合、あるいは単にバグの多いデスクトップツールにうんざりしているなら、BlueZスタックを直接操作する必要があります。

BlueZは、現代のLinuxディストリビューションにおける公式のBluetoothプロトコルスタックです。GNOMEやKDEなどのデスクトップ環境は、そのラッパーとして機能します。しかし、これらのラッパーは、デバイスのペアリングが拒否された際やLow Energy (BLE) センサーをテストする際に必要な詳細情報を隠してしまうことがよくあります。コマンドラインに移行することは、単なる「パワーユーザー」の自慢ではありません。ハードウェアがOSにリアルタイムで何を伝えているかを正確に把握できるようになるのです。

接続チェーンが切れる場所

「接続」をクリックしても何も起こらない場合、通常は3つのレイヤーのいずれかで失敗が発生しています。コマンドを実行する前に、どこで停滞しているかを特定する必要があります。

1. ドライバーとファームウェアのレイヤー

Linuxカーネルには、Intel AX200やBroadcom BCM43438などのほとんどのチップ用ドライバーが含まれています。しかし、これらは多くの場合、/lib/firmwareにあるプロプライエタリなファームウェアのバイナリ(blob)を必要とします。ファームウェアのロードに失敗すると、ハードウェアがlsusbに表示されても初期化されません。これは、linux-firmwareパッケージが正しく同期されていない状態でカーネルをアップデートした際によく発生します。

2. BlueZデーモン (bluetoothd)

このシステムサービスが基礎となるロジックを処理します。bluetoothdの設定が間違っていたり、適切な権限がなかったりすると、ペアリングは毎回失敗します。TLPのような電力管理ツールが、数ミリワットを節約するためにBluetooth USBブリッジを「自動サスペンド」してしまい、事実上サービスを停止させているケースをよく見かけます。

3. オーディオサーバー (PipeWire/PulseAudio)

ヘッドセットの場合、Bluetooth接続は戦いの半分に過ぎません。BlueZがリンクを確立し、オーディオストリームをPipeWireあるいはPulseAudioに引き渡します。A2DP (Advanced Audio Distribution Profile) が欠けていると、デバイスは「接続済み」なのに音が全く出ないという状態になります。FedoraやUbuntu 22.10以降のほとんどのディストリビューションでは、古いPulseAudioよりもこれらの引き継ぎをうまく処理できるPipeWireが標準となっています。

ツールの選択:GUI vs. CLI

すべての管理方法が等価というわけではありません。BluemanのようなデスクトップGUIはマウス操作には適していますが、エラーメッセージを隠してしまうためデバッグには不向きです。Low Energyサポートの有効化など、システム全体の設定変更には/etc/bluetooth/main.confの編集が必要ですが、bluetoothctlこそが黄金律(ゴールドスタンダード)です。これは対話型のCLIツールで、リアルタイムのフィードバックを提供し、SSH経由でも完璧に動作します。

ワークフロー:bluetoothctlをマスターする

まず、サービスがアクティブであることを確認します。以下のコマンドを実行してデーモンを起動してください:

sudo systemctl enable --now bluetooth
sudo systemctl status bluetooth

次に、bluetoothctlと入力して対話型シェルに入ります。ターミナルのプロンプトが変わり、通常はコントローラーのMACアドレスが表示されます。

ペアリングの手順

以下の手順を厳密に守ってください。「agent」や「trust」のステップを飛ばすことが、Linuxでペアリングが失敗する最大の原因です。

# コントローラーの電源をオンにする
[bluetooth]# power on

# PINコードを処理するためのエージェントを有効にする
[bluetooth]# agent on
[bluetooth]# default-agent

# デバイスをスキャンする
[bluetooth]# scan on

デバイスが表示されたら(例:Device 00:11:22:33:44:55 Sony WH-1000XM4)、MACアドレスをコピーします。次に、接続を確定させます:

# 帯域を確保するためにスキャンを停止する
[bluetooth]# scan off

# デバイスをペアリングし、信頼設定を行う
[bluetooth]# pair 00:11:22:33:44:55
[bluetooth]# trust 00:11:22:33:44:55

# 接続を確立する
[bluetooth]# connect 00:11:22:33:44:55

デバイスが「0000」や「1234」のようなPINを必要とする場合、bluetoothctlはターミナル内で直接入力を求めてきます。GUIでは通知ウィンドウがアクティブなワークスペースに常に表示されるとは限らないため、ここで失敗することがよくあります。

IoTおよびBLEセンサーの接続

Xiaomiの温度センサーや心拍数モニターを使用している場合、おそらくBluetooth Low Energy (BLE) を使用しています。標準的なペアリングが常に適用されるとは限りません。代わりに、GATT (Generic Attribute Profile) コマンドを使用します。新しいバージョンのbluetoothctlでは、menu gattと入力することで、バッテリーレベルやセンサーのデータストリームなど、BLEデバイスの特定の属性を探索できます。

よくあるエラーのトラブルシューティング

CLIを使っても壁にぶつかることがあります。ここでは、最も一般的な問題の解決方法を紹介します。

「Resource Not Available」エラー

電源をオンにしようとしたときに「org.bluez.Error.Failed」と表示される場合は、通常ソフトウェアレベルでのブロックが原因です。Linuxはrfkillを使用して、省電力や機内モードへの対応のためにソフトウェアレベルで無線を無効にします。

# 無線の状態を確認する
rfkill list

# Bluetoothのブロックを解除する
sudo rfkill unblock bluetooth

低音質オーディオ(HSP/HFPの罠)

ヘッドセットは接続されているのに、1990年代の電話のような音質になることがあります。これは、OSがA2DPではなくHands-Freeプロファイルを選択したときに起こります。PipeWireを使用している場合は、pactlを使用して高品質なコーデックを強制的に指定します:

# Bluetoothカードを見つける
pactl list cards short

# プロファイルをA2DP(高音質)に設定する
pactl set-card-profile bluez_card.00_11_22_33_44_55 a2dp-sink

「最終手段」

以前はペアリングできていたデバイスが接続を拒否する場合、ローカルキャッシュが破損している可能性があります。デバイスを完全に削除し、サービスを再起動して状態をクリアにします。

[bluetooth]# remove 00:11:22:33:44:55
[bluetooth]# exit
sudo systemctl restart bluetooth

まとめ

コマンドラインからのBluetooth管理は難しそうに感じるかもしれませんが、scan -> pair -> trust -> connectというワークフローは非常に信頼性が高いものです。推測に頼るのではなく、透明性が確保されます。ボタンをクリックしてうまくいくことを祈る代わりに、なぜ接続が失敗しているのかを正確に把握し、数秒で修正できるようになります。

Share: