ラベル ラズベリーパイ の投稿を表示しています。 すべての投稿を表示
ラベル ラズベリーパイ の投稿を表示しています。 すべての投稿を表示

2020年5月6日水曜日

ラズベリーパイでradikoラジオをつくる: その14 ケースに入れて完成


ケースに収納

ケースに入れて日常使いできるようになりました。手元のメモを調べましたら、2019年4月に資料集めを開始していましたので、足かけ1年での完成です。
今回は、市販のプラスチックケース使って、加工アラを目立たせにくく、かつ見栄え良く仕上げることを意識しました。まずベースとなるケースには、パネル面に段差がなく、かつ側板を取り外して加工ができるタカチのPFケースを選びました。そもそもタカチを選んだのは、有料で穴開け加工を請け負ってくれるからなのですが、後から見積もりをとったら予算オーバーとなり目論見が外れてしまいました。その後、紆余曲折あり工作名人F氏の好意に甘え、加工をお願いすることになりました。


仕上げとしてパネル全面をアクリル板で覆う設計としています。アクリル板の加工は専門会社が比較的低コストで受託してくれますから、パネルだけは製品品質を追求できます。副次的な効果として、プラスチップケースに貼り付けたシール紙やテプラを保護できますし、プラケースの加工アラを目立ちにくくできます。アクリル板の加工は、はざいや に依頼しました。Web画面で加工位置を入力すると図面まで表示される驚異の自動見積システムを備えています。(2020年5月現在、コロナ禍の影響で自動見積もりは運用中止)


ケース内部は、このような感じです。F氏の発案で秋月の50円スピーカを内蔵しています。まさに「ラジオの音」が出ます。


私は、ふだん内蔵スピーカを使用せず、外部スピーカに接続しています。スピーカ上部のハンドルはカインズホームの鋳物取手です。


さらにradikoラジオをスピーカ上部にマジックテープで固定しました。


謝辞

最後になりましたが、マルチスレッドプログラミングのヒントを頂いたb様、加工・バグ出し・操作性の向上に協力をいただいたF様・H様、そしてWeb上に有用なソフトウエアを提供してくださっている皆様に感謝を申し上げます。

2020年4月29日水曜日

ラズベリーパイでradikoラジオをつくる: その13 基板を再作成



前回作成した基板は、スイッチの高さが不揃いだったので基板を再作成しました。ついでに、当時国内版が発売されたばかりのラズパイ3A+対応とその他の改良を盛り込みました。

・プッシュスイッチをオムロンからAPEMに変更
・Raspberry Pi 3 Model A+を搭載できるようスーパーキャパシタ品の位置を変更
・ロータリエンコーダを基板取付形/スイッチ付きに変更
・チャタリング防止用のコンデンサ追加
・スーパーキャパシタのUVLO用パワースイッチを省略
・電源のオフディレイタイマー設定のジャンパピンを省略
・オーディオD/Aコンバータのミューティング回路接続を省略
・フォーンジャックの品種を変更
・試験用UART接続ピンの引き出し
・その他、細かな修正

回路図も変更になっています。



ただ1点、課題が残りました。
3台製作したうちの1台でスピーカ出力がプツプツと途切れることがありました。まるでデジタルアンプPAM8304が誤動作してシャットダウンを繰り返しているかのようです。しかもそれは基板ごとの個体差があること、USB ACアダプタの電源電圧が高めの場合に影響があることがわかっています。それでも、いろいろ手を尽くしてみたのですが、どうにもよくわかりません。仕方なく対症療法としてPAM8304を交換したり、USB ACアダプタを選別することで対応することにしました。

2020年4月26日日曜日

ラズベリーパイでradikoラジオをつくる: その12 HLS形式に対応するradiko受信スクリプト

radikoウェブサイトに『Adobe Flash サポート終了』と衝撃的なお知らせが出ていました。受信スクリプトはまさにFlash形式を使用していますので、後継となるHLS形式に否応なく移行する必要が出てきました。


