ハードウェア

ラズパイZero 2 Wで安価な人感センサー付きデジタルフォトフレームを作ったが、もう安くはない件

セルフホストしているImmichの写真をスライドショー表示するデジタルフォトフレームを、Raspberry Pi Zero 2 Wで自作しました。
コンテナイメージ化してDockerで手軽に動かせるのでサクッと作れます。

GitHub - seizu-dev/raspi-photo-frame
Contribute to seizu-dev/raspi-photo-frame development by creating an account on GitHub.
スポンサーリンク

なぜ自作したか

市販の安価なデジタルフォトフレームは古いAndroidベースのものが多く、機能も少なくて動作ももっさりしていて、正直使いづらいんですね。何より画質が悪い。
高機能な機種になると2万円以上したりするので、気軽に手を出せる値段でもありません。

それと、セルフホストしている Immich と連携させたかった、というのが一番の理由です。
Immichのアルバムやお気に入りをそのままフォトフレームで流せたら便利だな、と。

1号機と2号機

実は最初に作ったのは Raspberry Pi 3B 向けのもので、GUIは Kivy で作りました。
この1号機は今も実家で稼働していますが、とにかく動作が重いです。

そこで今回の2号機では、Zero 2 Wでも動作できるようにGUIを pygame-ce に置き換えて軽量化しました。
デスクトップ環境は使わず、SDL2のKMSDRMドライバで直接描画しています。
結果、アイドル時に BOINC を回しても問題ないレベルまで落とし込むことができました👍

Zero 2 WのRAMは512MBしかないので、ここが一番の制約でした。
画像バッファの数や画像処理パイプラインの形は、この制限から逆算して決めています。

主な機能

  • Immichのアルバム・お気に入り・日替わりピックアップから写真を取得
  • 切り替えエフェクト4種(クロスフェード / 暗転 / スライド / ワイプ)+ランダム
  • 時計・撮影日・写真カウンター・次の写真までのカウントダウン表示
  • タッチ操作(左右タップで前後の写真、中央タップで情報表示、歯車ボタンでメニュー)
  • 人感センサー(AM312)とタッチによる自動消灯・復帰
  • UI言語の切り替え(日本語 / 英語)

アプリ内から各種設定ができます。

自動消灯して人感センサーで復帰するので、パネルの寿命も長持ちするし省電力です。
……とはいえ、うちではアイドル時にBOINCを回しているので、結果的にあまり省電力にはなっていません😅

ちなみに1号機のパネルにはバックライト消灯用のジャンパーピンが備わっているので、そちらでは素早くON/OFFできるようにしています。

必要なもの

パーツ 備考 価格
Raspberry Pi Zero 2 W 64 bit Arm Cortex-A53 クアッドコア BCM2710A1 1GHz RAM 512MB 購入時 2,000円程度(実勢価格 8,000円程度)
タッチ対応ディスプレイ HDMI入力 Raspberry Pi対応 1024×600以上推奨 Amazonで 6,000円程度~
変換ボード Pi 5のインターフェースを再現するもの AliExpressで 2,000円程度
人感センサー(任意) AM312(GPIO 18に接続) 数百円程度(電子部品屋での購入がおすすめ)
microSDカード 16GB以上 A1以上推奨 容量次第
電源 5V 2.5A推奨 ピンキリ
Immichサーバー セルフホストなので別途用意

使ったタッチパネルはこちら。※Zero 2 Wの直接マウントには対応していません(後述)

タッチ対応のディスプレイであれば、大きさや解像度は自由に選べます。

安く作れる……はずだった

もともとZero 2 Wは2,000円ぐらいで手に入れていたので、安く作れるつもりでした。
ところが昨今の高騰で、今の実勢価格は8,000円程度。結局まったく安くはありません。

¥8,564 (2026/09/22 22:15時点 | Amazon調べ)

変換ボードまわりの苦労

このタッチパネルはZero 2 Wには対応していないので、Pi 5のインターフェースを再現する変換ボードを間に挟んでいます。
そもそも Mini HDMI と Micro USB という変換必須なポートしかないのが辛い。

Pi 5のインターフェースに変換する基盤

ただ、この構成だとHDMI出力が異常で、パネル本来の 1024×600@60Hz では映りませんでした。
調べたところ、アダプタが40MHz以上のピクセルクロックを通せないようで、今は 50Hz で妥協しています(詳細は後述の cmdline.txt の設定を参照)。

また、Zero 2 W単体でも取り付け自体は可能ですが、HDMIポートのクリアランスが厳しくて、変換ボードなしでは市販のケーブルで背面のコネクタに収めるのは難しいです。

背面HDMIポートが基盤にベタ付けなので、市販のケーブルでは側面ポートを使うしかない

Zero 2 WはDSIコネクタを持っていないので、公式のRaspberry Piタッチディスプレイ(DSI接続)は使えません。

Zero 2 Wの高騰もありますし、意外とマウント対応しているタッチディスプレイが売っていないので、今から作るなら、結局 Pi 3B を買った方が選択肢もコスト面も有利そうです。

