2019年12月18日水曜日

ラズベリーパイでradikoラジオをつくる: その8 AirPlayストリーミング

shairport-syncをインストール

タイトルとは矛盾しますが、外部入力くらい付けても良いよね、ということでAppleのAirPlay対応を試してみることにしました。これは、iPhoneやitunesからWi-Fi接続のスピーカに見えるというものです。shairport-syncが有名で、様々なサイトで紹介されています。何をやっているのかよくわかりませんので、手順のみ列挙します。

必要なパッケージをインストール
$ sudo apt-get update
$ sudo apt-get install -y build-essential git xmltoman autoconf automake libtool libdaemon-dev libasound2-dev libpopt-dev libconfig-dev avahi-daemon libavahi-client-dev libssl-dev libsoxr-dev
gitからshairport-syncをクローンしてビルド。
$ git clone https://github.com/mikebrady/shairport-sync.git
$ cd shairport-sync
$ autoreconf -i -f   ・・・数分かかります
$ ./configure --sysconfdir=/etc --with-alsa --with-avahi --with-ssl=openssl --with-metadata --with-soxr --with-systemd
$ make
$ sudo make install
shairport-syncの自動起動を設定
$ sudo systemctl enable shairport-sync

shairport-sync.conf を編集します。設定項目がコメントアウトされているので、適宜修正して有効にします。
$ sudo nano /etc/shairport-sync.conf
general = {
name = "IP Radio Receiver";
//interpolation = "soxr";
ignore_volume_control = "yes";
nameは、iPhone等に表示されるデバイス名です。interpolationは、サンプルレート変換に使用するソフトウエアで soxr は処理が重い代わりに音質が良いとされています。私は有効にせず、デフォルトの basic で行くことにしました。ignore_volume_control = "yes"で iPhone等でのボリューム設定を無視します。

shairport-syncサービスを起動
$ sudo systemctl start shairport-sync
systemctl のステータスを表示
$ sudo systemctl status shairport-sync

iTunesから鳴らしてみる

アプリの上部のアイコンをクリックすると、IP Radio Receiverが表示されました。レ点を入れ、再生するとRadiko音声とミックスされて音楽が聞こえてきます。iPadからもあっさりと動作してくれて拍子抜けでしたね。
iTunesの再生ボタンを押してから音が出るまで2秒ほど遅延があります。Wi-Fiの品質にシビアのようで、弱電界では途切れたり再生が停止してしまします。ラズパイのWi-Fiは2.4GHzなので混信の影響も大きいのだろうと考えています。



2019年12月14日土曜日

ラズベリーパイでradikoラジオをつくる: その7 radikoの再生停止問題

スピーカを接続

Auratoneに接続してみました。スピーカのエッジはぼろぼろなのに、まともな音で鳴ってくれます。エッジ交換したら良くなるんですかね。初めて使ったD級パワーアンプ、好印象です。


radiko再生停止問題

radikoを起動後1分ほどで再生が停止、手動でもういちど再生するとその後は安定するという現象がありましたが。この開発を進めるうち、いつの間にやら再生開始から1分ほどで停止するのを繰り返すようになってしまいました。苦し紛れで、自動的に再接続する仕組みを実装しましたけど、1分毎に10秒以上無音になるようでは、最早使い物になりません。調べてみたところ、mplayer のキャッシュを設定すれば改善できるという情報があり早速試したところ解決することができました。

mplayer の設定は、/etc/mplayer/mplayer.conf にあります。こんな内容です。
# Use 8MB input cache by default.
#cache = 8192
#
# Prefill 20% of the cache before starting playback.
#cache-min = 20.0
mplayer.conf のデフォルトはキャッシュ無しの設定でした。そこでキャッシュを設定したところ256kBで安定して再生できることを確認しました。RadikoのPC向け配信ビットレートはHE-AAC 48kbpsということからキャッシュ256kBは約40秒に相当します。家庭内の光ファイバ環境ならこれで十分ではないでしょうか。とはいえキャッシュを増やせば、再生開始までの時間が増えてしまうはず。
  • 設定なし: 再生まで14秒、1分ほどで再生が停止
  • cache = 256 :再生まで14秒、その後も途切れない
  • cache = 512 :再生まで16秒、その後も途切れない
以上の結果から、256kBが妥当と考えました。cache-minは、0から20まで変えてみましたが、再生開始までの時間に大きな変化はありません。光回線の速度が十分に速いためでしょう。ところで再生まで14秒とは、時間かかりすぎです。以前は10秒程度だったはず。ラジコプレミアム対応のスクリプトをコマンドで手動実行して時間を測ってみました。

  • 初回: 13秒
  • 2回目以降: 8秒
初回よりも2回目以降はより早く再生開始します。これは認証情報を使い回しているからと考えています。この初回時の時間と、操作ボタンでの再生開始までの時間は、ほぼ一致します。ということは、操作ボタンでは初回扱いとして動作しているに違いありません。Pythonのsubprocess.Popen()でスクリプトを起動しているせいかなあ。

ところで、開発を進めるうちに再生停止が顕在化する原因がわかりました。なんと、操作スクリプトがCPU使用率を圧迫していたのです。python /home/pi/operation_panel/panel.pyというのがそれで63.8%もあります。radikoの再生をしている mplayer と合わせてCPU使用率 90%弱とほぼ目一杯使い切っています。


操作スクリプトがそんなに重いのは不自然です。とりあえず、対症療法でwaitを入れると、10%程に下がりました。それでもSSHログインやpsコマンドを打ち込むだけでも一瞬音が途切れます。操作スクリプトの見直しが必要ですね。


2019年12月10日火曜日

ラズベリーパイでradikoラジオをつくる: その6 Wi-Fiを自動切替・信号強度表示

登録済みのWi-Fiを自動的に切り替え

自宅や外出先で使用することを想定して、Wi-Fiの自動切り替えを設定してみます。
まずは、wpa_supplicant.confにアクセスポイントを設定を追加して行きます。
$ sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
 次のように、3つのアクセスポイントを設定してみました。関連する設定を抜粋して示します。ssidはSSID名、pskはパスフレーズ、priorityは接続優先度で、値が大きいssidに優先的に接続します。なお、パスフレーズの暗号化やSSIDのステルス機能を使うには更に設定が必要です。
network={
ssid="aterm-00000"
psk="abcd0000"
priority=2
}
network={
ssid="MOBILErouter"
psk="abcd1111"
priority=3
}
network={
ssid="Android1234"
psk="abcd2222"
priority=1
}
さらにIPアドレスを固定する場合は dhcpcd.conf を設定します。
$ sudo nano /etc/dhcpcd.conf
IPアドレスを固定したいアクセスポイントのみ次のように記載します。モバイルルータやスマホのテザリングでは自動割り当てにしたいので何も記載しません。
interface wlan0
ssid aterm-00000
static ip_address=192.168.0.100/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

Wi-Fi IPアドレス表示

Linuxの「ip」コマンドでwlan0の情報を取得しました。
$ ip addr show dev wlan0
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.100/24 brd 192.168.0.255 scope global noprefixroute wlan0
       valid_lft forever preferred_lft forever
    inet6 fe80::6f54:0000:0000:0000/64 scope link
       valid_lft forever preferred_lft forever
さらにawkでIPアドレスを抽出します
$ ip addr show dev wlan0 | awk '/inet /{printf substr($2, 1, index($2, \"/\")-1)}'
192.168.0.100
ところで、ipコマンドで送受信バイト数が表示出来ますから、この数値を使ってリアルタイムのデータ転送速度を表示出来そうです。
$ ip -s link show dev wlan0
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DORMANT group default qlen 1000
    link/ether b8:27:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    RX: bytes  packets  errors  dropped overrun mcast 
    1935131    3444     0       0       0       1527 
    TX: bytes  packets  errors  dropped carrier collsns
    177144     1401     0       0       0       0   
   

Wi-Fi 信号強度とSSID表示

Linuxの「iwconfig」コマンドを利用します。
$ iwconfig wlan0
wlan0     IEEE 802.11  ESSID:"aterm-00000" 
          Mode:Managed  Frequency:2.442 GHz  Access Point: 00:3A:9D:91:D3:94 
          Bit Rate=65 Mb/s   Tx-Power=31 dBm 
          Retry short limit:7   RTS thr:off   Fragment thr:off
          Power Management:on
          Link Quality=70/70  Signal level=-38 dBm
          Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:1  Invalid misc:0   Missed beacon:0
信号強度、ESSIDもawkで抽出します。ESSIDは二重引用符で囲まれたものを抽出したかったのですが、苦し紛れで文字数数えています。
$ iwconfig wlan0 | awk '/Signal level/{print substr($4,7)}'
$ iwconfig wlan0 | awk '/ESSID/{printf substr($NF, 8, length($NF)-8)}'

Wi-Fi 信号強度インジケータの実装

スマホの真似をしようとしたんですが、表示の閾値をいくつにするか悩みました。通信品質はWi-Fiの変調方式はもちろん、機器の差や近隣の干渉波によっても変わるはず。Webで調べるとバッファローは「-30dBm~-40dBmで無線接続が安定します」、IBS JapanのページにはVoIPの最小信号強度-67dBmとありました。これらを参考に、-60dBm/-50dBm/-40dBmでインジケータを振らすことにしました。


2019年11月28日木曜日

ラズベリーパイでradikoラジオをつくる: その5 ロータリエンコーダで音量調整

ロータリエンコーダを使ってみる

今回使うのは、BOURNSのPEC16という1回転で24パルスのロータリエンコーダです。デテント付きと言って、軸を廻すとクリック感のあるタイプを選びました。データシートにはPRO AUDIOとあって、何のことかと思いましたが廻して納得。フィーリングが心地よく、ひょっとしたらデジタルミキサなどのプロ用音響機器に採用されているのかもと思わせるものでした。



ところでロータリエンコーダの軸は、断面がD形(半月形)になっていて、つまみが入手しにくい印象です。今回はRSで購入しましました。記事を書きながら「D形シャフト用つまみ」を改めて調べてみたら、マーベル社の製品が千石で扱いがあるようです。
ロータリエンコーダの中にはスイッチが2個入っていて、回転に合わせてスイッチが断続します。波形の変化から回転方向がわかります。下図でDはデテントの固定位置、CWは時計回り、CCWは反時計回りを示しています。


ロータリエンコーダの接続はこんな感じです。ENC-A, ENC-BをラズパイのGPIOで読み取ります。ラズパイ側のプルアップ抵抗は無しです。


ロータリエンコーダの接点を表示するスクリプト

まずはPythonでスクリプトを作成しました。ENC-A, ENC-Bのどちらかのレベルが変動したら、接点を読み取り標準出力に表示します。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import RPi.GPIO as GPIO_RPi
# GPIO pin assign
SW_ENCA= 22
SW_ENCB= 23

def callback(callback):
    re = (GPIO_RPi.input(SW_ENCA) &1)
    re += (GPIO_RPi.input(SW_ENCB) &1)<<1
    print(re)

def main():
    GPIO_RPi.setwarnings(False)
    GPIO_RPi.setmode(GPIO_RPi.BCM)
    # GPIO initialize
    GPIO_RPi.setup(SW_ENCA, GPIO_RPi.IN)
    GPIO_RPi.setup(SW_ENCB, GPIO_RPi.IN)
    GPIO_RPi.add_event_detect(SW_ENCA, GPIO_RPi.BOTH, callback=callback, bouncetime=5)
    GPIO_RPi.add_event_detect(SW_ENCB, GPIO_RPi.BOTH, callback=callback, bouncetime=5)
    try:
        while(True):
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("break")
        GPIO_RPi.cleanup()

if __name__ == "__main__":
    main()
早速スクリプトを実行させて、右に2ステップ、左に3ステップ廻してみました。右に廻すと0→1→3→2、左は1→0→2→3の順番で変化します。見た感じ、チャタリングは無いように見えます。


ロータリエンコーダで音量を調整

読み取ったデータからスクリプトでロータリエンコーダの回転方向を判定し、音量調整に利用します。回転方向判定は、ChaN氏のページのテーブル変換を参考にしました。ただ、そのままでは軸を1クリック廻すごとに4回動いたと判定されてしまうため、1回になるようテーブルに手を入れています。
ロータリエンコーダの回転を検知する毎にamixerコマンドを発行すれば、音量が変わります。このコマンドで音量は0から255までの数値で指定できますが、減衰量は1/256ずつ直線的に変化すると思っていましたが、実際にやってみるとそうではないようです。この変化のルールがどうなっているのか調べたところ、同じamixerコマンドでその答えが表示されることがわかりました。この表示によると0dBから-51dBまで0.2dBステップで調整できることになっています。


ラジオのボリュームとして使いやすいよう16段階で音量調整できるようにします。減衰量は配列に入れておき、ロータリエンコーダが動く都度読み出します。減衰量のデータは適当に作ったので音量変化が妥当になるよう見直しが必要ですね。参考までスクリプトを示します。今回の製作で初めてPythonに触れたので不適切なところがあるかも、と言い訳しておきます。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import subprocess
import RPi.GPIO as GPIO_RPi
# GPIO pin assign
SW_ENCA= 22
SW_ENCB= 23
re = 0
position = 13

def callback(callback):
    global re
    global position
    #dir = [0,1,-1,0,-1,0,0,1,1,0,0,-1,0,-1,1,0]
    dir = [0,1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0]
    re = (re &3)<<2
    re += (GPIO_RPi.input(SW_ENCA) &1)
    re += (GPIO_RPi.input(SW_ENCB) &1)<<1
    n = dir[re & 15]
    if n==1:
        print('CW')
        if position < 15:
            position += n
        print(position)
        vol_cont(position)
    elif n==-1:
        print('CCW')
        if position > 0:
            position += n
        print(position)
        vol_cont(position)

def vol_cont(position):
    vol = [0,115,125,135,145,155,165,175,185,195,205,215,225,235,245,255]
    cmd = ["amixer", "-c0", "sset", "PCM", str(vol[position]), "unmute"]
    ret = subprocess.Popen(cmd)

def main():
    GPIO_RPi.setwarnings(False)
    GPIO_RPi.setmode(GPIO_RPi.BCM)
    # GPIO initialize
    GPIO_RPi.setup(SW_ENCA, GPIO_RPi.IN)
    GPIO_RPi.setup(SW_ENCB, GPIO_RPi.IN)
    GPIO_RPi.add_event_detect(SW_ENCA, GPIO_RPi.BOTH, callback=callback, bouncetime=5)
    GPIO_RPi.add_event_detect(SW_ENCB, GPIO_RPi.BOTH, callback=callback, bouncetime=5)
    try:
        while(True):
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("break")
        GPIO_RPi.cleanup()

if __name__ == "__main__":
    main()

2019年11月22日金曜日

ラズベリーパイでradikoラジオをつくる: その4 2.8インチTFTディスプレイを使う


2.8インチTFTディスプレイ

2.8インチSPI接続 240×320ドットのもので、ebayで入手しました。ILI9341というメジャーなコントローラチップが使われています。タッチパネルも付いてますが、今回は使いません。3.3V電源で動作させるときは、裏面J1をショートして3.3Vレギュレータ(XC6206)をスルーにします。



Adafruitのソフトウエア設定

  • 今回はデスクトップ画面のディスプレイとしてではなく、Pythonから文字や画像の描画をさせるつもりで、ここでもAdafruit のお世話になります。リンク先の記事User-space SPI TFT Python Library - ILI9341では、This code is discontinuedとありますが代替えのものはCircuitPythonベースのものなので、あえて旧い記事のものを試してみます。
  • ラズパイとLCDの結線は、Adafruit のデフォルトでは次の通りです。(括弧内は今回の基板での結線)事前にラズパイのSPIを有効にしておきます。
Raspberry Pi SCLK (SPI0 SCLK) to TFT CLK. (SCK)
Raspberry Pi MOSI (SPI0 MOSI) to TFT MOSI.(MOSI)
Raspberry Pi CE0 (GPIO 8/SPI0_CE1) to TFT CS.(CS)
Raspberry Pi GPIO 18 (GPIO 25) to TFT D/C.
Raspberry Pi GPIO 23 (GPIO 7) to TFT RST.
  • ソフトウエアをインストールします。元記事では python-imagingが指定されていましたが、python-pilに読み替えました。PIL(Python Imaging Library)はPython 3未対応のうえ2011年を最後に開発が止まっているため、後継のPillowをインストールしようとしましたがexceptionが出てうまく入らなかったので、やむなくPILで続けます。
$ sudo apt-get update
$ sudo apt-get install build-essential python-dev python-smbus python-pip python-pil python-numpy git
$ sudo pip install RPi.GPIO
  • 次にTFTモジュールをインストールします。
$ cd ~
$ git clone https://github.com/adafruit/Adafruit_Python_ILI9341.git
$ cd Adafruit_Python_ILI9341
$ sudo python setup.py install

動作確認

  • ~/Adafruit_Python_ILI9341/examples にサンプルが入っています。
  • まずはimage.pyです。エディタでピンアサインを変更します。変更箇所は次の通りです。他のサンプルも同様に修正します。
# Raspberry Pi configuration.
DC = 25
RST = 7
SPI_PORT = 0
SPI_DEVICE = 0
  • image.pyを動作させると、猫の写真が描画されます。
$ sudo python image.py

  • image_timed.py は、猫の写真を連続描画させて所要時間を測るベンチマークみたいな物です。概ね6fps程度、JPEGデコードしてシリアルのSPI接続でデータを送るならこんなものなんですかね。

  • shapes.pyを動作させると、Pythonスクリプトで設定した図形や文字が表示されます。しかし文字が小さすぎです。デフォルトフォントは8px固定で高さは1.2ミリ程度。
$ sudo python shapes.py
  • この機能を使って、ラジオの表示パネルっぽいものを作ってみます。まずは文字を大きくしたいところ。スクリプトにフォントの変更方法が書かれています。使用できるフォントの一覧は次のコマンドで取得できます。
$ fc-list
  • インストール済みのフォントから、TrueTypeフォントのDejavu Sansを試します。フォント名あるいはフォントをフルパスで指定できます。
font = ImageFont.truetype('DejaVuSans.ttf',16)
font = ImageFont.truetype('/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',24)
  • ラジオのディスプレイっぽい雰囲気にしてみました。スイッチを押してもまったく動かないハッタリですけど、完成した感が出ます。文字の位置や大きさを調整していたところ、面倒なことに気がつきました。どうやらTFTパネルは縦長画面がデフォルトで、横長表示するには回転させる必要があるようです。だから、この文字はソフトウエアで90度回転させて描画しています。

2019年11月20日水曜日

ラズベリーパイでradikoラジオをつくる: その3 I2S DACでRadiko再生

I2S出力を有効にする

  • Adafruit I2S Audio Bonnet for Raspberry Piのインストールスクリプトを試してみます。これには再生開始・停止時のポップ音対策としてサウンドデバイスが使われていないときにI2Sが途切れないよう無音を再生する機能が含まれています。この無音再生機能は、インストールスクリプトを再度実行すれば無効化もできます。
  • このスクリプトを走らせると、サウンドカードはI2Sのみが有効になり、HDMI出力のオーディオエンベデッド信号は無効となります。インストールは次のようにするだけでした。
$ curl -sS https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/i2samp.sh | bash
  • インストール後に再起動します。さらにもう一度、同じスクリプトを実行し、再起動すると、音量調整機能が有効になります。(この操作を忘れて時間をかなり無駄にしました)音量調整は、alsamixerを起動し、上下キーで変更できます。
$ alsamixer

  • aplayで再生デバイスを確認すると、sndrpihifiberry(I2Sのドライバ)のみが有効になっていることがわかります


  • 次は、コマンドラインでの音量調整を試してみます。Card 0/PCMの音量を左100%、右25%に変更する例です。(注意:「100%, 25%」のように空白が入ると動きません)この音量の数値に対し、音量は(対数ではなく)リニアに変化するようです。

$ amixer -c0 sset PCM 100%,25% unmute
音量はフルスケール255に対する実数でも指定できます。
$ amixer -c0 sset PCM 25,75 unmute

DACを有効にする

  • この基板ではDACのミュート端子をGPIO 27に接続してあるので、音を出すにはHレベルにします。この操作は、いずれ選局操作のスクリプトに組み込むつもりです。とりあえず動作確認のため、dac_enable.pyとしてスクリプトを作成しました。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import RPi.GPIO as GPIO
GPIO_DAC_ENABLE = 27
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_DAC_ENABLE,GPIO.OUT)
GPIO.output(GPIO_DAC_ENABLE,True)

