Raspberry piをbluetoothスピーカーにする

前回の「Raspberry piをNAS兼音楽プレイヤーにする(MPD編)」でRaspberry Pi にスピーカーを接続したので、今度は Bluetooth スピーカーの機能も持たせてみます。NASストレージ内の音楽だけでなく、スマホ内の音楽を再生したり、スマホからAmazon Musicなどを再生できるようになります。

ただ、音楽再生には十分ですが、遅延が大きいですので、動画再生やゲームなどには向いていません。

また狙ってはいなかったのですが、試してみたらマルチポイント接続もできました! スマホとPCを同時に接続して同時に再生することも可能です。

目指す構成

今までの NAS 内の音楽を再生する機能を持ちつつ、Bluetoothスピーカーの機能も持たせます。これにより、NAS内の音楽再生に加えて、Bluetoothで接続すればスマホ内の音楽やスマホで再生した Youtube、 Amazon Music などのサブスク系音楽も聞けるという、音楽再生を全て 1 つに集約したような音楽サーバを目指します。

A2DPの実装: pulseaudio bluetooth と bluealsa

Linux での Bluetooth スタックと言えば bluez なのですが、A2DPについては bluez 単体ではサポートしておらず、プラグインの形で別のソフトが機能提供しているようです。

そして bluez 用の A2DP プラグインの実装は2つあるようです。

  • pulseaudio bluetooth module
  • bluealsa (bluez-alsa)

ソフトウェアの構造としてはそれぞれ以下のような感じになります。

pulseaudio bluetooth module を使った時のソフトウェア構成

まずは pulseaudio bluetooth module です。

pulseaudio-molude-bluetooh は pulseaudio のモジュールであり、bluezの A2DP のプラグインでもあるような構成です。このモジュールを有効化してpulseaudio を起動すると bluez に対して A2DP の機能を提供するようになるようです。

この構成の場合は pulseaudio が必須です。

bluealsa を使った時のソフトウェア構成

続いて bluealsa です。

bluealsa プロセスは bluezのプラグインとしてA2DPの機能を提供します。もう1つ bulealsa-aplay という別のコマンドで、bluealsa から音声データを受け取り、ALSA で再生します。そのため pulseaudio はなくても再生できます。

今回は前回の mpd 構築時に pulseaudio を起動させているので、上図のように実際は ALSA ではなく  pulseaudio がフックするような形になります。

pulseaudio がいないと mpd が ALSA を専有してしまうので bluealsa-aplay が再生できなくなってしまいます。pulseaudio がいるおかげで mpd で音楽を再生させながらスマホの音を再生させることも可能になります。

でどっちがいいの?

どちらも試してみたのですが、Raspberry OS(buster) の環境では bluealsa が良いようです。

pulseaudio bluetooth module の場合はバッファリングのサイズが小さいのか、buffer under run のエラーが出てちゃんと再生できないことがありました。スマホからは何度か再生停止を行うと安定して再生できたのですが、PCからの再生は途切れ途切れになってしまうなど、ちょっと難ありでした。もう少し設定値を詰めれば直るのかもしれませんが・・。

bluealsa は今の所ちゃんと再生できています。また、bluealsaを自前でビルドすれば apt-x のコーデックにも対応できるらしいです。今回は RPi のステレオジャックからの再生なので音質はあまり気にせずビルドしなくても使用可能な SBC で構築します。

必要なもの

  • Bluetoothアダプタ(USB)
  • アンプ付きスピーカー

すでに前回の MPD 環境を構築している場合はスピーカーは接続済みだと思いますので、新しく必要なのは Bluetooth アダプタのみです。

手持ちの USB Bluetooth アダプタは相当古く、Bluetooth v1.2 の頃のものだったため新たに新調しました。Amazonで USB Bluetooth アダプタを探すと、500円以下のあまりに安いものが見つかりますが、ちょっと怖いということもあり、1000円程度の以下のもの(それでも国内メーカ品より安い)を買いました。

TP-Link 社という私は初めて聞いたのですが、中国のWiFiルータなどネットワーク機器のメーカーのようです。

今の所、特に不具合なく使えています。

Raspberry Pi3 以降であれば Bluetooth が搭載されているのでドングルなしでも行けるかもしれません(未確認)。

bluetoothスピーカーにする手順

bluetoothスピーカーにするには大きく以下の3つを実施します。

  1. bluetoothを使えるようにする(初期設定)
  2. A2DPで再生できるようにする (bluealsaの設定)
  3. パスコードでペアリングできるようにする

順番にやっていきましょう。