HLS対応のスクリプトはWeb上でいくつか発表されています。その中でも、エリアフリー対応で単一のファイルで構成されている radish を選定しました。radish は、NHKラジオ らじる★らじるやListenRadio等で現在配信中の番組を保存するシェルスクリプトです。
まずは、radishの実行に必要なアプリをインストールします。
$ sudo apt install libxml2-utils
$ sudo apt install ffmpeg
$ sudo apt install jq

次に、GitHubからダウンロードしたスクリプトファイル radi.sh を/home/pi/radikoに置き、スクリプトの録音ルーチンを再生できるように書き替えます。(赤字部分)
# Record
if [ "${type}" = "radiko" ]; then
   ffplay \
      -nodisp -loglevel quiet \
      -headers "X-RADIKO-AUTHTOKEN:${radiko_authtoken}" \
      -i ${playlist_uri}

elif [ "${type}" = "shiburadi" ]; then
   ffplay \
      -nodisp -loglevel quiet \
      -i ${playlist_uri}

else
   ffplay \
      -nodisp -loglevel quiet \
      -i ${playlist_uri}

fi

このスクリプトの使い方は次の通りです。
radikoで地元局(この例ではTBSラジオ)を受信する場合
$ cd ~/radiko/
$ ./radi.sh -t radiko -s TBS
radikoプレミアムのログイン情報を付加するとエリアフリー受信ができます。次の例では、北海道のHBCラジオをエリア外で受信します。
$ ./radi.sh -t radiko -s HBC -i “taro@mail.jp” -p “password”
NHKらじる☆らじる にも対応しています。NHKはエリア制限が無いようで、エリア外からでも受信できました。
$ ./radi.sh -t nhk -s tokyo-r1
赤字で示した放送局IDやメディア記号は、次のコマンドで知ることができます。(radishはシンプルなのに多機能です!)
$ ./radi.sh -l
ところで、再生にmplayerを使う場合、スクリプトの実行開始から放送の聴こえ始めまで10秒程度かかりますが、ffplayでは2秒程度です。しかも音切れもなく安定しており言うことがありません。素晴らしい!

2020年4月24日金曜日

ラズベリーパイでradikoラジオをつくる: その11 Wi-Fi設定をパネル操作で行う

Wi-Fiやradikoエリアフリーなど各種の設定をパネルスイッチ操作で行えるようにしてみました。以下にサンプル画面を示します。地味な画面ながらプログラミングはとっても手間がかかりました。


Wi-Fi設定
一筋縄で行かなかったのはWi-Fiの切替でした。当初、nmcliコマンドを使おうとしましたが、思うように動いてくれないところがあり、結局 wpa_supplicant.conf の書き換えで対応しました。
処理の流れとしては、1アクセスポイント探索、2接続先を選択、3パスフレーズ入力、4アクセスポイント接続とステップを踏んでいきます。

まずscanコマンドでアクセスポイント探索を開始します
$ sudo wpa_cli -i wlan0 scan
そして結果が得られるまで scan_resultsがOKを返すのを待ちます。Raspberry Pi 3A+で5秒ほどかかります。
$ sudo wpa_cli -i wlan0 scan_results

scan結果を画面に表示させたところです。現在接続中のアクセスポイントを赤字で表しています。さらに、近隣のアクセスポイントも表示され、なかなか興味深いです。

接続したいアクセスポイントにカーソルを合わせて選択すると、Wi-Fiパスワードの入力画面を表示します。

選択したアクセスポイントとパスワードをwpa_supplicant.confに書き込み、wpa_cliコマンドにより再読込すると設定変更が反映されます。コマンド発行後、しばらくすると新たにDHCPサーバからIPアドレスが付与されインターネット通信が有効になります。
$ sudo wpa_cli -i wlan0 reconfigure
これらの設定画面により、PCに接続することなく自宅Wi-Fiや携帯テザリングなどの切り替えを自由にできるようになりました。

radiko設定
放送局プリセットは全県分を作成しました。エリア選択画面をもうけ、県別の放送局プリセットを選べます。radikoのエリアフリーのログイン情報を登録すれば、各県の放送局を自由に選んで聴くことができます。


2020年4月17日金曜日