音声出力のテスト

  • メディアプレーヤー アプリである MPlayer をインストールします。
$ sudo apt install mplayer
  • このアプリで、WAV, MP3, ALAC, FLAC等の音楽ファイルを再生できます。I2Sはデフォルトのサウンドデバイスでは無いので、出力先をI2S(card 1/Subdevice #0)と指定します。ちなみにALSA(Advanced Linux Sound Architecture)はサウンドのカーネルコンポーネントの名称とのこと。
$ mplayer music.mp3

play_radiko.shでRadiko受信

$ sudo apt install mplayer
$ sudo apt install rtmpdump swftools libxml2-utils
  • ダウンロードした play_radiko.shをTera Termのscpコマンドで ~/play_radiko に転送し、実行権限を付与します。
$ chmod 777 ~/play_radiko/play_radiko.sh
  • それでは再生(受信?)してみましょう。聴取する放送局のID記号は、radikoのwikiページに書かれています。NHKも地元局のみで他地区のものは聴けません。
$ cd ~/play_radiko/
$ ./play_radiko.sh [ID] 
東京都のID(例)
TBSラジオ: TBS(関東広域圏)
文化放送: QRR(関東広域圏)
ニッポン放送: LFR(関東広域圏)
InterFM897: INT(関東広域圏)
TOKYO FM: FMT
J-WAVE: FMJ
NHK R1: JOAK(関東広域圏)
NHK FM: JOAK-FM(全国)

ラジコプレミアム(エリアフリー)対応スクリプト

$ sudo apt-get install rtmpdump swftools libxml2-utils ffmpeg libavcodec-extra-53
としたらlibavcodec-extra-53のエラーが出て失敗してしまうので、その他をインストールします。どうやらffmpegにlibavcodecの機能が組み込まれている?ようです。
$ sudo apt-get install rtmpdump swftools libxml2-utils ffmpeg
  • 記事通りrec_radiko.sh を修正しplay_radiko2.shとして保存。実行権限を付与します。
$ sudo nano play_radiko2.sh
$ sudo chmod 755 play_radiko2.sh
  • 次のようにすると再生できます。スクリプトは~/play_radiko2/に置きました。IDは放送局のID記号で前項と同様です。timeは録音時間で、元々は録音アプリだったことの名残なので0でOKです。mail addressとpasswordは、radikoプレミアムのログインIDとパスワードです。
$ cd ~/play_radiko2/
$ ./play_radiko2.sh [ID] [time] [mail address] [password]
J-WAVEの聴取例
$ ./play_radiko2.sh FMJ 0 [mail address] [password]

2019年11月17日日曜日

ラズベリーパイでradikoラジオをつくる: その2 OSセットアップと自動シャットダウンの設定

事前に準備するもの

  • Raspberry Pi Zero W(Wi-Fi対応) ※WHタイプはヘッダピン実装済み
  • microSDカード: 容量8から32GB、速度Class 10程度
  • miniHDMI変換アダプタ・HDMIケーブル
  • USBホストケーブル(OTGケーブル;microUSB オス-USB A メス変換)、キーボード、給電機能付きUSBハブ、microUSBケーブル
  • microUSBケーブル付き ACアダプタ

microSDカードにOSイメージを書き込み

  • OSイメージをmicroSDカードに書き込み
  • Raspbian Buster Lite をダウンロードしOSイメージファイルを解凍
  • ダウンロードしたOSイメージをbalenaEtcherアプリでSDカードに書き込み

Raspbian起動とOSの設定

  • Raspberry Pi ZeroにはmicroUSBコネクタが2個あり、中央寄りがデータ用、残りは電源接続用です。キーボードに電源を供給するため給電機能付きUSBハブを経由でRaspberry Pi ZeroのUSBコネクタに接続
  • 親基板の電源接続用USBコネクタにACアダプタを接続します。Raspberry Pi Zeroの電源は、ヘッダピン経由で親基板から供給されるので、Raspberry Pi Zeroの電源接続用USB端子には何も繋がなくてもOKです
  • Raspberry Pi ZeroのminiHDMI端子にディスプレイを接続
  • OSイメージを書き込んだmicroSDカードを装着し親基板の電源スイッチを投入
  • ログインidは pi、初期passwordは raspberry
  • raspi-config アプリを起動します (上下キー、TAB、ENTERで操作します)
$ sudo raspi-config 
1 Change User Password
 ユーザpi のパスワードを適宜変更
2 Network Options
 N1 Hostname  適宜
 N2 Wi-fi Country/SSID/パスフレーズ
4 Localisation Options Setup...
 L2 Change Timezone: Asia - Tokyo
 L3 Change Keyboard Layout: 日本語キーボードのときは適宜変更
5 Interfacing Options
 P2 SSH: Enable
 P4 SPI: Enable
Finishして再起動
  • 再ログイン後 Wifiから割り振られたIPアドレスを確認します
$ ifconfig
  • ここからはPCから作業できます。TeraTermから先ほどのIPアドレスにSSHログインします。まずはIPアドレスを固定するため、nanoエディタで dhcpcd.conf を編集します。(Ctrl+O 保存、Ctrl+X終了)
$ sudo nano /etc/dhcpcd.conf
とし、最終行に次の内容を追加
interface wlan0
static ip_address=192.168.0.x/24 ⇒ 固定したいIPアドレス
static routers=192.168.0.x ⇒ デフォルトゲートウェイ
static domain_name_servers=192.168.0.x ⇒ DNS
  • 再起動
$ sudo reboot
  • rootのパスワードも設定する
$ sudo passwd root
  • インストール済みソフトウエアパッケージ、パッケージ情報のアップデート
$ sudo apt update
$ sudo apt upgrade

samba をインストール


  • 手順は次の通り。

$ sudo apt-get update
$ sudo apt-get install samba
$ mkdir /home/pi/public
$ sudo nano /etc/samba/smb.conf
[global]項に以下を追記します。IPアドレスは環境に適宜合わせます。
interfaces 192.168.2. wlan0
bind interfaces only = yes
hosts allow = 192.168.2.
security = user
共有フォルダ名を「public」とするため、末尾に次のように追記します。
[public]
comment = IPRadioReceiver
path = /home/pi/public
guest ok = yes
read only = no
browsable = yes
force user = pi

  • smbサービスの再起動

$ sudo service smbd restart

  • Windowsから \\192.168.2.xx\public のファイルを読み書きできればOK (2020.1.1追記)


自動シャットダウンスクリプトの準備

Pythonから利用できるGPIO制御ライブラリはいくつかありますが、標準インストールされているRPi.GPIOを使います。スクリプトは、ImageCanvas Annex "Raspberry Pi Zero用 簡易UPSの作成"を参考にさせて頂きました。停電あるいは電源スイッチオフから3秒経過したら自動的にシャットダウンするようにします。スクリプト作成をミスると、再起動を繰り返すなど手に負えなくなる場合がありますから、事前の動作テストは必須です。
  • nanoで ~/operation_panel/power_monitor.py を新規作成します。スクリプトは次のようにしました。
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import time
import os
import RPi.GPIO as GPIO
GPIO_POWER_GOOD = 17
THRESHOLD = 3
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_POWER_GOOD, GPIO.IN, GPIO.PUD_UP)
while True:
  count = 0
  GPIO.wait_for_edge(GPIO_POWER_GOOD, GPIO.FALLING)
  while True:
    state = GPIO.input(GPIO_POWER_GOOD)
    if state == False:
      count += 0.05
      if count >= THRESHOLD:
        print("[Power Monitor] SHUTDOWN NOW!")
        os.system("sudo shutdown -h now")
        break
    if count > 0 and state == True:
      break
    if count == 0:
      break
    time.sleep(0.05)
  • スクリプトを動作確認します。電源スイッチをオフにして3秒後にシャットダウンするはずです。
