LCDを使ってStandalone Playerを作る

更新日: 2026.08.30

概要

"Fixing Radiko Streaming Issues (2026)" と"ILI9341 LCD を使う"を 使って Radiko, Web Radio, Music File を再生出来る "Player" を作りました。LCD画面にメニューを表示するStandaloneプレーヤーです。 メニューにタッチしながら "Player" をコントロールします。

Standalone Media Player

部品一覧

部品詳細
Raspberry PI Zero W
Raspberry PI Zero W
本体です。
3.5" LCD
LCD ILI9341
3.5" 320x240タッチセンサ付きLCD。
USB Speaker
USB Speaker
電源はUSBですが、音声入力はミニジャックのスピーカ。
USB HUB
USB HUB
ケーブルがMicro USBのUSB HUB。電源供給コネクタ有り。
USB <-> mini jack cable
USB <-> ミニジャック変換器
スピーカー入力用に使用。
USB type A to micro cable
USB <-> Micro USB 変換ケーブル
Raspberry PI Zeroの電源供給用。2本必要。
USB cable with switch
スイッチ付きUSBケーブル
本体用電源スイッチ。
AC adapter
ACアダプター USB充電器 2ポート
本体用電源。

回路と接続

LDC と Raspberry PI Zero W

Raspberry PI Zero W and LCD circuit schematic

その他の部品の接続

Compornents connection
  • ACアダプタ:AC100Vのコンセントに接続。
  • スイッチ付きUSBケーブル:このスイッチで電源のオンオフを行います。
  • USB変換ケーブル:USB HUBの電源供給コネクタにつなぎます
  • HUBケーブル:Raspberry PI Zero WのUSBコネクタにつなぎます。
  • HUBのポート:ポートを3つ使用しています。
    • Raspberry PI Zero Wの電源(USB変換ケーブル)
    • USBスピーカの電源
    • USBミニジャック
  • スピーカのミニジャック:USBミニジャックと接続。
  • Product Back

OSのインストールと下準備

"Legacy 32bit light(Bookworm no Desktop)"を Raspberry PI Imager でセットアップしています。 OSインストール後下記を行います。1から4は"ILI9341 LCD を使う"と同じ。5以降が今回の追加分です。

  1. アップデートとアップグレイド
    • sudo apt update
    • sudo apt full-upgrade -y
  2. SPI通信の有効化
    • raspi-config で「Interface Options」から SPI を有効にする。
  3. Pi.GPIO: Raspberry PiのGPIOピン制御。spidev: SPI通信制御。 Pillow (PIL): 画像処理・描画のインストール
    • sudo apt install python3-rpi.gpio python3-spidev python3-pil
  4. 注:デフォルトでは spi.open(0, 1) が正しく機能しなかったので
    • 今回使用したRas PI Zero固有の問題と思いますがspi.open(0, 1)が正常に通信出来ませんでした。
    • CS2(GPIO8)の不具合が原因でした。そこで、CS2をGPIO8からGPIO22に変更しています。
    • "/boot/firmware/config.txt" の一番下に有る "[all]" 項目の下に
        "dtoverlay=spi0-2cs,cs0_pin=8,cs1_pin=22"
      を追加するとCS2をGPIO8からGPIO22に変更しています。
  5. MPVのインストール
    • sudo apt update
    • sudo apt install mpv
  6. スピーカーのセットアップ
    • sudo raspi-configで 1 System Options -> S2 Audio と進む。
    • USBミニジャックオーディオ(今回はAB13X USB Audioでした)を選択。
    • aplay /usr/share/sounds/alsa/Front_Center.wav 等を行い音が出ることを確認。
  7. フォント一式をインストール:使用するフォントのインストール
    • sudo apt update
    • sudo apt install fonts-noto-cjk
  8. 作業用ディレクトリの作成 :ホームの下に "player" フォルダを作成
    • mkdir -p ~/player
    • "player.zip"をダウンロードし解凍。
      • ILI9341.py btn.py main.py music // folder music_main.py radiko_main.py state.txt web_radio.txt
    • これらを ~/player/ に保存。
      • "main.py":今回のアプリのコード。
      • "btn.py" :ユーザインタフェース ボタンのコード。次で使用。
  9. IPアドレスの固定
    • 今後の操作を考えるとIPアドレスを固定した方が便利です。
    • 必要に応じて固定して下さい。

ユーザインタフェース ボタンの作成

ボタンの状態は通常下記の3つ。

  1. 通常(何もしていない)。
  2. ボタンを押した。
  3. ボタンを離した。

普通 "3" の離したと同時に何かの処理を行います。それを踏まえて下記のコードを書きました。

