みつきんのメモ

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

raspberrypi2でMPDサーバ

raspberrypi2+DACでMPDサーバを作成する。

今回は作成済みレイヤの使用方法ではなく、レイヤを作成する方法をまとめてみる。 作成済みレイヤの使用方法はこちら

機能

  • windows10で共有しているフォルダから音楽ファイルを参照
  • MPDサーバで音楽ファイルを再生
  • SYSTEMDを使用
  • ネットワーク設定にはconnmanを使用

DAC

今回はこのDACを使用する。

システム構成

ネットワークを含めたシステムの構成を 図 1 に示す。

f:id:mickey_happygolucky:20160524012104p:plain

図 1:システム構成

yocto環境

作業に使用するレイヤのブランチとバージョンを 表 1 に示す。

表 1: レイヤのブランチとバージョン

レイヤ ブランチ バージョン URL
poky krogoth 2.1 git://git.yoctoproject.org/poky.git
meta-openembedded krogoth 2.1 git://git.openembedded.org/meta-openembedded
meta-raspberrypi master - git://git.yoctoproject.org/meta-raspberrypi
meta-hifiberry-rpi master - https://github.com/mickey-happygolucky/meta-hifiberry-rpi.git

作業ディレクトリ

作業は~/work/yocto/rpiで行う。

ディレクトリの構成

\~/work/yocto/rpi以下の構成を以下に示す。

.
├── build
└── poky
    ├── meta-openembedded
    ├── meta-raspberrypi
    └── meta-hifiberry-rpi

poky以下は追加するレイヤのみ記載している。

ベース環境の作成

ベース環境の手順を示す。

  1. 作業ディレクトリ(\~/work/yocto/rpi)の作成
  2. pokyのダウンロード
  3. meta-openembeddedのダウンロード
  4. meta-raspberrypiのダウンロード
  5. meta-hifiberryp-rpiのダウンロード

下記のように実行する。

$ mkdir mkdir -p ~/work/yocto/rpi && cd ~/work/yocto/rpi
$ git clone git://git.yoctoproject.org/poky.git -b krogoth && cd poky
$ git clone git://git.openembedded.org/meta-openembedded -b krogoth
$ git clone git://git.yoctoproject.org/meta-raspberrypi
$ git clone https://github.com/mickey-happygolucky/meta-hifiberry-rpi.git

MPD用レイヤ

MPDサーバ向けのレイヤとしてmeta-mpd-serverを作成する。

下記のコマンドを実行し作業用のディレクトリを作成する。

$ mkdir -p ~/work/yocto/rpi/poky/meta-mpd-server

layer.conf

レイヤとしてbitbakeに認識させるためにはlayer.confが必要になる。

layer.confはmeta-mpd-server/conf以下に配置される必要がある。

下記の要領でまず、空のlayer.confファイルを作成する。

$ mkdir -p ~/work/yocto/rpi/poky/meta-mpd-server/conf
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/conf/layer.conf

layer.confをエディタで開き コード 1 の内容にに編集し、保存する。

コード 1: layer.conf
# We have a conf and classes directory, append to BBPATH
BBPATH .= ":${LAYERDIR}"

# We have a recipes directory containing .bb and .bbappend files, add to BBFILES
BBFILES += "${LAYERDIR}/recipes*/*/*.bb \
            ${LAYERDIR}/recipes*/*/*.bbappend"

BBFILE_COLLECTIONS += "mpd-server"
BBFILE_PATTERN_mpd-server := "^${LAYERDIR}/"
BBFILE_PRIORITY_mpd-server = "10"

LAYERDEPENDS_mpd-server = "raspberrypi"

LAYERDEPENDS_mpd-server = "raspberrypi"の行で、meta-mpd-serverがmeta-raspberrypiに依存していることを明示している。

この時点でmeta-mpd-serverは以下のような構成になる。

./meta-mpd-server
└── conf
    └── layer.conf

ネットワーク設定

今回はサーバマシンなので、IPアドレスを固定で割り振りたい。 OSイメージがbitbakeで生成された時点で静的なIPアドレスを設定されるようにレシピを作成する。

レシピ名はconnman-ipv4-conf.bbとする。レシピは役割ごとに分類されたフォルダに配置する。connman-ipv4-conf.bbは以下のように配置する。

recipes-connectivity
└── connman
    └── connman-ipv4-conf.bb

下記のようにコマンドを実行し空のレシピファイルを作成する。

$ mkdir -p ~/work/yocto/rpi/poky/meta-mpd-server/recipes-connectivity/connman
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/recipes-connectivity/connman/connman-ipv4-conf.bb