$ python power_monitor.py
  •  無事動作したら rc.local に登録してOS起動時に自動実行させます。
$ sudo nano /etc/rc.local
  • rc.localの “exit 0” の前の行に次の文を追記します。2019.11.20 ディレクトリ表記が間違っていたのを修正しました
python /home/pi/operation_panel/power_monitor.py &
  • ラズベリーパイを再起動後、電源スイッチをオフにして暫くするとOSが自動シャットダウンされることを確認できればOKです。ところで、OSシャットダウン後の再起動はラズベリーパイの電源リセットが必要です。再起動させるには、親基板のLEDが消灯するのを待って、電源スイッチを再度オンにします。以上、さらりと書いてますが、不慣れゆえ、ここに到るまで結構苦労しました。

2019年11月15日金曜日

ラズベリーパイでradikoラジオをつくる: その1 基板をとりあえず作ってみる


私の自宅アパートは鉄筋コンクリート造で、室内でのAMラジオ受信はノイズが混じり聴きづらくなっています。ベランダに出れば何の問題も無く受信できるので、いかんせんAMラジオ波の波長が長すぎて、建物の鉄筋に阻まれて室内に入って来られないのでしょう。その対策としてFM補完放送が登場してきたわけですが、我が家のラジオが旧すぎて受信上限90MHzなのでFM補完放送に対応できません。
そこで、iPadでradiko受信してみたんですが、スイッチをひねれば音が出る、といった操作性とはほど遠いことが面倒で、結局はノイズ混じりのラジオ受信に戻ってしまいました。
そんな時、書店でラジオ受信バイブル2018:radiko専用ホームラジオの製作作者のWebページ)という記事を見かけ、求めていたのはこれだと思い、更に自分なりに欲しい機能を盛り込んでみようと目論んだのが今回の製作です。