btn.py python
import time from PIL import Image, ImageDraw, ImageFont import ILI9341 # 先ほどのファイルをインポート # ========================================== # ボタンの設定 # ========================================== BTN_W, BTN_H = 120, 50 btn_x = (ILI9341.WIDTH - BTN_W) // 2 btn_y = (ILI9341.HEIGHT - BTN_H) // 2 btn_x1 = btn_x + BTN_W - 1 btn_y1 = btn_y + BTN_H - 1 # フォントの準備(ILI9341.pyのフォント流用、または新規指定) try: font = ImageFont.truetype( "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", 20 ) except: font = None def draw_button(is_pressed=False): """ボタンの描画を行う関数(状態によって色を変える)""" # ボタンの画像を作成(背景黒) image = Image.new("RGB", (BTN_W, BTN_H), "black") draw = ImageDraw.Draw(image) # ボタンの色を決定(押されていたらグレー、通常は青) bg_color = (100, 100, 100) if is_pressed else (0, 0, 255) outline_color = (255, 255, 255) # ボタンの四角形を描画 draw.rectangle([0, 0, BTN_W - 1, BTN_H - 1], fill=bg_color, outline=outline_color) # ボタンの中のテキスト draw.text((35, 8), "PUSH", fill=(255, 255, 255), font=font) # 色データの変換 buf = bytearray() for r, g, b in image.getdata(): color16 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) buf.append(color16 >> 8) buf.append(color16 & 0xFF) ILI9341.set_window(btn_x, btn_y, btn_x1, btn_y1) ILI9341.write_data(buf) def is_inside_button(x, y): """タッチされた座標がボタンの範囲内か判定""" return btn_x <= x <= btn_x1 and btn_y <= y <= btn_y1 def main(): # ディスプレイの初期化(横向き) ILI9341.init_ili9341() ILI9341.set_rotation(1) # 初期画面の描画 image = Image.new("RGB", (ILI9341.WIDTH, ILI9341.HEIGHT), "black") draw = ImageDraw.Draw(image) draw.text((20, 20), "Button Test App", fill=(255, 255, 255), font=font) bg_color = (0, 0, 255) outline_color = (255, 255, 255) draw.rectangle([btn_x, btn_y, btn_x1, btn_y1], fill=bg_color, outline=outline_color) draw.text((btn_x + 35, btn_y + 8), "PUSH", fill=(255, 255, 255), font=font) ILI9341.display_image(image) print("ボタン監視ループを開始します。画面のボタンを押してください。") was_touching = False #ボタン監視ループ try: while True: # ILI9341.pyの関数で生のタッチデータを取得 raw_x, raw_y = ILI9341.read_touch_raw() # タッチされているかどうかの簡易判定 (生データの有効範囲チェック) current_touching = False if raw_x > 0 and raw_y > 0: # ILI9341.pyの座標変換関数でピクセル座標に変換 px, py = ILI9341.map_coordinate(raw_x, raw_y) # ボタンの範囲内をタッチしているか if is_inside_button(px, py): current_touching = True # -------------------------------------------------- # 状態変化の検知 (押された / 離した) # -------------------------------------------------- if not was_touching and current_touching: # 【押された瞬間】 print("-> [押された]") draw_button(is_pressed=True) # 押された見た目に更新 elif was_touching and not current_touching: # 【離した瞬間】 print("-> [離した] ──> アクション実行!") draw_button(is_pressed=False) # 元の見た目に戻す # TODO: ここで処理を行う # 次のループのために状態を保存 was_touching = current_touching time.sleep(0.05) except KeyboardInterrupt: print("\n終了します") finally: # 終了時のクリーンアップ ILI9341.spi.close() ILI9341.spi_touch.close() ILI9341.GPIO.cleanup() if __name__ == "__main__": main()
  • 61~68行:画面全体のオブジェクトを作り初期画面を製作、LCDに転送し表示。
  • 75~107行:ここでループを組んでボタンの状態を監視しています。
    • 変数 "was_touching" と "current_touching" がボタンの状態を保持。
    • 77行:タッチセンサをスキャン。
    • 81行:タッチが有り。
    • 86行:ボタンの範囲内なら変数 "current_touching" をTrueにセット。
    • 93~101行:"was_touching" と "current_touching" の組み合わせでボタンの状態を判断。
    • 96,101行:draw_button(is_pressed=True) -> ボタンの描画を行う関数。本体は23行。
      • 26行:LCD全体では無くボタンの大きさのオブジェクトを作成。
      • 状態に合わせてボタンの背景色を設定。
      • 46,47行:ここで部分的に更新。部分的な書き換えなので更新が速い。
    • 102行:ここでボタンが離れた事を感知。この後に希望の処理を行う。
  • コードを実行するとこんな感じ
      Result of bottom sample code
    • ボタンが押される度に背景が変わる。
    • モニタには状態が表示される。

Playerの使い方

モニタで python3 main.py と実行して下さい。アプリが上がります。