bluetoothを使えるようにする(初期設定)

まずは bluetooth USB ドングルを認識させて、初期設定をしていきます。

Raspberry Pi OS(debian)には bluez がデフォルトでインストールされているので、USB Bluetooth アダプタを挿すだけで認識します。

bluetoothグループにユーザを追加

これから bluetoothctl などのコマンドを使うのですが、sudo を付けなくても実行できるように bluetooth グループに自分のユーザを追加します。デフォルトでは pi ユーザです。

$ sudo usermod -a -G bluetooth ユーザ名

usermod でグループを変更してもログイン済みのユーザには反映されないため、一度 ssh 接続をログアウトして、再度ログインし直します。もしくはシステムの再起動でもOKです。

再ログイン/再起動後に以下のコマンドを実行して自ユーザが bluetooth に所属していればOKです。XXXXXには自分のグループ名(デフォルトはpi)が入っていると思います。

$ groups
XXXXX adm lp dialout cdrom sudo audio video plugdev games users input netdev bluetooth pulse-access gpio i2c sp

bluetoothとして認識しているか確認

bluetooth グループに所属していれば bluetoothctl show を実行すると USB Bluetooth アダプタの情報が表示されます。

$ bluetoothctl show
Controller XX:XX:XX:XX:XX:XX (public)
Name: BlueZ
Alias: BlueZ
Class: 0x00000000
Powered: yes
Discoverable: no
Pairable: no
UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
UUID: Handsfree (0000111e-0000-1000-8000-00805f9b34fb)
UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb)
UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)
Modalias: usb:v1D6Bp0246d0532
Discovering: no

細かい内容は置いておいて、このような表示が出力されれば認識されています。

名前とクラスの設定

bluetoothで周辺機器を検索した時に出てくる名前やクラスを設定します。

クラスはデバイスを検索した時にどういうデバイスなのかを相手知らせるための設定で、Audioを再生できるデバイスとして設定しないとペアリングができないことがあります。

クラスの値は以下のサイトで作ることができるようです。

以下のようにそれっぽい設定にして作ってみたところ 0x20041c となりました。

  • Major Service Class → Audio
  • Major Device Class → Audio/Video
  • Minor Device Class → Portable Audio

それでは設定していきます。赤自分を変更しています。

$ sudo vi /etc/bluetooth/main.conf
<省略>
Name = RaspberryPi
<省略>
Class = 0x20041c
<省略>
AutoEnable=true

最後の AutoEnable は認識したらすぐに電源を入れる設定です。

設定が完了したら bluez を再起動します。

$ sudo systemctl restart bluetooth.service

もう一度 bluetoohctl show を実行すると名前とクラスが変わっているはずです。

$ bluetoothctl show
Controller XX:XX:XX:XX:XX:XX (public)
Name: RaspberryPi
Alias: RaspberryPi
Class: 0x0020014c
Powered: yes
Discoverable: no
<<省略>>

A2DPで再生できるようにする (bluealsaの設定)

bluealsaのインストールとA2DPの有効化

bluez パッケージはデフォルトでインストールされているので、インストールが必要なのは bluealsa のみです。

$ sudo apt-get install bluealsa

インストールすると bluealsa が systemd に登録され自動的に起動します。デフォルトでは A2DP の受信機能が有効化されていないので bluealsa コマンドにオプションを追加します。下記の赤字部分を追記してください。

$sudo vi /lib/systemd/system/bluealsa.service
[Unit]
Description=BluezALSA proxy
Requires=bluetooth.service
After=bluetooth.service

[Service]
Type=simple
User=root
ExecStart=/usr/bin/bluealsa -p a2dp-sink

追加したら bluealsa を再起動します。

$ sudo systemctl restart bluealsa.service

ps コマンドでデーモンとして起動している bluealsa コマンドにオプションがついているかを確認します。

$ ps aux | grep bluealsa
root 14372 6.8 0.4 45148 4496 ? Ssl 13:37 20:53 /usr/bin/bluealsa -p a2dp-sink

この状態で bluetoothctl show を実行すれば Audio Sink が表示されるはずです。

$ bluetoothctl show
Controller XX:XX:XX:XX:XX:XX (public)
Name: RaspberryPi
Alias: RaspberryPi
Class: 0x0020041c
Powered: yes
Discoverable: no
Pairable: no
UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)
UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
UUID: Handsfree (0000111e-0000-1000-8000-00805f9b34fb)
UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb)
UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)
Modalias: usb:v1D6Bp0246d0532
Discovering: no

Audio Sink が表示されました。これで Audio Sink (音声再生) 機能をもたせることができました。