自分が欲しいRadiko専用ラジオの要件

  • Wi-Fiでインターネットに接続
  • 操作が簡便(スイッチを入れて選局ボタンを押せば聴ける)で、メニューボタンにより細かな操作にも対応できる
  • 音量調整はつまみを廻してできること、UP/DOWNボタンは面倒!
  • ラジコプレミアムに対応
  • パワーアンプを内蔵し音のよい外付けスピーカが使えること
  • 液晶画面に選局情報を大きく表示したい
  • リビングでの据置使用を想定して外部電源(5V 1A程度のUSB電源アダプタ)を使用するが必要ならモバイルバッテリーを使えること
  • いきなり電源ケーブルを抜いてもラズパイが壊れないよう簡易なバックアップ電源を備える

ラズベリーパイ周り

心臓部分はもちろんラズベリーパイ、Wi-Fi対応のRaspberry Pi Zero Wを使います。GPIOに操作用タクトスイッチ、中華グラフィックTFT LCD、ロータリーエンコーダを接続します。ちゃんと使えるかは、後から考えましょう。


電源周辺

  • 外部電源は、USB電源アダプタを選びました。入手性が良いので最近の工作では多用しています。でもUSB電源アダプタやUSBケーブルの品質は玉石混交ですね。特にスマホの急速充電の相性を目にして以来、安物には手を出さずANKER信者になっています。
  • 電源回路には、結構悩みました。ラズパイはLinuxなので、とくに電源オフ時はシャットダウン動作の時間稼ぎのため、しばらく電源供給を継続する必要があります、さらに、シャットダウン後に再起動させるには電源リセットの必要があるので、電源供給を遮断しなくてはいけません。
  • 電源スイッチには、プッシュスイッチを選んだのですが、電流容量がわずかしかないので負荷の入り切りには使えません。また、電源オン時は即動、電源オフ時は遅延させる動作シーケンスのため、VISHAYの負荷スイッチSiP32510を使うことにしました。
  • ラズパイのシャットダウン動作は、電源回路からPOWER_FAIL信号をラズパイ側で監視し、ソフトウエアでシャットダウン動作をします。電源オフの遅延時間はジャンパスイッチで切り替えできるようにしていますが、図の定数はテキトーです。実際に試してみたら倍ぐらいの時間になっていました。
  • 不意の停電や万一電源コードを抜かれても、シャットダウン動作の支障にならないよう、バックアップ電源も考慮しています。スーパーキャパシタを使う例を参考にしました。スーパーキャパシタは秋月で購入できる7.5F 5.4Vを選びました。スーパーキャパシタの充電にはLED用定電流ドライバ NSI45090JDを使って140mA程度でチビチビと充電しつつ、外部電源とスーパーキャパシタをダイオードで突き合わせてバックアップ電源としています。
  • Raspberry Pi Zeroは、正規の電源電圧は5Vですが、基板内のレギュレータPAM2306 で3.3V/1.8Vに降圧しています。トラ技の記事によると3.5V以上あればラズパイが動作できたとのこと。スーパーキャパシタの放電時は、時間とともに電圧が低下しますが、5Vへの昇圧回路を省略する代わりに、やや大きい容量のスーパーキャパシタを選びました。
  • スーパーキャパシタの再充電時間を減らすためスーパーキャパシタが放電しきる前に負荷を切り離すことを考えました。TOREXのパワースイッチ XC8107のUVLO(Under Voltage Lock Out)機能を使います。TOREXは、入手性が良くないですが消費電流が少ないことに魅力を感じてあえて選びましたが効果のほどはどうでしょう。無くても良かったかな...??