エディタでconnman-ipv4-conf.bbを開き、コード 2 の内容に編集し保存する。

コード 2: connman-ipv4-conf.bb
SUMMARY = "Connman config to static IPv4 address setup wired interface"
DESCRIPTION = "ConnMan configuration to set up to Wired network interface"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

FILES_${PN} = "${localstatedir}/*"

#                IP Address     / subnet mask
# CONNMAN_IPv4 = xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx

IPv4CFGF = "${D}${localstatedir}/lib/connman/wired.config"

do_install() {
    if [ -n "${CONNMAN_IPv4}" ] ; then
    install -d ${D}${localstatedir}/lib/connman

    echo "[global]" > ${IPv4CFGF}
    echo "Name = Wired" >> ${IPv4CFGF}
    echo "Description = Wired network configuration" >> ${IPv4CFGF}
    echo "" >> ${IPv4CFGF}
    echo "[service_ether]" >> ${IPv4CFGF}
    echo "Type = ethernet" >> ${IPv4CFGF}
    echo "IPv4 = ${CONNMAN_IPv4}" >> ${IPv4CFGF}
    
    if [ -n "${CONNMAN_IPv4_NS}" ] ; then
        echo "Nameservers = ${CONNMAN_IPv4_NS}" \
         >> ${D}${localstatedir}/lib/connman/wired.config
    fi
    else
    install -d ${D}${localstatedir}/lib/connman
    touch ${D}${localstatedir}/dummy
    fi
}

このレシピによって、RPi2上に/lib/connman/wired.configが作成され、有線接続のネットワーク設定が行われる。

固定IPアドレスを指定するには、local.confに下記のように記述する。

CONNMAN_IPv4 = 192.168.1.100/255.255.255.0

この例ではIPアドレス192.168.1.100、サブネットマスク255.255.255.0に設定している。

共有フォルダ設定

Windows 10の共有フォルダ

Windows10の共有フォルダをマウントするには下記のようにする。

$ mount -t cifs -ousername=USERNAME,password=PASSWORD,vers=3.0 //machine_name/shared_folder /mnt/point

ポイントはvers=3.0で、これをオプションに指定しないと、下記のようにエラーになってしまう。

mount: mounting //machine_name/shared_folder on /mnt/point failed: Input/output error

vers=の指定は、jethroのcifs-utilsでは認識されないため、少なくともWindows10の共有フォルダをマウントしたい場合は、krogothかmasterを使用する必要がある。

Windows8については筆者の環境がないため試せないが、もしかすると状況は同じかもしれない。 Windows7に関してはvers=は必要なかった。

共有フォルダ設定レシピ

OSイメージがbitbakeで生成された時点で指定の共有フォルダがマウントされるようにレシピを作成する。

レシピ名はcifs-mount-networkd.bbとする。

cifs-mount-networkd.bbは以下のように配置する。

recipes-connectivity
└── samba
    └── cifs-mount-networkd.bb

下記のようにコマンドを実行し空のレシピファイルを作成する。

$ mkdir -p ~/work/yocto/rpi/poky/meta-mpd-server/recipes-connectivity/samba
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/recipes-connectivity/samba/cifs-mount-networkd.bb

エディタでcifs-mount-networkd.bbを開き、コード 3 の内容に編集し保存する。

コード 3: cifs-mount-networkd.bb
SUMMARY = "Mount CIFS at boot"
DESCRIPTION = "Generate a mount unit file to mount remote via cifs at boot."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

inherit systemd

MOUNT_NAME = "${@ '${CIFS_MOUNT_DIR}'.replace('/', '-')[1:]}"
CIFS_MOUNT_UNIT = "${D}${sysconfdir}/systemd/system/${MOUNT_NAME}.mount"

SYSTEMD_SERVICE_${PN} = "${MOUNT_NAME}.mount"