ラズベリーパイでradikoラジオをつくる: その10 AirPlayのジャケ写画像を表示


前回記事から日にちがたってしまいました。当時困ったことを思い出しながら書き残しておきます。shairport-sync-metadata-readerで取得した楽曲情報をLCD表示を行うには、メタデータ受信とLCD表示を並列処理する必要があり、まさかの"マルチスレッドプログラミング"となりました。別スレッドで受信したメタデータをPython 2.7Queueクラスに入れ、メインプログラムで逐次それぞ取り出す処理をしました。
スレッドの起動は次のような感じです。ひとつ問題があって、手動でPythonスクリプトを起動すると期待通り動作するのに、Linuxの自動起動に組み込んだらNGという現象がありました。どうやらshairport-syncよりメタデータ受信のスレッドが先行して起動してしまっていたらしく、shairport-syncプロセス起動を待って、スレッドを起動するようにすると期待通りの動作になりました。

import threading
if get_shairport_status() == 'active':
    if threading.active_count() == 1:
        thread_1 = threading.Thread(target=airplay.read)
        thread_1.start()

ジャケ写画像の表示は、PILpillow)ライブラリのおかげで簡素に書けました。他人のライブラリを拝借しているだけでなんですが、画像が表示されるとそれなりに達成感を感じられました。スクリプトを抜粋すると、つぎのような感じです。LCDへのデータ転送がとにかく遅いので、画像に限らず描画範囲を必要最小限にするよう心がけないと、すぐレスポンスが悪くなります。

path = coverart_path + "/" + coverart_filename
im = Image.open(path)
im = im.resize((140, 140))
im = im.rotate(90)
disp.buffer.paste(im, (75, 179))
disp.set_window(x0=75, y0=170, x1=220, y1=319)
disp.display()

あと、日本語表示をするためIPAフォントをインストールし、文字コード関連の記述を追記することになります。
font2gothic = ImageFont.truetype('ipag.ttf', 18, encoding='unic')
str = airplay.artist
str = trim_str(str.decode('utf-8'), 18)
draw_rotated_text(disp.buffer, str, (109, 2), 90, font2gothic, fill=col_orange)
使用した液晶パネルの都合で、デフォルトでは文字が半回転した右詰め表示になっていまいます。所定の表示幅に合わせて文字を左詰めするいは、余ったスペースを半角スペース埋めにしようとしたところ都合のよい関数がなくて、無理矢理つくりました。等幅フォントを前提としています。

# 全角は2文字として計数し、半角n個の文字列を切り出し
    def trim_str(self, text, n):
        bytes_count = 0
        str_count = 0
        for c in text:
            if unicodedata.east_asian_width(c) in 'FWA':
                bytes_count += 2
            else:
                bytes_count += 1
            str_count +=1
            if bytes_count > n:
                str_count -= 1
                break
        if (n - bytes_count) >0:
            return text[:str_count].ljust(n + str_count - bytes_count)
        else:
            return text[:str_count]

2020年3月30日月曜日

ラズベリーパイでGNU Radio: その1 Raspberry Pi 4で動作させる


今更ながらUSBドングルでSDR受信を試すことにしました。手元にあったワンセグ受信用ドングルは、アンテナコネクタがMCXで使い勝手が悪いなーと調べていたら、e-bayでSMAコネクタがついたRTL-SDR Blogブランドのものが2000円台で売られているのを見つけ購入してみました。現物はワンセグ用ドングルより大きく、サイズ感が想像と違いました。


Raspberry Pi 4のセットアップ

Linuxで使いたいのでラズパイ4を新たに購入しました。ラズパイ4は消費電力が大きくCPUの冷却が必要とされているので、FLIRCのファンレスタイプのケースを選びました。このケースのアルミ筐体がCPUと熱的に結合され、ケース全体がCPUの放熱板として働くのでファン要らずになるという仕掛けです。アイドル動作中には、ケースが人肌程度に暖まります。注意点としては、ラズパイのヘッダピンがケースに覆われてしまうので使えなくなることでしょうか。このケース、国内では入手しづらいようで、amazon.comで購入しました。