オーディオ周辺

  • Raspberry Pi Zeroにはアナログオーディオ出力が無いので、I2S出力を外部DACで受けてアナログに変換します。そのためには、どの端子にI2S信号が出てくるのか知りたいところ、公式ページを見てもよくわからない。Webを彷徨ってAdafruit I2S Audio Bonnet for Raspberry Piにすごーくわかりやすく公開されていました。
  • ラズパイは、オーディオDACが必要とする128fsとか256fsとかのシステムクロック信号を出力してくれません。そこで、システムクロック入力が無くても動き、外付け部品が少ないPCM5102Aを選びました。
  • オーディオアンプには、秋月でも購入できるD級パワーアンプPAM8304ASRを選びました。外付け部品が少なくてうまく動けば素晴らしい。


2019年5月5日日曜日

有料化した2D CAD「DraftSight」の代替は

2次元CADのDraftSight無償版の提供が終了してしまいました。現在使えている無償版もWindows用は2019年12月末で使用できなくなります。今後は、サブスク版のみの提供となるようですが、趣味ベースで年に数度の頻度で使う向きには利用に躊躇します。そこで無償で使える代替ソフトを探してみました。

DraftSightはAutoCADそっくりのユーザインターフェースでしたが、あいにく無償ソフトでは同様なものを見つけられません。そこで、Jw_cadDesignSpark MechanicalQCADを試してみたところ、操作性は大きく異なるもののQCADなら付き合って行けそうな感触を得ました。