do_install() {
    if [ -n "${CIFS_SHARED_DIR}" -a -n ${CIFS_MOUNT_DIR} ] ; then
        install -d ${D}${sysconfdir}/systemd/system

    if [ ${CIFS_SEC_NTLM} = "1" ] ; then
       SEC=",sec=ntlm"
    else
       SEC=""
    fi

    if [ -n ${CIFS_VERS} ] ; then
       VERS=",vers=${CIFS_VERS}"
    else
       VERS=""
    fi

    echo "[Unit]" > ${CIFS_MOUNT_UNIT}
    echo "Description=Mount Share at boot" >> ${CIFS_MOUNT_UNIT}
    echo "Requires=connman.service" >> ${CIFS_MOUNT_UNIT}
        echo "After=connman-wait-online.service" >> ${CIFS_MOUNT_UNIT}
    echo "" >> ${CIFS_MOUNT_UNIT}
    echo "[Mount]" >> ${CIFS_MOUNT_UNIT}
    echo "What=${CIFS_SHARED_DIR}" >> ${CIFS_MOUNT_UNIT}
    echo "Where=${CIFS_MOUNT_DIR}" >> ${CIFS_MOUNT_UNIT}
    echo "Options=username=${CIFS_USER},password=${CIFS_PASSWD},iocharset=utf8,rw,x-systemd.automount${SEC}${VERS}" >> ${CIFS_MOUNT_UNIT}
    echo "Type=cifs" >> ${CIFS_MOUNT_UNIT}
    echo "TimeoutSec=30" >> ${CIFS_MOUNT_UNIT}
    echo "" >> ${CIFS_MOUNT_UNIT}
    echo "[Install]" >> ${CIFS_MOUNT_UNIT}
    echo "WantedBy=multi-user.target" >> ${CIFS_MOUNT_UNIT}
    fi
}

共有フォルダをマウントするにはlocal.confに下記のように記述する。

CIFS_SHARED_DIR = "//machine_name/shared_folder"
CIFS_MOUNT_DIR = "/mnt/point"
CIFS_USER = "USERNAME"
CIFS_PASSWD = "PASSWORD"
CIFS_VERS = "3.0"

CIFS_VERSによりオプションにvers=を設定する。

cifs-mount-networkd.bbでは、複数の共有フォルダのマウントを指定することはできない。

MPD設定

MPDのサウンドデバイスの設定を既存から、DAC向けに変更する必要がある。 そのためにはmpd.confの記述を変更する必要があるが、mpd.confのインストールはmpd_0.19.10.bbで行っている。

bbappendを使用すると既存のレシピを直接変更せずに挙動を変更することができる。 ここではmpd_%.bbappendを作成する。%はバージョンをワイルドカード指定している。

bbappendは元になるbbと同じ構成になるように配置する。

mpd_%.bbappendは以下のようになる。

recipes-multimedia
└── musicpd
    └── mpd_%.bbappend

下記のようにコマンドを実行し空のレシピファイルを作成する。

$ mkdir -p ~/work/yocto/rpi/poky/meta-mpd-server/recipes-multimedia/musicpd
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/recipes-multimedia/musicpd/mpd_%.bbappend

エディタでmpd_%.bbappendを開き、コード 4 の内容に編集し保存する。

コード 4: mpd\_%.bbappend
SYSTEMD_SERVICE_${PN}_remove = "mpd.socket"
FILES_${PN} += "${systemd_unitdir}/system/mpd.socket"

PACKAGECONFIG_append = " id3tag"

do_install_append() {
    if [ "${HIFIBERRY_DIGI}" = "1" ] ; then
       echo 'audio_output {' >> ${D}/${sysconfdir}/mpd.conf
       echo '        type            "alsa"' >> ${D}/${sysconfdir}/mpd.conf
       echo '        name            "MyAlsa"' >> ${D}/${sysconfdir}/mpd.conf
       echo '        device          "hw:0,0"' >> ${D}/${sysconfdir}/mpd.conf
       echo '        mixer_control   "Digital"' >> ${D}/${sysconfdir}/mpd.conf
       echo '}' >> ${D}/${sysconfdir}/mpd.conf
    fi
    if [ "${HIFIBERRY_DAC}" = "1" -o "${HIFIBERRY_DACPLUS}" = "1" ] ; then
       echo 'audio_output {' >> ${D}/${sysconfdir}/mpd.conf
       echo '        type            "alsa"' >> ${D}/${sysconfdir}/mpd.conf
       echo '        name            "MyAlsa"' >> ${D}/${sysconfdir}/mpd.conf
       echo '        device          "hw:0,0"' >> ${D}/${sysconfdir}/mpd.conf
       echo '}' >> ${D}/${sysconfdir}/mpd.conf
    fi
}

PACKAGECONFIG_append = " id3tag"の行がないとmp3ファイルが正しくDBに登録されない。 これは、mp3タグの解析が有効にならないため。

音源ディレクトリの設定

mpdはmpd.confのmusic_directoryで指定されたディレクトリ以下にある音楽ファイルを再生する。

このディレクトリをマウント済みのWindowsの共有フォルダを参照する。 mpd_0.19.bbにより、music_directoryは/var/lib/mpd/musicに設定されている。

今回は共有フォルダをマウントしている/mnt/nasシンボリックリンク/var/lib/mpd/musicに作成するアプローチを取る。