ラズパイ4では、電源コネクタがUSB Type-Cに、ビデオ出力がmicroHDMIにそれぞれ変更されています。ACアダプタ(5.1V 3A以上)、HDMIケーブルは秋月電子で購入できます。ちなみにRTL-SDRドングルを含めた消費電流はアイドル時に約0.6A。SDR使用中に0.9Aを超える程度まで変動します。なお、ラズパイには電源スイッチがありません。なので電源スイッチ付きUSBケーブルがローテクながら便利です。

ラズパイのOSインストールにはこれまでと特段変わりないので割愛します。今回はRaspbian Buster with desktop and recommended softwareを入れてみました。設定でSSHログインとVNC Serverを有効にしておきます。インストール時にはラズパイにHDMIディスプレを接続すると思いますが、画面解像度はスタートメニューから 設定 → Screen Configuration にて1920×1080に変更できます。

ラズパイのヘッドレス運用

ディスプレイやキーボードなどを接続せずに使うことをヘッドレス運用とよぶそうです。WindowsからVNCでリモートログインするには、追加設定が必要でした。
一つ目はVNCのパスワード設定です。まず、ラズパイGUI画面で右上タスクバーのVNCアイコンをクリックしVNC Serverアプリを起動します。アプリ右上の三本線メニューでOptions... → Security → Authenticationを"VNC password"に変更します。
これで、WindowsにVNC Viewerアプリをインストールすればリモートログインできます。ところが、ここでラズパイからHDMIディスプレイを取り外すとCannot currently show the desktopとエラーが出て表示できません。そこで、ラズパイのLXTerminalまたはSSHログインにてraspi-configを実行し、画面解像度を設定します。
$ sudo raspi-config
7 Advanced Options → A5 Resolution → DMT Mode 82 1920x1080 60Hz 16:9 としました。

rtl-sdrをインストール

SDR受信を行うには、Osmocom projectのrtl-sdrライブラリを使います。導入には一癖あるようです。よくわからないこともありWeb記事に沿って作業を行いました。まずは、rtl-sdrに必要なlibusbライブラリとビルドツールをインストールします。
$ sudo apt-get update
$ apt-cache search libusb
 ・・・ インストール可能なパッケージが表示されますが、libusb-1.0-0ではだめなのでlibusb-1.0-0-devを選びます
$ sudo apt-get install libusb-1.0-0-dev
$ sudo apt-get install autoconf
$ sudo apt-get install libtool
次いでrtl-sdrをインストールします
$ git clone git://git.osmocom.org/rtl-sdr.git
$ cd rtl-sdr
$ aclocal
$ autoreconf --force --install
$ ./configure
$ make
$ sudo make install
$ sudo make install-udev-rules
$ sudo ldconfig
ここでrtl-sdrで採用されているRTL28xxチップに対応するテレビ受信用標準ドライバをblacklist登録してカーネルに読み込まれないようにします。
$ sudo nano /etc/modprobe.d/rtl_blacklist.conf
ファイルの内容は次の2行です
blacklist dvb_usb_rtl28xxu
blacklist rtl2832
ラズパイを再起動して、rtl_testコマンドで情報が取得できればOK。
$ rtl_test

rtl_fmコマンドで受信テスト

rtl_sdrライブラリ付属のコマンドラインツールrtl_fmで受信してみました。まずは音声再生のためsoxをインストールします。
$ sudo apt-get install -y alsa-utils sox libsox-fmt-all
そしてFMラジオを受信します。82.5MHzのFMラジオを受信しようとしたら、やや低い周波数を周波数を指定する必要がありました。いちおう音は出るんですが、ノイズ混じりだしひずみっぽいし、さらには金属的な付帯音が聞こえます。RFゲイン問題でも無さそうですし、サンプルレートの変換がうまく行っていないのか、aplayで再生しても同じで、原因はわかりませんでした。
rtl_fm -f 82.184M -M wbfm -s 200k| play -r 32k -t raw -e s -b 16 -c 1 -V1 -
rtl_fm -f 82.184M -M wbfm -s 200k -r 32000-| aplay -r 32 -f S16_LE