インストール


  • QCAD Professional Trialをインストール
  • 無償のCommunity Editionにダウングレードする。ウインドウのRemoveをクリックすると、除去すべきdllファイルが表示される

 


  • 前項で表示されたdllファイルを、C:\Program Files\QCAD\plugins から削除
  • アプリを再起動し、ヘルプのバージョン表示がCommunity Editionに変わっていることを確認


参考

無料CADソフト「QCAD」で設計・製図
QCAD操作説明書
QCAD日本語情報
QCADを用いた製図 簡易チュートリアル

2019年3月25日月曜日

STM32: HALライブラリを使ってI2C送受信

SW4STM32(System Workbench for STM32)とSTM32CubeMX(初期化コード生成ツール)でI2Cをいじっています。STM32標準のHALライブラリを使えば、コマンド1行でI2Cを簡単に扱えますが、サンプルコードから少し外れた使い方をしようとするといきなり行き詰まってしまいました。とても初歩的ながら、ポインタやら構造体を勉強しないと動かせないようです。そこで、試行錯誤して動作確認できたコードをメモとして残しておきます。

やりたいこととしては、SiliconLabsのAM/FMラジオ Si4730の制御です。Si4730は、STM32のI2C1に接続されていてアドレスは0x11です。STM32CubeMXでI2C1を適切に設定し、初期化コードが生成されていることを前提とします。