bluealsa-aplayを起動させる

前項で設定した bluealsa は A2DP の受信を受け持つだけで再生はしてくれないので、再生するための bluealsa-aplay コマンドをデーモンとして起動させます。

# インストールしたらこの辺りまでやってくれればいいのに・・・

bluealsa-aplay.service というサービスを新規に作成して systemd に追加します。以下の内容でファイルを作成します。

$ sudo vi /etc/systemd/system/bluealsa-aplay.service
[Unit]
Description=BlueALSA aplay service
After=bluetooth.service
Requires=bluetooth.service

[Service]
ExecStart=/usr/bin/bluealsa-aplay 00:00:00:00:00:00

[Install]
WantedBy=multi-user.target

作成したらサービスを登録し、起動させます

$ sudo systemctl enable bluealsa-aplay.service
$ sudo systemctl start bluealsa-aplay.service

これで再生する準備は整いました。

ペアリング、接続、再生確認

ここまで設定できたら、ペアリング/接続できるか確認してみます。

ググるとよく出てくる手順は、RPi→Bluetoothデバイス方向の接続確認手順ですが、今回はRPiはデバイス側なので、デバイス側としての接続確認をしてみます。

bluetoothctlコマンドを対話モードで使います。下記赤字部分が入力したところです。※以降はコメントです

私も agent 周りはよく分かっていないのでコメント部分は間違っているかも・・

$ sudo hciconfig hci0 sspmode 0     ※これをやらないとペアリングがうまくいかない
$ bluetoothctl
Agent registered
[bluetooth]# agent off    ※agentを無効化 (agent = ペアリングする際の手順を実施するもの)
Agent unregistered
[bluetooth]# agent NoInputNoOutput   ※agentを NoInputNoOutput に設定
Agent registered
[bluetooth]# default-agent    ※ 上記NoINputNoOutputをデフォルトに設定
Default agent request successful
[bluetooth]# discoverable yes    ※他のデバイスから検索可能にする
Changing discoverable on succeeded
[bluetooth]# pairable yes     ※他のデバイスからのペアリングリクエストを受け付ける
Changing pairable on succeeded

この段階でスマホからBluetoothの検索を行うと、RaspberryPiデバイスが表示されるはずです。

スマホからペアリング操作をすると以下の表示が出ますので、PINコードを入力します。私のAndroidスマホではPINコードは2回入力が必要でした。

[NEW] Device XX:XX:XX:XX:XX:XX phone
Request PIN code
[agent] Enter PIN code: 1234
[DEL] Device XX:XX:XX:XX:XX:XX phone
※この辺でスマホにPINコード入力画面が表示されるので1234を入力
[NEW] Device XX:XX:XX:XX:XX:XX phone
Request PIN code
[agent] Enter PIN code: 1234
[CHG] Device XX:XX:XX:XX:XX:XX Class: 0x005a020c
[CHG] Device XX:XX:XX:XX:XX:XX Icon: phone
[CHG] Device XX:XX:XX:XX:XX:XX Connected: yes
[CHG] Device XX:XX:XX:XX:XX:XX Modalias: bluetooth:v010Fp107Ed1436
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 0000046a-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001105-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 0000110a-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 0000110c-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001112-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001115-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001116-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 0000111f-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 0000112f-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001132-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001200-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001800-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 00001801-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX UUIDs: 0000fe35-0000-1000-8000-00805f9b34fb
[CHG] Device XX:XX:XX:XX:XX:XX ServicesResolved: yes
[CHG] Device XX:XX:XX:XX:XX:XX Paired: yes
Authorize service
[agent] Authorize service 0000111e-0000-1000-8000-00805f9b34fb (yes/no): yes
Authorize service
[agent] Authorize service 0000110d-0000-1000-8000-00805f9b34fb (yes/no): yes
[phone]# paired-devices 
Device XX:XX:XX:XX:XX:XX phone

これでペアリングは成功です。ペアリング後自動的に接続されているはずです。

Androidスマホの場合、以下のように「通話とメディア音声に対して接続済み」と表示されていればA2DPでの接続に成功しています。

Androidで接続した場合の画面
この状態でスマホから音楽を再生させればスピーカーから音が流れるはずです。

固定のパスコードでペアリングできるようにする

ペアリングするたびにRPiにログインして上記のようなコマンドを打つのは面倒なので、一般的なBluetoothスピーカーのように、固定のPINコードを入力すればペアリングできるようにします。

※セキュリティ的に固定PINコードが心配な人はここは飛ばしてもOKです。