GNU Radioのインストール

まずはGNU Radio
$ sudo apt-get install gnuradio

次いでGNU radioからrtl-sdrを使えるようgr-osmosdrをインストール
$ sudo apt-get install gr-osmosdr

動作確認として osmocom_fft コマンドでスペクトラムを表示させてみました。NHK FMラジオが無音になったタイミングで、中心キャリアのほかにステレオパイロット信号とVICS信号がぼんやりと盛り上がって見えます。FFTサンプルレートは任意の値を指定できるようにみえて制約がありました。デフォルトは250kHzで1MHzはOKでしたが、500kHzはNGでした。
$ osmocom_fft -f 82.5M -s 250k


次に本命のGNU Radioを起動します。紹介記事で見かける画面が現れました。
$ gnuradio-companion

ネット上にあるFMラジオ受信のサンプルファイルを実行しようとすると、The xterm executable " is missingなるエラーが出ます。xtermが要るようなのでインストールします。
$ sudo apt-get install xterm
それでもエラーが出ますが、使い方がわからず手も足も出ません。そこで、RFワールドNo.44 GRCで広がるSDRの世界を片手に試すことにしました。ところがいちばん最初の、1kHz信号源をオーディオ出力するところでいきなりつまずいてしまいます。Audio Sinkのプロパティで(サウンド)Device Nameがブランクになっているのが原因のようです。試行錯誤してここにhw:0,1またはplughw:0,1とセットするとラズパイのイヤホンジャックを経由して音が出ることを確認できました。また、オーディオのサンプルレートは32kHz/48kHz/192kHzはOKですが、44.1kHzは使えないようです。


ラズパイを受信サーバとしてWindowsでリモート受信

ラズパイで受信サーバコマンドrtl_tcpを起動します。IPアドレスはラズパイのものに適宜読み替えてください。1234はデフォルトポート番号です。
$ sudo /usr/local/bin/rtl_tcp -a 192.168.0.xx -p 1234
Windows側ではWindows SDR Software Packageをダウンロードして、SDR#を起動します。次の操作で受信できるはずです。
・SourceとしてRTL-SDR(TCP)を選びます
・Configure Source(歯車マーク)をクリックし、受信サーバのIPアドレスをセット
 ※rtl-sdrライブラリのドライバではTCP接続ではDirect samplingモードを選べませんでしたが、RTL-SDR Blogのドライバでは改善されているようです(未確認)
・Start(再生マーク)をクリック

RTL-SDR Blog V3

DS-DT305

DT305では、局発リークとスプリアスが見えます。また、ウチの環境ではSDR#をTCP受信で使用すると、受信停止やアプリの終了のタイミングで高確率でハングアップします。

2020年1月1日水曜日

ラズベリーパイでradikoラジオをつくる: その9 AirPlay楽曲情報取得

AirPlayのメタデータを取得