そのためのレシピmpd-musicdir_0.1.bbを作成する。

レシピの構成

レシピの構成は以下のようになる。

recipes-multimedia
└── musicpd
    ├── mpd-musicdir
    │   ├── mpd-musicdir.service
    │   └── replace-mpd-musicdir.sh
    └── mpd-musicdir_0.1.bb

シンボリックリンクの作成はreplace-mpd-musicdir.shで行う。 そして、システム起動時にmpd-musicdir.serviceにより、replace-mpd-musicdir.shを実行するようにしている。

下記の要領で空のファイルを作成する。

$ mkdir -p ~/work/yocto/rpi/poky/meta-mpd-server/recipes-multimedia/musicpd/mpd-musicdir
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/recipes-multimedia/musicpd/mpd-musicdir_0.1.bb
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/recipes-multimedia/musicpd/mpd-musicdir/mpd-musicdir.service
$ touch ~/work/yocto/rpi/poky/meta-mpd-server/recipes-multimedia/musicpd/mpd-musicdir/replace-mpd-musicdir.sh

まず、エディタでmpd-musicdir_0.1.bbを開き コード 5 の内容に編集、保存する。

コード 5: mpd-musicdir\_0.1.bb
SUMMARY = "Replace mpd music directory."
DESCRIPTION = "Generate a unit file to replace music directory."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

inherit systemd

RDPENDS = "mpd"

SRC_URI = "file://mpd-musicdir.service \
       file://replace-mpd-musicdir.sh \
"

FILES_${PN} = "${sysconfdir}/systemd/system/* \
           ${bindir}/* \
"

CIFS_MOUNT_NAME = "${@ '${CIFS_MOUNT_DIR}'.replace('/', '-')[1:]}"
MPD_MUSICDIR_UNIT = "${D}${sysconfdir}/systemd/system/mpd-musicdir.service"
SYSTEMD_SERVICE_${PN} = "mpd-musicdir.service"

do_install() {
    if [ -n "${MPD_MUSIC_DIR}" ] ; then
       install -d ${D}${sysconfdir}/systemd/system
       install -m 0644 ${WORKDIR}/mpd-musicdir.service ${D}${sysconfdir}/systemd/system
       if [ -n "${CIFS_MOUNT_NAME}" ] ; then
          sed -i 's|Before=mpd.service|Before=mpd.service\nAfter=${CIFS_MOUNT_NAME}.mount|' \
          ${MPD_MUSICDIR_UNIT}
       fi

       install -d ${D}${bindir}
       install -m 0755 ${WORKDIR}/replace-mpd-musicdir.sh ${D}${bindir}
       sed -i -e 's|MPD_MUSIC_DIR_SRC=""|MPD_MUSIC_DIR_SRC="${MPD_MUSIC_DIR}"|' \
           ${D}${bindir}//replace-mpd-musicdir.sh
    fi
}

次に、mpd-musicdir.serviceをエディタで コード 6 の内容に編集する。

コード 6: mpd-musicdir.service
[Unit]
Description="Replace MPD Musid Directory"
Before=mpd.service

[Service]
Type=oneshot
ExecStart=/usr/bin/replace-mpd-musicdir.sh

[Install]
WantedBy=multi-user.target

最後に、replace-mpd-musicdir.shをエディタで コード 7 のように編集する。

コード 7: replace-mpd-musicdir.sh
#!/bin/sh

MPD_MUSIC_DIR_SRC=""
MPD_MUSIC_DIR_DST="/var/lib/mpd/music"

if [ -d ${MPD_MUSIC_DIR_DST} ] ; then
    rmdir ${MPD_MUSIC_DIR_DST}
fi

if [ -d "${MPD_MUSIC_DIR_SRC}" ] ; then
    ln -s "${MPD_MUSIC_DIR_SRC}" "${MPD_MUSIC_DIR_DST}"
else
    echo "${MPD_MUSIC_DIR_SRC} is not directory."
    exit 1
fi

echo "done."

bitbakeの準備

OSイメージを作成するためにbitbakeを実行するが、その前に書きコマンドでbitbakeを実行するために必要な設定を行う。

$ cd ~/work/yocto/rpi
$ source poky/oe-init-build-env build

実行後、buildディレクトリが生成され、自動的に移動される。

生成されたbuildディレクトリは以下のような構成になっている。

./build
└── conf
    ├── bblayers.conf
    ├── local.conf
    └── templateconf.cfg

conf以下のファイルは自動生成されたもので、この内、bblayers.confとlocal.confは修正する必要がある。

bblayers.conf