ペアリングする時の処理は agent と呼んでいましたが、上記手順では bluetoothctl コマンドが持っている内蔵 agent (NoInputNoOutput) を使いました。これは bluetoothctl コマンド内蔵の agent なので対話モードを抜けてしまうと(bluetoothctlコマンドが終了すると) agent もいなくなってしまいます。

そこで agent の処理をするプロセスを別途デーモンとして起動させておく必要があります。このデーモン用の agent が bt-agent コマンドです。

bt-agentコマンドを起動するサービスを systemd に登録します。ここでは bt-agent.service という名前にしました。以下のようなファイルを作成します。

$ sudo vi /etc/systemd/system/bt-agent.service[Unit]
Description=Bluetooth Auth Agent
After=bluetooth.service
PartOf=bluetooth.service

[Service]
Type=simple
ExecStart=/usr/bin/bt-agent -c NoInputNoOutput -p /etc/bluetooth/pin.conf
ExecStartPost=/bin/sleep 1
ExecStartPost=/bin/hciconfig hci0 sspmode 0

[Install]
WantedBy=bluetooth.target

bt-agent の引数に渡している /etc/bluetooh/pin.conf というファイルから PIN コードを読み込みます。

以下のような内容で pin.conf を作成します。

$ sudo vi /etc/bluetooth/pin.conf
* 123789

123789 の部分はPINコードですので適宜変更してください。* の部分には 00:00:00:00:00:00 のようにHWアドレスを指定することも可能です。

PINコードは0000や1234などの一般的なものは絶対に避けてください。

Bluetoothは思ったよりも電波が飛ぶので、隣人にペアリングされてしまうと、大音量で音楽を再生されるなどのイタズラができてしまいます 😀

main.service ファイルと pin.conf ファイルが作成できたら、bt-agent.service を登録して起動します。

$ sudo systemctl enable bt-agent.service
$ sudo systemctl start bt-agent.service

スマホからペアリングを一旦解除して、もう一度ペアリングしてみてください。pin.conf で設定したPINコードでペアリングに成功すればOKです。

これで一般的なBluetoothスピーカーの機能をもたせることができました。お疲れ様でした。

おまけ: ハマったところ

bluezのバージョンによる設定の違い

bluez a2dp などで検索すると、/etc/bluetooth/audio.conf に Enable=Source,Sink を追加ればよいとか簡単にできそうな記事が出てくるのですが、今はこの方法は使えない(?)ようです。前のバージョンは bluez が A2DP の機能も持っていたのだと思います。

bluezの公式ドキュメントが少ない

上記 A2DP の設定にも関連するのですが、公式のドキュメントを探したのですが、manpage 程度の情報しかなくて最新の情報を集めるのに苦労しました・・。

bluezの公式サイトを見ても大したことは書いてないし、ソースアーカイブの doc/ フォルダにもAPIの説明ばかりであまり参考になる情報もなくて。

探し方が悪いのか、固定PINコードでのペアリングなんかはブログやQAサイトの回答くらいしかまともな情報がなく、1つ1つ試すのが大変で・・。皆さんどうやって使ってるんでしょうか・・?

bluez は zaurus の頃にもお世話になったのでかなり昔からあるのですが、ドキュメントが貧弱なのは驚きでした。

Raspberry piをbluetoothスピーカーにする” への4件のフィードバック

    1. はるはるさん
      コメント頂きありがとうございます。
      また、コメントに気づくのが遅くなり、返信が遅くなってすみませんでした。
      実行したら一発でインストールできるような shell script ってことですよね。
      時間ができたらやってみようと思います。
      すみませんが、気長にお待ちいただければと思います。

    1. 返信遅くなってすみません。
      aptx についてですが、技術的な興味はあるものの、Raspbery pi オーディオの使用頻度が下がったのと、SBCで満足してしまっていることもあり、あまり腰が上がらない状況です。すみません。

      SBC のままで結構ですが実際に試して聞いてみたでしょうか?
      まだであれば、ぜひ聞いてみて、音質を確認してみていただきたいです。

      私も SBC だからとあまり期待していなかったのですが、個人的には満足な音質だったので aptx にまで手を出さなかったということもあります。

      密閉空間でダイレクトに音を感じ取れるヘッドホンやイヤホンであれば、SBC の音質の悪さが気になったり、aptx の音質・低遅延を十分体感できると思いますが
      生活音にあふれる家でスピーカーで聞くと、音質の悪さは全く気にならなかったです。
      (aptxの低遅延が目的なら的外れなコメントでごめんなさい)

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です