この記事は、Show Artist & Song Metadata Using Airplay on Raspberry Piを参考にしました。AirPlayのメタデータを取得するには、shairport-sync の作者Mike Brady氏が作成された shairport-sync-metadata-reader を使います。(感謝!)
事前準備として、前記事の通りshairport-syncを --with-metadata --with-dbus-interface --with-mpris-interfaceオプション付きでビルドしておく必要があります。そしてshairport-sync.conf のmetadataに関する設定を有効にしてメタデータを出力させます。
$ sudo nano /etc/shairport-sync.conf
metadata =
{
enabled = "yes"; // set this to yes to get Shairport Sync to solicit metadata from the source and to pass it on via a pipe
include_cover_art = "yes"; // set to "yes" to get Shairport Sync to solicit cover art from the source and pass it via the pipe. You must also set "enabled" to "yes".
cover_art_cache_directory = "/tmp/shairport-sync/.cache/coverart"; // artwork will be  stored in this directory if the dbus or MPRIS interfaces are enabled or if the MQTT client is in use. Set it to "" to prevent caching, which may be useful on some systems
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-syncサービスを再起動します。
$ sudo systemctl restart shairport-sync
メタデータを表示してみます。次のコマンドを実行し、iTunesから何か音楽を再生すると、どばっとXMLデータが表示されます。でも、何が何だかわからんですね。
$ cat /tmp/shairport-sync-metadata

shairport-sync-metadata-reader

XMLデータの解析ツールshairport-sync-metadata-readerをインストールします
$ cd ~
$ git clone https://github.com/mikebrady/shairport-sync-metadata-reader.git
$ cd shairport-sync-metadata-reader
$ autoreconf -i -f
$ ./configure
$ make
$ sudo make install
AirPlayが接続済みであれば再接続します。そして、次のコマンドを実行しiTunesから音楽を再生してみます。
$ shairport-sync-metadata-reader < /tmp/shairport-sync-metadata

今度は、少しわかりやすく表示されました。
冒頭に音源の情報が羅列されています。snuaは"user agent"、acreはActive-Remoteトークン、daidはDACP-IDです。スピーカ側からiTunesを制御?するときに使うようです。スピーカのボリューム変更がiTuneにも反映されるようにするとかですかね、よくわかりません。pendは再生ストリームの終了、pbegは再生ストリームの開始、pvolは音量、dapoはソース側のポート番号でiTunesは3689です。
次いでAlbum NameArtistTitleの情報が取得できています。
そして、mdstはメタデータのシーケンス開始、mdenは終了、数値はRTPタイムスタンプを表します。Shairport Syncの説明によると、32ビットの符号なし整数で1秒あたり44,100フレームのオーディオフレームのインデックス番号であるとのこと。
pcstは画像の送信開始、pcenは送信終了です。Picture received, length 240192 bytes.とあるとおりディスク・ジャケット画像が受信されshairport-sync.confで指定したフォルダに保存されます。この画像は、shairport-syncが受信しており、画像が変わる度に新たな名前で更新され、前の画像は削除されるようです。なお、ジャケット画像を含まない音楽ファイルでは更新されず、前の画像が残ったままになるようです。


prgr(progress)は、再生シーケンス開始、現在の再生ポイント、再生シーケンス終了のRTPタイムスタンプを表しています。
再生を一時停止すると次のようになります。flsrは、停止位置のRTSPタイムスタンプのようです。pflsは再生ストリームが無くなったことを示しています。


そのまま待っていると、10秒ほどで"ssnc" "pend"が2回繰り返しそのままとなります。pendは再生ストリームの終了を示しています。また、再生を再開するときは、再生開始と同じメッセージが出ました。


スライダーで再生位置を変更。


曲の再生が終わり、次の曲に移るとき。


iTunesには音量調整のスライダーがあります。音量最大、中間、最小に変更すると、1回ずつpvolが出ます。音量値の単位はdBで、-144.00はミュートを表すそうです。
shairport-sync.confでignore_volume_control = "yes"としていなければ、音量が変わるはずです。


メタデータの利用

曲名等とアルバム・ジャケット画像の表示のために、次のメタデータ情報を使います。prgrは再生開始、pflsは一時停止、pendは再生停止と解釈してみることにします。
Album Name: "Opus".
Artist: "山下達郎".
Title: "Down Town".
Picture received, length 240192 bytes.
"ssnc" "prgr": "3268798776/3268811064/3283215360".
"ssnc" "pfls": "".
"ssnc" "pend": "".
次に、shairport-sync-metadata-readerの出力を受け取り、楽曲情報・再生状態・アルバム・ジャケット画像ファイル名を表示するスクリプトを作成してみました。


Pythonスクリプトを以下に示します。標準出力の受け取り方がわからず困惑しましたが、Webに投稿された例を参考にしました。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import subprocess
import os

class airplay_metadata:
    def __init__(self):
        self.artist = ""
        self.album = ""
        self.title = ""
        self.status = ""
        self.coverart_path = '/tmp/shairport-sync/.cache/coverart'
        self.coverart_filename = ""

    def read(self):
        cmd = "shairport-sync-metadata-reader < /tmp/shairport-sync-metadata"
        proc = subprocess.Popen(cmd,stdout=subprocess.PIPE,shell=True)

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度回転させて描画しています。