I2Cで送信

まずは送信です。0x12 SET_PROPERTYコマンドでProperty 0x0104 DIGITAL_OUTPUT_SAMPLE_RATEに0xBB80を設定します。I2C送信データとしては、レジスタ0x12にレジスタ値0x000104BB80を送信することになります。ロジアナ波形は次の通りです。


HAL_I2C_Master_Transmit()関数の場合

  uint8_t txBuffer4730[6];
  uint16_t Si4730_Pri_address =  0x11<<1;

  txBuffer4730[0] = 0x12;
  txBuffer4730[1] = 0x00;
  txBuffer4730[2] = 0x01;
  txBuffer4730[3] = 0x04;
  txBuffer4730[4] = 0xBB;
  txBuffer4730[5] = 0x80;
  HAL_I2C_Master_Transmit(&hi2c1, Si4730_Pri_address, txBuffer4730, 6, 100);

  • 第2引数は7ビットのデバイスアドレスを1ビット左シフトさせます
  • 第4引数は送信データのByte数

HAL_I2C_Mem_Write()関数の場合

 この関数を使って同じ波形を作れます。メモリアクセス用の関数なのでレジスタとデータを分けて指定します。

  uint8_t txBuffer4730[5];
  uint16_t Si4730_Pri_address =  0x11<<1;

  txBuffer4730[0] = 0x00;
  txBuffer4730[1] = 0x01;
  txBuffer4730[2] = 0x04;
  txBuffer4730[3] = 0xBB;
  txBuffer4730[4] = 0x80;
  HAL_I2C_Mem_Write(&hi2c1, Si4730_Pri_address, 0x12, 1, txBuffer4730, 5, 100);

  • 第2引数は7ビットのデバイスアドレスを1ビット左シフトさせます
  • 第3引数はメモリ(レジスタ)アドレスを指定
  • 第4引数はメモリ(レジスタ)アドレスサイズのByte数
  • 第6引数は送信データのByte数