bblayers.confはbitbakeがビルド対象とするレイヤを定義する。

MPDサーバをビルドするため、自動生成されたbblayers.confを コード 8 の内容にまるごと置き換える。

コード 8: bblayers.conf
# POKY_BBLAYERS_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
POKY_BBLAYERS_CONF_VERSION = "2"

BBPATH = "${TOPDIR}"
BBFILES ?= ""

LAYERSTOP = "${TOPDIR}/.."

BBLAYERS ?= " \
  ${LAYERSTOP}/poky/meta \
  ${LAYERSTOP}/poky/meta-poky \
  ${LAYERSTOP}/poky/meta-yocto-bsp \
  ${LAYERSTOP}/poky/meta-raspberrypi \
  ${LAYERSTOP}/poky/meta-mpd-server \
  ${LAYERSTOP}/poky/meta-hifiberry-rpi \
  ${LAYERSTOP}/poky/meta-openembedded/meta-oe \
  ${LAYERSTOP}/poky/meta-openembedded/meta-python \
  ${LAYERSTOP}/poky/meta-openembedded/meta-networking \
  ${LAYERSTOP}/poky/meta-openembedded/meta-multimedia \
  "

local.conf

local.confはターゲットマシンの設定やインストールするレシピの追加、その他様々な設定を記述することができる。

通常は、自動生成されたものにマシンの設定とレシピの追加を行うことが多い。

自動生成されたlocal.confの先頭の方に コード 9 の内容を追加する。

コード 9: local.conf(抜粋)
MACHINE ?= "raspberrypi2"
BB_NUMBER_THREADS ?= "${@oe.utils.cpu_count()}"
PARALLEL_MAKE ?= "-j ${@oe.utils.cpu_count()}"
GPU_MEM = "128"
DL_DIR ?= "${HOME}/work/yocto/downloads"

# use systemd instead of sysvinit
DISTRO_FEATURES_append = " systemd"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

# # systemd-timesyncd
NTP_SERVER = "ntp.nict.jp"

## cifs mount
IMAGE_INSTALL_append = " cifs-utils cifs-mount-networkd"
CIFS_SHARED_DIR = "//192.168.1.60/Music"
CIFS_MOUNT_DIR = "/mnt/nas"
CIFS_USER = "USERNAME"
CIFS_PASSWD = "PASSWORD"
CIFS_VERS = "3.0"

# mpd
LICENSE_FLAGS_WHITELIST = "commercial"
IMAGE_INSTALL_append = " mpd"

#  connman
IMAGE_INSTALL_append = " connman \
                         connman-wait-online \
                         connman-ipv4-conf \
"

# static IPv4 address for connman
CONNMAN_IPv4 = "192.168.1.55/255.255.255.0"
CONNMAN_IPv4_NS = "192.168.1.1"

# Enable I2C
ENABLE_I2C = "1"

# DAC S/PDIF
HIFIBERRY_DIGI = "1"

# MPD Music Directory
IMAGE_INSTALL_append = " mpd-musicdir"
MPD_MUSIC_DIR = "/mnt/nas"

# misc
IMAGE_INSTALL_append = " alsa-utils file"

bitbakeの実行

下記の要領で、bitbakeを実行する。

$ cd ~/work/yocto/rpi/build
$ bitbake rpi-basic-image

下記のディレクトリに、OSイメージファイルrpi-basic-image-raspberrypi2.rpi-sdimgが生成される。

build/tmp/deploy/images/raspberrypi2

イメージの書き込み

ddコマンドでrpi-basic-image-raspberrypi2.rpi-sdimgをSDカードに書き込む。

sudo dd if=tmp/deploy/images/raspberrypi2/rpi-basic-image-raspberrypi2.rpi-sdimg of=/dev/sdb bs=40M

起動

イメージを書き込んだSDカードをRPi2に挿入し電源をONする。

connman-wait-online.serviceについて

本来であれば、connman.incの下記の行によってサービスがenableされ、システム起動時に実行されるようになるはずだが、

SYSTEMD_SERVICE_${PN}-wait-online = "connman-wait-online.service"

現状では、おそらくpokyの不具合によりdisabledの状態になっている。

そのため、下記のようにして、connman-wait-online.serviceをenableする必要がある。

$ systemctl enable connman-wait-online.service
$ sync

connman-wait-online.serviceが実行されないと起動時の共有フォルダのマウントに失敗するため、 MPDも意図したとおり音楽ファイルを参照できない。

connman-wait-online.serviceをenableしたあと、RPi2を再起動する。

接続

クライアントはAndroid端末からMPDroidで接続する。