人感センサーの取り付け

人感センサーは AM312 を使っています。

端子は VCC / OUT / GND の3本だけなので、ジャンパーワイヤーで40ピンヘッダーに直結するだけでOKです。

配線図

AM312 Raspberry Pi(物理ピン番号)
VCC 17番ピン(3.3V)
OUT 12番ピン(GPIO 18)
GND 14番ピン(GND)
AM312のピンの並び順はロットによって異なります。
配線する前に、必ず基板のシルク印刷で確認してください。

センサー側は一応ポリイミドテープで絶縁

GPIOのプルダウンはアプリ側(libgpiod)で設定しているので、抵抗などの追加部品は要りません。
コンテナに /dev/gpiochip0 を渡す設定も docker-compose.yml に入っています。

人感センサーは任意です。
付けない場合は、config/settings.jsonmotion_sensor_enabledfalse にしておけば、タッチでの復帰だけになります。

セットアップ手順

ここからは、Raspberry Pi OS Lite(ARM64 / 64bit版)を書き込んだ後から、起動するまでの最短の流れです。
コンテナイメージはGHCRに公開しているので、基本的には docker compose pull するだけで動きます。
ウィークエンドDIYにもちょうどいいと思います。むしろImmichのセルフホストの方がハードル高いかも……😣

Step 1: 画面モードの設定

もし私のように変換ボードを通したことで、EDID通りの解像度で表示ができない場合は /boot/firmware/cmdline.txt に以下を追記して妥協します。
そのような問題が無く表示できている場合は次のステップに進んでください。

cmdline.txt は1行で書くファイルなので、既存の行の末尾にスペース区切りで追加してください。

video=HDMI-A-1:1024x600MR@50e
この値はアダプタとパネルの組み合わせで変わります。
私の環境では標準の1024×600@60(51.5MHz)だと何も映らず、上記の設定で 1024×600 @ 49.61Hz / 36.36MHz で動いています。
切り分けの経緯はリポジトリの SPECIFICATION.md の 9-9 節に書いています。

Step 2: Dockerのインストールとリポジトリの取得

Dockerは公式のインストールスクリプトで一発で入ります。

curl -fsSL https://get.docker.com | sh
sudo なしで docker コマンドを使いたい場合は、sudo usermod -aG docker $USER を実行して、一度ログインし直してください。

続いて、リポジトリを取得します。

git clone https://github.com/seizu-dev/raspi-photo-frame.git
cd raspi-photo-frame

Step 3: フレームバッファコンソールの切り離し

これをやらないと、消灯・復帰のたびにコンソールの文字が画面にチラっと出てしまいます。

sudo install -m 755 tools/host-setup/pf-fbcon-off.sh /usr/local/sbin/pf-fbcon-off.sh
sudo install -m 644 tools/host-setup/pf-fbcon-off.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now pf-fbcon-off.service

Step 4: ImmichのAPIキー発行

Immichの画面でAPIキーを発行しておきます。
権限は以下の3つがあれば足ります(Immich 2.7.5のOpenAPIスペックで確認)。

権限 使うAPI 用途
album.read GET /api/albumsGET /api/albums/{id} アルバム一覧・アルバムの写真・デイリーピックアップ
asset.read POST /api/search/metadata お気に入り(source: "favorites"
asset.view GET /api/assets/{id}/thumbnail 写真本体(preview)とサムネイル

asset.download など、それ以外の権限は使っていません。
フォトフレームは写真を表示するだけなので、必要最小限の権限にしておくと安心ですね。

権限が足りないと、Immichは 403Missing required permission: <権限名> を返します。
切り分けには tools/verification/immich_probe.py が使えます。

Step 5: 認証情報と設定ファイル

cp .env.sample .env
cp config/settings.sample.json config/settings.json

.env にImmichのURLと、Step 4で発行したAPIキーを書きます。
また、.envGID_* にはデバイスのグループIDを入れるのですが、これはホストごとに異なるので、以下で確認してから記入してください。

getent group video render input gpio

Step 6: 起動

docker compose pull
docker compose up -d
docker compose logs -f

restart: unless-stopped を指定しているので、OS起動時の自動起動もこれでOKです。

実機上で docker compose build してネイティブビルドすることもできますが、Zero 2 Wだと10分以上かかってCPUも張り付きます。
基本はGHCRのイメージをpullする方が手っ取り早いです。

写真の表示方法や切り替え間隔などは、画面の設定メニューか config/settings.json で変更できます。

サクッと完成

おわりに

本当は Google PhotosのLibrary API にも対応させたかったのですが、制限が厳しくなって不自由なので、今回は断念しました。
今後は Picker API を使って対応する方法を模索したいと思います。

市販品に不満があって、Immichをセルフホストしている方は、気軽にできるちょうどいい工作になると思いますので、ラズパイで何か作ってみたいと思っている方はやってみてはいかがでしょうか。
現状はラズパイシリーズの本体が安くは無いのが残念なところですが…😣

以上!

コメント

タイトルとURLをコピーしました