I2Cで受信

次に受信です。先ほど設定したプロパティを 0x13 GET_PROPERTYコマンドで読み出します。I2Cでの通信手順としては、まず最初に4バイトのレジスタ指定(書き込み)を送信し、直後に受信する2ステップの流れとなります。

HAL_I2C_Master_Receive()関数の場合

  uint8_t txBuffer4730[4];
  uint8_t rxBuffer4730[4];
  uint16_t Si4730_Pri_address =  0x11<<1;

  txBuffer4730[0] = 0x13;
  txBuffer4730[1] = 0x00;
  txBuffer4730[2] = 0x01;
  txBuffer4730[3] = 0x04;
  HAL_I2C_Master_Transmit(&hi2c1, Si4730_Pri_address, txBuffer4730, 4, 100);
  HAL_I2C_Master_Receive(&hi2c1, Si4730_Pri_address, rxBuffer4730, 4, 100);
  • 第2引数は7ビットのデバイスアドレスを1ビット左シフトさせます
  • 第3引数は受信バッファのポインタ
  • 第4引数は受信データのByte数




HAL_I2C_Mem_Read()関数の場合

この関数では、送信と受信を一括して行える反面、2バイトまでしかレジスタ指定できません。参考としてレジスタ0x12ABから3バイトを受信する場合を例示します。

  uint16_t txBuffer4730;
  uint8_t rxBuffer4730[3];
  uint16_t Si4730_Pri_address =  0x11<<1;

  txBuffer4730 = 0x12AB
  HAL_I2C_Mem_Read(&hi2c1, Si4730_Pri_address, txBuffer4730, 2, rxBuffer4730, 3 , 1000);

  • 第2引数は7ビットのデバイスアドレスを1ビット左シフトさせます
  • 第3引数はレジスタアドレス
  • 第4引数はレジスタアドレスのByte数
  • 第5引数は受信バッファのポインタ
  • 第6引数は受信データのByte数