ラズパイ3 + I2S DAC (ES9023/PCM5102A) + AirPlay オーディオ

ラズベリーパイ は Inter-IC Sound (I2S, 注：I2Cではない）に対応しています。I2S というのは CDプレーヤーの部品などの互換性のためにフィリップス社が作成したディジタルオーディオ用のシリアルバス規格です。I2S は３本の信号線 LRCLK, BCLK, SDATA から構成されており、ラズベリーパイ の GPIO に割り当てられています。

この記事では I2S DAC (Digital-Analog Converter) 基板 とラズパイ3 で AirPlay オーディオを作ります。I2S DAC は ES9023 SABRE と PCM5102A の２つを使い、２つの DAC を配線で切り替えて使えるようにしておきます。青色ブロックの部分を今回は作り、AirPlay で Apple Music のオーディオを自宅のアンプとスピーカーで鳴らします。

![IO.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/a4b4c0c1-2503-fed1-6faa-df29ec56ffd6.png)

使った２種類の I2S DAC は以下の通り：

- [ES9023 SABRE Premier DAC (DIYINHK)](https://www.diyinhk.com/shop/audio-kits/26-24bit192khz-es9023-dac-i2s-input-ultra-low-noise-regulator.html)
- [PCM5102A DAC (DIYINHK)](https://www.diyinhk.com/shop/audio-kits/31-384khz32bit-pcm5102a-dac-i2s-input-ultra-low-noise-regulator.html)

## RaspberryPi OS インストールと初期設定

まずは普通のラズパイOSをインストールして、無線LANなどの初期設定をします。

```
raspberrypi$ sudo raspi-config
```

- 2 Network Options から N2 Wireless LAN を設定
- 3 Boot Options から B1 Desktop/CLI を選び、B1 Console に設定
- 4 Localisation Options で以下に設定
  - Time Zone: Tokyo
  - Keyboard Layout: Generic 105-key PC (Intl.), Japanese (OADG 109A)
- 1 Change User Password でパスワード変更
- 5 Interfacing Options
  - P2 SSH を選び、SSH を有効にする
  - P5 I2C を選び、ARM I2C を有効にする (注：ここは I2Sではなく I2C）
- 7 Advanced Options
  - A1 Expand filesystem を実行

最後にラズパイを reboot して、以降は別マシンから ssh で設定をおこないます。

```
Mac$ ping raspberrypi.local
PING raspberrypi.local (192.168.1.14): 56 data bytes
64 bytes from 192.168.1.14: icmp_seq=0 ttl=64 time=9.146 ms
....
Mac$ ssh pi@raspberrypi.local
...
raspberrypi$ sudo apt-get update; sudo apt-get upgrade
```

## shairport-sync (AirPlay)

[このへん](https://101010.fun/iot/raspberry-pi-airplay-server.html)を参考にオーディオ用AirPlayである shairport-sync をインストールします。（注：shareport ではなく shairport）

```
raspberrypi$ sudo apt-get install git autoconf libdaemon-dev libpopt-dev libconfig-dev libasound2-dev libpulse-dev libavahi-client-dev libssl-dev libsoxr-dev
raspberrypi$ git clone https://github.com/mikebrady/shairport-sync.git
raspberrypi$ cd shairport-sync
raspberrypi$ autoreconf -i -f
raspberrypi$ ./configure --sysconfdir=/etc --with-alsa --with-pa --with-avahi --with-ssl=openssl --with-metadata --with-soxr --with-systemd
raspberrypi$ make; sudo make install
raspberrypi$ sudo service shairport-sync start
raspberrypi$ sudo systemctl enable shairport-sync.service

```

以下のように iPhone から AirPlay サーバとして raspberrypi が表示されます。まだ音声は I2S DAC から再生できません。

![airplay1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/cf9ee634-4062-7469-fe96-aa6dc3bf0ab3.jpeg)

## I2S DAC (ES9023) をラズパイ３に配線する

ラズベリーパイ３に I2S DAC である ES9023 を以下の表のように接続します。ES9023にある青い箱は電源なのでラズパイ3 の 5V (2番ピン）と GND（6番ピン）に接続しました。

![ES9023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/73fc2413-5f3a-9b8d-1468-efafc9c102f2.png)

| ES9023 (I2S DAC) | ラズパイ3 GPIO |
|---|---|
| GND  | 34 (GNDならどこでもOK) |
| SDATA | 40 |
| BCLK  | 12 |
| LRCLK | 35 |

昔の AM/FMレシーバーの箱を使っているため、もともとある R と L の RCA 端子へ ES9023 の出力（Rout, Lout, GND）を接続しています。この記事ではラズパイの DAC で音を再生させて RCA端子の先につながっているアンプ経由でスピーカーから音が鳴るようにします。

![audio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/012f1de2-4409-2b83-38a1-252c1c284143.png)


## I2S DAC の設定

Volumio の SDカードイメージを使えば I2S カーネルドライバなどの面倒な設定は不要で楽なのですが、中身がどう動いているかわからないのと Volumio のユーザインターフェースが若干不安定ため、今回は RaspberryPi OS を使って自分でドライバの設定などをおこないます。

[このへん](http://hoteruaucra.blog.fc2.com/blog-category-3.html)を参考に、I2S のカーネルドライバと対応カーネルをビルドします。

```
$ cd
$ mkdir kernel
$ cd kernel
$ git clone --depth=1 https://github.com/raspberrypi/linux
$ sudo apt-get install bc flex bison libncurses5-dev
$ cd linux
$ KERNEL=kernel7
$ make bcm2709_defconfig
...
$ make menuconfig
```

以下のように、Device Drivers > Sound card support > Advanced Linux Sound Architecture > ALSA for SoC audio support で "SoC Audio support for the Broadcom BCM2835 I2S module" が M、ほかに HifiBerry DAC などがすべて M になっていることを確認し、最後に Exit すれば Makefile ができます。

![kernel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/8b5978a8-a093-6161-3fa6-2391c62de2be.png)

以下のようにカーネルに名前をつけておくと後で uname -a したときに出てきて嬉しいです。

```
$ vi Makefile
EXTRAVERSOIN = RASPI_AUDIO
```

ラズパイ３でカーネルをセルフビルドします。１〜２時間かかりますので置いておきましょう。ラズパイカーネルの公式ビルドマニュアルは[こちら](https://www.raspberrypi.org/documentation/linux/kernel/building.md)だと思います。

```
$ make -j4 zImage modules dtbs
$ sudo make modules_install
$ sudo cp arch/arm/boot/dts/*.dtb /boot/
$ sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
$ sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
$ sudo cp arch/arm/boot/zImage /boot/$KERNEL.img
```

再起動してカーネルが更新されていることを確認します。自分でつけた名前が入っていると少しだけ嬉しいですね。

```
raspberrypi$ uname -a
Linux raspberrypi 5.4.61RASPI_AUDIO-v7+ #1 SMP Sat Sep 5 22:38:37 JST 2020 armv7l GNU/Linux
```

カーネルモジュールもコンパイルされていることを確認します。

```
raspberrypい$ ls /lib/modules/5.4.61RASPI_AUDIO-v7+/kernel/sound/soc/bcm/
snd-soc-allo-boss-dac.ko                     snd-soc-hifiberry-dacplusadcpro.ko
snd-soc-allo-katana-codec.ko                 snd-soc-hifiberry-dacplusdsp.ko
snd-soc-allo-piano-dac.ko                    snd-soc-hifiberry-dacplushd.ko
snd-soc-allo-piano-dac-plus.ko               snd-soc-hifiberry-dacplus.ko
snd-soc-audioinjector-isolated-soundcard.ko  snd-soc-iqaudio-codec.ko
snd-soc-audioinjector-octo-soundcard.ko      snd-soc-iqaudio-dac.ko
snd-soc-audioinjector-pi-soundcard.ko        snd-soc-i-sabre-q2m.ko
snd-soc-audiosense-pi.ko                     snd-soc-justboom-both.ko
snd-soc-bcm2835-i2s.ko                       snd-soc-justboom-dac.ko
snd-soc-digidac1-soundcard.ko                snd-soc-pisound.ko
snd-soc-dionaudio-loco.ko                    snd-soc-rpi-cirrus.ko
snd-soc-dionaudio-loco-v2.ko                 snd-soc-rpi-proto.ko
snd-soc-fe-pi-audio.ko                       snd-soc-rpi-simple-soundcard.ko
snd-soc-googlevoicehat-codec.ko              snd-soc-rpi-wm8804-soundcard.ko
```

起動時に読み込むカーネルモジュールを /etc/modules に記述します。hifiverry_dac は I2S DAC の標準的なドライバなので、私の持っている２つの DAC はどちらも動作しました。

```
raspberrypi$ sudo vi /etc/modules
最後に以下を追記：
snd_soc_bcm2708
snd_soc_bcm2708_i2s
bcm2708_dmaengine
snd_soc_pcm5102a
snd_soc_hifiberry_dac
```

ラズパイのファームウェアを更新します（必要かどうか不明）。

```
sudo rpi-update
```

カーネルドライバを読み込ませる設定をします。

```
sudo vi /boot/config.txt 
最後に以下を追記：
dtoverlay=hifiberry-dac
```

reboot して I2S DAC が認識されていることを確認します。

```
raspberrypi$ aplay -l
**** List of PLAYBACK Hardware Devices ****
card 0: Headphones [bcm2835 Headphones], device 0: bcm2835 Headphones [bcm2835 Headphones]
....
card 1: sndrpihifiberry [snd_rpi_hifiberry_dac], device 0: HifiBerry DAC HiFi pcm5102a-hifi-0 [HifiBerry DAC HiFi pcm5102a-hifi-0]
  Subdevices: 1/1
  Subdevice #0: subdevice #0
```

I2S DAC から音を再生できることを確認します。RCA端子の先につながっているアンプ経由でスピーカーから音が鳴りました！

```
raspberrypi$ wget https://sound.jp/musicyou/s/Peak_test_A.wav
raspberrypi$ aplay -D hw:1,0 Peak_test_A.wav
```

このままだと airplay (shairport-sync) から I2S DAC で再生されないので、以下のように設定を変更して、shairport-sync サービスを再起動します。

```
raspberrypi$ sudo vi /etc/shairport-sync.conf
以下を変更する：
alsa =
{
        output_device = "plughw:1,0";
...
raspberrypi$ sudo service shairport-sync restart
```

iPhone の Apple Music から再生できました！

![IMG_F5FB720FC235-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/635b1dac-1c32-d638-88a7-b3e3628ef91f.jpeg)

参考にさせていただいた[この記事]（http://hoteruaucra.blog.fc2.com/blog-category-3.html) によると I2S DAC 以外を無効にすると音が良くなるらしい...?? ので念のため設定します。

```
raspberrypi$ sudo vi /etc/modprobe.d/raspi-blacklist.conf
以下を追記：
blacklist snd_bcm2835
```

このあと reboot するとデバイス番号が変更になるので、以下のように shairport-sync の設定も直しておきます。

```
raspberrypi$ sudo vi /etc/shairport-sync.conf
以下を変更する：
alsa =
{
        output_device = "plughw:0,0";
...
raspberrypi$ sudo service shairport-sync restart
```

音は良くなった.....かもしれません。

## 別の DAC (PCM5102A) に切り替えてみる

ラズパイをシャットダウンさせ、I2S DAC を ES9023 から PCM5102A に変えてみます。GPIO、オーディオ出力の配線を変えるだけです。ラズパイの設定はそのままです。個人的には ES9023 のほうが少しだけクラシック向きの暖かい音のように感じますが個人差程度かもしれません。

![PCM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/211d510c-48e9-21f0-4a75-aa5888c0aa63.png)


## LCD をつける

[WayinTop 1602 LCD ディスプレイモジュール](https://www.amazon.co.jp/WayinTop-ディスプレイモジュール-16×2キャラクタ-バックライト付き-シリアルインターフェイス/dp/B07WTMXRTQ/ref=sr_1_7?__mk_ja_JP=カタカナ&dchild=1&keywords=LCD+I2C&qid=1599381493&sr=8-7)を600円で購入しました。I2Cインターフェースが付いているので配線が少なくて済みます。

紛らわしいですが、LCD は I2S (Inter-IC Sound) ではなく I2C (Inter-Integrated Circuit) で接続します。I2C もフィリップスが開発したシリアルバスで、こちらはオーディオだけでなく汎用的なシリアルバスだそうです。

[このへん](https://qiita.com/shmiki/items/472d970ab0269169ed7e) を参考にして、以下のサンプルプログラムを改造し時計を表示させるようにしました。

```
raspberrypi$ wget http://osoyoo.com/driver/i2clcda.py
raspberrypi$ diff print-lcd.py i2clcda.py
10d9
< import datetime
91,92d89
<     dt_now = datetime.datetime.now()
< 
94,95c91,92
<     lcd_string( dt_now.strftime('%m/%d %H:%M.%S'),LCD_LINE_1)
<     lcd_string("AirPlay service",LCD_LINE_2)
---
>     lcd_string("Created by         <",LCD_LINE_1)
>     lcd_string("Osoyoo.com        <",LCD_LINE_2)
97c94
<     time.sleep(1)
---
>     time.sleep(3)
98a96,101
>     # Send some more text
>     lcd_string("> Tutorial Url:",LCD_LINE_1)
>     lcd_string("> http://osoyoo.com",LCD_LINE_2)
> 
>     time.sleep(3)
> 
```

シェルスクリプトにします。

```
raspberrypi$ vi print-lcd.sh
#!/bin/bash
python /home/pi/print-lcd.py
...
raspberrypi$ chmod 755 print-lcd.sh
```

サービスに登録します。

```
$raspberrypi$ sudo vi /lib/systemd/system/print-lcd.service
[Unit]
Description=Print characters on LCD
After=shairport-sync.target network-online.target multi-user.target

[Service]
ExecStart=/home/pi/print-lcd.sh
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target
```

サービスを実行、登録します。

```
$raspberrypi$  sudo systemctl daemon-reload
$raspberrypi$  sudo systemctl start print-lcd
$raspberrypi$  sudo systemctl enable print-lcd
```

ラズパイを再起動して自動的に時刻が表示されるようになりました。

![IMG_3468.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/277dc486-0d58-a7ee-e79e-4028beb6c20e.jpeg)

## おまけ：通信速度を LCD に表示させてみる

LCD に CLI の speedtest コマンドで取得した通信速度を表示させてみます。１行１６文字になるように sed で文字列をすこし加工しています。

```
sudo pip install speedtest-cli 
```

たとえば cron で定期的に以下のシェルスクリプトを実行させることができます。

```
raspberrypi$ cat speed-cron.sh
speedtest-cli > /tmp/speedtest.log
python printlcd.py "`grep Download /tmp/speedtest.log|sed -s 's/load//g'|sed -s 's/it\//p/g'`" "`grep Upload /tmp/speedtest.log|sed -s 's/load//g'|sed -s 's/it\//p/g'`" 10
```

![IMG_3467.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550398/e02c86a7-3349-16f6-4237-2a39bfe6b4f3.jpeg)

<!--

IPv6 を無効化

```
raspberrypi$ vi /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
raspberrypi$ sudo sysctl -p
```


## shairport メタデータを取得する

/etc/shairport-sync.conf を開き、metadata セクションの enabled、pipe_name、pipe_timeout の行の先頭の // を削除します。

```
...
metadata =
{
      enabled = "yes"; // set this to yes to get Shairport Sync to solicit met
...
        pipe_name = "/tmp/shairport-sync-metadata";
        pipe_timeout = 5000; // wait for this number of milliseconds for a blocked pipe to unblock before giving up
...
```

shairport を再起動して、/tmp/shairport-sync-metadata の中身を確認します。

```
sudo systemctl restart shairport-sync
cat /tmp/shairport-sync-metadata
```
-->