みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

yocto raspberrypi3でA2DP レシーバ

スマートフォンなどの音源を、Bluetooth経由でラズベリーパイ3からならす。 OSはいつもどおりyoctoprojectを使用して作成。

BluetoothではA2DPを使用し、sinkとなるようにする。 実際に音を鳴らすのはpulseaudioで、ラズベリーパイ3が他の端末からオーディオに見えるようにするための設定も pulseaudioのbluetooth-discoverモジュールで行う。

そのため、bluez5自体の設定ファイルはいらない。

ラズベリーパイ3上でBluetoothで接続された音源と音を鳴らすためのデバイスをloopbackしたり、経路を作る必要があり、 よそのサイトではこの部分にudevのルールを書いたりpythonスクリプトを書いたりしているのを見かけたが、 これもpulseaudioのbluetooth-policyモジュールで行うため今回は不要。

使用するyoctoprojectのバージョンはpyro(2.3) すでにrocko(2.4)が出ているが、今回は古いので行く。

環境構築

ソースの取得

$ git clone git://git.yoctoproject.org/poky.git -b pyro
$ git clone git://git.openembedded.org/meta-openembedded -b pyro
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b pyro

環境変数設定

環境変数の読み込みとビルドディレクトリの作成

$ cd ..
$ source poky/oe-init-build-env

ビルド対象のレイヤを追加

$ bitbake-layers add-layer ../poky/meta-raspberrypi
$ bitbake-layers add-layer ../poky/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../poky/meta-openembedded/meta-python

local.confの変更

下記をlocal.confに追加する

MACHINE = "raspberrypi3"

# systemd
DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""
IMAGE_INSTALL_append = " systemd-bash-completion \
             "

# pulseaudio
DISTRO_FEATURES_append = " pulseaudio pam"
IMAGE_INSTALL_append = " pulseaudio \
                 pulseaudio-server \
             pulseaudio-misc \
             pulseaudio-module-systemd-login \
             pulseaudio-module-bluez5-discover \
             pulseaudio-module-bluez5-device \
             pulseaudio-module-bluetooth-policy \
             pulseaudio-module-bluetooth-discover \
             pulseaudio-module-loopback \
             pulseaudio-bash-completion \
"

# bluez5
IMAGE_INSTALL_append = " bluez5"
IMAGE_INSTALL_append = " bluez5-testtools"

# connman
IMAGE_INSTALL_append = " connman connman-client"

# avahi
IMAGE_INSTALL_append = " avahi-daemon"

FETCHCMD_wget = "/usr/bin/env wget -t 2 -T 3000 -nv --passive-ftp --no-check-certificate"

# automount
DISTRO_FEATURES_append = " automount"

# util-linux 
IMAGE_INSTALL_append = " util-linux-bash-completion \
                 util-linux-lsblk"

# Enable UART for debug console
ENABLE_UART = "1"

# ALSA
IMAGE_INSTALL_append = " alsa-utils \
             alsa-utils-speakertest \
"

# sudo
IMAGE_INSTALL_append = " sudo"

若干不要なパッケージや設定も含まれているが気にしない。

pulseaudioのbluetoothモジュールのロード設定

/etc/pulse/system.paに下記を追加

### Automatically load driver modules for Bluetooth hardware
.ifexists module-bluetooth-policy.so
load-module module-bluetooth-policy
.endif

.ifexists module-bluetooth-discover.so
load-module module-bluetooth-discover
.endif

今回のケースでは、module-bluetooth-discoverはBluetoothの設定を自動化し module-bluetooth-policyはa2dp_sourceとsinkのloopbackを自動化する。

pulseaudioのシステムサーバ化

/etc/systemd/system/pulseaudio.serviceを下記の内容で作成

[Unit]
Description=PulseAudio system server

[Service]
Type=notify
ExecStart=/usr/bin/pulseaudio --daemonize=no --system --realtime --log-target=journal

[Install]
WantedBy=multi-user.target

サービスの登録、起動

$ systemctl --system enable pulseaudio.service 
$ systemctl --system start pulseaudio.service 

dbus設定

/etc/dbus-1/system.d/pulseaudio-system.confに下記の行を追加

 <busconfig>
                                                                          
   <!-- System-wide PulseAudio runs as 'pulse' user. This fragment is
        not necessary for user PulseAudio instances. -->
                                                                          
   <policy user="pulse">
     <allow own="org.pulseaudio.Server"/>
+    <allow send_destination="org.bluez"/>
+    <allow send_interface="org.bluez.Manager"/>
   </policy>
                                                                     
 </busconfig> 

pactlの使い方

sudo でpulseユーザーを指定して実行する

$ sudo -u pulse pactl list short sinks

blueoothの設定

discoverableを有効にする。

$ connmanctl enable bluetooth
$ bluetoothctl
> power on
> discoverable on
> agent on
> quit

ここまででペアリング可能になる。

このままではAccessDeniedが発生し接続できないが、相手のデバイスをtrustすることで接続できるようになる。

自動化

bluetoothctlではバッチ処理に不向であることと、相手のデバイスをtrustする手順が煩雑になるため、 bluez5-testtoolsに含まれるスクリプトを使用し、ある程度自動化できるようにする。

まずはバグフィックス

$ cd /usr/lib/bluez/test/
$ find -type f | xargs sed -i 's/iteritems/items/g'

次のコマンドを実行する。

$ cd /usr/lib/bluez/test
$ ./test-adapter discoverable on
$ ./simple-agent -c NoInputNoOutput

simple-agentがtrustまで行ってくれるので、bluetoothctlの手順よりも多少は簡略化できる。 これでも、相手とのペアリングのタイミングでプロンプトがでるため、完全にヘッドレスでの運用はできない。 一度、接続が成功し相手の端末に接続設定が保存されてしまえば、ヘッドレス運用もできると思う。

その場合はシステム起動時にsimple-agentを起動し、 GPIOで物理ボタンなどをつけて、押されたタイミングでdiscoverable onを行うなど工夫が必要になる。

内蔵オーディオについて

ラズベリーパイ内蔵のオーディオを使用するためにはsnd_bcm2835ドライバを組み込む必要がある。

SDカードのブートパーティションにあるconfig.txtに下記を追加すると、ブート時にドライバが組み込まれるようになる。

dtparam=audio=on

まとめ

ここまでの設定を行った後で再起動してsimple-agentを立ち上げると、 スマートフォンipodなどとBluetoothで接続して、音源を鳴らすことが出来るようになる。