How to use this aprication
  • 一番左の "Main Panle" がLCDに表示されます。ボタンが3つ有り各ボタンにタッチすると、
    • Radiko:Radiko画面(上段)に飛びます。Radikoと登録したWeb Radioが聞けます。
    • Music :Music画面(下段)に飛びます。登録した音楽ファイルを再生します。
    • Exit :アプリを終了します。"sudo Shutdown -h now" を実行します。
  • Radiko画面
    • 画面中央:
      • Radikoのラジオ局(フリー)と登録されたWebラジオ局のリスト表示。
      • 起動時はリストトップが選択(背景が薄い灰色)されます。
      • リストの項目にタッチすればそれが新しく選択されます。
    • 画面下部の3つのボタン:
      • "Play", "Mute" ボタンはトグル式になっています。
      • "Play" ボタン
        • 最初のタッチ:背景がタッチした瞬間に灰色。選択されたラジオの再生始で赤になります。
        • つきのタッチ:再生を中止(接続を切る)し背景を青(元の色)に戻す。
      • "Mute" ボタン
        • 最初のタッチ:背景が赤に変わり消音。接続は切れていない。
        • つきのタッチ:消音を解除して背景を青に戻す。
      • "Back" ボタン: Main Panel に戻る。
    • リスト表示右側のスクロールボタン
      • 三角のスクロールボタンは表示の範囲を上下に1つ移動します。
      • 上下スクロールボタン間の灰色四角は全リストに対して現在表示されている部分を示します。
      • ここは表示のみです。位置を手動で変更出来ません。
  • Music画面
    • 左:登録されたフォルダが表示されます。起動時はTopが選択されています。
      • "OK"ボタン:選択。 "Back"ボタン:Main Panel に戻る。
    • 中:リストの項目にタッチして、”OK"ボタンを押すと
    • 右:曲再生画面が表示されます。
      • "Play", "Mute" ボタンは Radikoと同じ機能
      • "Back"ボタンは1つ前の画面(フォルダ表示)に戻ります。
      • 画面左の白、青、赤の3つの丸にタッチすると下記の機能がオンになります。
        • 全てオフ:選択された曲を再生して終了。
        • 白がオン:選択された曲を繰り返し再生。終了しない。
        • 青がオン:フォルダ内で最初から終わりまで順に再生し終了。
        • 赤がオン:フォルダ内でランダムに再生し全てを再生したら終了。

Web Radio と 音楽ファイルの登録

  • Web Radioの登録
    • アプリルート(~/player)の下に有る "web_radio.txt" にWeb RadioのURLとタイトルを登録する。
        http://strm112.1.fm/blues_mobile_mp3 FM - Blues Radio http://live-radio01.mediahubaustralia.com/JAZW/mp3/ ABC Jazz https://edge68.live-sm.absolutradio.de/absolut-hot/stream/aacp?aggregator=smk-m3u-aac Absolut Hot http://play2.organlive.com:7002/320 Positivly Baroque https://smoothjazz.cdnstream1.com/2586_320.mp3 SmoothLounge Global
      • 2行で1組です。
        • 1行目:Web RadioのURL
        • 2行目:Web Radioのタイトル。LCDのリスト表に表示されます。
      • 登録したWeb Radioは Radiko局の下に追加されます。
  • 音楽ファイルの登録
    • アプリルート(~/player)の下に "music" というフォルダが有ります。ここに登録する。
    • 先ず、登録したい音楽ファイルを1つのフォルダにまとめます。
    • そのフォルダを"music" フォルダの下に保存します。
    • "music" フォルダの下は音楽ファイルをまとめたフォルダのみとなります。
        ~/player/music ├── folder-0 │   ├── music0.mp3 │   ├── music1.mp3 │   └── music2.mp3 ├── folder-1 │   ├── play0.acc │   ├── play1.mp3 │   └── play2.acc ├── folder-2 └── folder-3
    • "music" フォルダ下のフォルダ名がアプリのフォルダ表示画面に表示されます。
    • 音楽ファイル名がアプリ演奏のリスト表示画面に表示されます。
    • これ以外のフォーマットには対応していません。
      • "music" フォルダ下に音楽ファイルを置く。
      • 音楽フォルダを階層にする。
      等には対応していません。

自動起動

最後にサービスを登録して自動起動させます。モニタ画面で "sudo nano /etc/systemd/system/player.service" を実行し 下記を作成。"XXXXX" の部分(3箇所)は自身の環境に合わせて入力して下さい。

player.service text
[Unit] Description=Player Application After=network.target sound.target [Service] Type=simple User=xxxxx WorkingDirectory=/home/xxxxx/player ExecStart=/usr/bin/python3 /home/xxxxx/player/main.py Restart=always RestartSec=5 [Install] WantedBy=multi-user.target

保存して下記を実行。

      # 設定を読み込み直す   sudo systemctl daemon-reload   # 起動時に自動で立ち上がるようにする    sudo systemctl enable player   # 今すぐサービスを起動する   sudo systemctl start player

最後に

ボタン1つで素晴らしいメディアプレイヤーが出来ました。Standaloneで動きスマホ等がいらないのが便利です。 立ち上がりが遅く電源を入れてからMain Panelが表示されるまでかなりの時間がかかりますが、 それ以外は処理能力の低いRaspberry PI Zero Wでも十分に機能します。ラジオも曲も聞けて満足です。

SINCE 2026