Raspberry Pi 5:3.5インチLCDとHDMIモニタを1コマンドで切り替える運用基盤の作り方
はじめに
ディスプレイ付きのRaspberry Piで、開発時は大きいHDMIモニタを使い、本番運用時は3.5インチLCDで動かす。そんな運用を1コマンド(+再起動)で切り替えられるようにする手順をまとめます。
完成後はこんな風に使えます。
# 現在の状態を確認
$ display-mode status
現在のモード: lcd
...
# 開発モードに切り替え(HDMIモニタ + Pi Connect screen share利用可)
$ sudo display-mode hdmi
[INFO] HDMI モードへ切り替えます (現在: lcd)
[ OK ] config.txt を HDMI モード設定に切り替えました
...
今すぐ再起動しますか? [y/N]: y
# 本番モードに戻す(3.5インチLCD表示)
$ sudo display-mode lcd
短縮エイリアスとしてdmも用意するので、sudo dm hdmi sudo dm lcd dm status でも操作できます。
こんな人におすすめ
- ディスプレイ付きRaspberry Piで、開発と本番でディスプレイを切り替えたい方
- LCD-show系のドライバを入れたら、Raspberry Pi Connectのscreen shareが動かなくなって困っている方
- LCDモードからWaylandデスクトップに戻したい/共存させたい方
環境
| 項目 | 内容 |
|---|---|
| ハード | Raspberry Pi 5 |
| LCD | 3.5インチSPI接続LCD(MHS35 / ILI9486チップ) |
| HDMIモニタ | 一般的なHDMIモニタ |
| OS | Raspberry Pi OS Bookworm 64-bit (kernel 6.12.x) |
| 標準デスクトップ | labwc (Wayland) ※OS同梱 |
| 前提状態 | LCD-show系スクリプト適用済み(LCDに表示できている状態) |
| ネットワーク | SSH接続およびRaspberry Pi Connect設定済み |
ホスト名は記事内では仮にmypi、ユーザー名はpiとして記載します。読み替えてください。
何が起きていたか:問題の特定
症状
LCD-show系のドライバスクリプトを適用すると、3.5インチLCDには表示できるようになりますが、Raspberry Pi Connectのscreen shareが動かなくなります。rpi-connect doctorを実行すると、こんな出力が返ってきます。
$ rpi-connect doctor
Screen sharing is supported by this version of rpi-connect
✗ Wayland compositor available - Screen sharing is unavailable unless an active, supported Wayland desktop session is found for your user
✓ Communication with Raspberry Pi Connect WebSocket server
✓ Communication with Raspberry Pi Connect API
✓ Authentication with Raspberry Pi Connect API
✓ Peer-to-peer connection candidate via STUN
✓ Peer-to-peer connection candidate via TURN
✗ Some checks failed
「Wayland compositor available」が ✗。Pi Connectのscreen shareはWaylandセッション必須ですが、システムがX11で起動していて要件を満たしていない、ということを示しています。
加えて、HDMIモニタを繋いでも、デスクトップが表示されず、テキストコンソール(ログイン後のmotdなど)しか出ない症状も発生します。
原因:LCD-show系がシステムに仕込んだ3つの仕掛け
LCD-show系のスクリプトは、3.5インチLCDを動かすために、システムに3つの大きな変更を入れます。
仕掛け1:KMS(Kernel Mode Setting)を無効化
/boot/firmware/config.txtの以下が変更されています。
# Enable DRM VC4 V3D driver
#dtoverlay=vc4-kms-v3d # ← コメントアウト:KMS無効化
disable_fw_kms_setup=1 # ← 追加:ファームウェアKMSも無効
KMSが無効だと、Wayland(labwc/Wayfire)はディスプレイを掴めません。これがscreen share失敗の根本原因です。
仕掛け2:lightdmを止めてxinitから直接Xorg起動
lightdmがdisableされ、代わりに/etc/systemd/system/getty@tty1.service.d/autologin.confでtty1を自動ログインさせ、~/.bash_profileからstartxを直接起動する仕組みになっています。
# ~/.bash_profile(LCD-show適用後)
export FRAMEBUFFER=/dev/fb1
startx 2> /tmp/log_output.txt
FRAMEBUFFER=/dev/fb1によって、Xorgが LCD(fb1)に直接描画する仕組みです。
仕掛け3:X11前提のタッチパネル設定
/etc/X11/xorg.conf.d/99-calibration.confが配置され、ADS7846タッチコントローラ向けのX11設定が入ります。これも「システムをX11前提にする」効果を持っています。
まとめ:なぜscreen shareが動かないのか
Pi Connectのscreen shareは Waylandが必須ですが、LCD-show系がKMS無効化+lightdm停止+X11前提化を行うため、Waylandが起動できません。この3つを切り戻せばscreen shareは復活しますが、そうすると今度は3.5インチLCDが映らなくなります。
そこで、設定を切り替えて両方使えるようにするのが本記事のゴールです。
設計:両モード切り替えの戦略
方針
3つの設計判断をしました。
1. ファイル丸ごとコピー方式
config.txtを編集する際、sedで1行ずつ書き換えると失敗時の復旧が難しくなります。代わりに、両モードのconfig.txtを完成形のテンプレートとして保存しておき、切り替え時はコピーするだけにします。
/boot/firmware/config.txt ← 現在モードの実体
/boot/firmware/config.txt.lcd-mode ← LCDモードの完成形テンプレート
/boot/firmware/config.txt.hdmi-mode ← HDMIモードの完成形テンプレート
2. 状態管理ファイルで現在モードを明示
config.txtの中身から現在モードを推測するのではなく、/etc/display-mode/currentに明示的に書き込んで管理します。これにより冪等性チェック(既に目的モードなら何もしない)が確実になります。
3. 安全策の徹底
- 切り替え前に
/boot/firmware/config.txt.last-stateへ自動バックアップ - 操作ログを
/var/log/display-mode.logに記録 - 必須テンプレートが揃っていない場合は即終了
- 再起動前に確認プロンプト(自動化用に
-yフラグでスキップ可)
各モードの差分マトリクス
| 項目 | LCDモード(X11) | HDMIモード(Wayland) |
|---|---|---|
dtoverlay=vc4-kms-v3d |
コメントアウト | 有効 |
disable_fw_kms_setup=1 |
有効 | コメントアウト |
dtoverlay=mhs35:rotate=270 |
有効 | コメントアウト |
hdmi_force_hotplug=1 |
有効(LCD偽装用) | 有効(実HDMI検出用) |
hdmi_group=2 / hdmi_mode=* / hdmi_cvt / hdmi_drive=2
|
有効(480x320偽装) | コメントアウト |
~/.bash_profile |
startx自動起動あり | startx無効化 |
/etc/X11/xorg.conf.d/99-calibration.conf |
配置 | 退避 |
lightdm.service |
disabled | enabled |
default.target |
multi-user.target | graphical.target |
これらが切り替え時に変更されるすべてです。
実装手順
ここから実際の構築手順です。全コマンドはSSHから実行可能です。万一のトラブルに備えて、HDMIモニタを物理接続しておくことを推奨します(保険として)。
Step 1:両モードのconfig.txtテンプレート作成
LCDモードで動いている現在のconfig.txtを、両モードのテンプレートとして複製します。
# 現状を「lcd-mode」テンプレートとして保存(今のconfig.txtがLCDモード)
sudo cp /boot/firmware/config.txt /boot/firmware/config.txt.lcd-mode
# HDMIモード用テンプレートを作成(次のStepで編集)
sudo cp /boot/firmware/config.txt /boot/firmware/config.txt.hdmi-mode
# 緊急復旧用バックアップ
sudo cp /boot/firmware/config.txt /boot/firmware/config.txt.original-bak
# ~/.bash_profile もバックアップ(LCDモード版)
cp ~/.bash_profile ~/.bash_profile.lcd-bak
# 確認
ls -la /boot/firmware/config.txt*
ls -la ~/.bash_profile*
Step 2:HDMIモード用config.txtの編集
/boot/firmware/config.txt.hdmi-modeだけを編集します(現役のconfig.txtには触りません)。
sedで一括修正する場合:
sudo sed -i \
-e 's|^#dtoverlay=vc4-kms-v3d|dtoverlay=vc4-kms-v3d|' \
-e 's|^disable_fw_kms_setup=1|#disable_fw_kms_setup=1|' \
-e 's|^dtoverlay=mhs35:rotate=270|#dtoverlay=mhs35:rotate=270|' \
-e 's|^hdmi_group=2|#hdmi_group=2|' \
-e 's|^hdmi_mode=1$|#hdmi_mode=1|' \
-e 's|^hdmi_mode=87|#hdmi_mode=87|' \
-e 's|^hdmi_cvt 480 320 60 6 0 0 0|#hdmi_cvt 480 320 60 6 0 0 0|' \
-e 's|^hdmi_drive=2|#hdmi_drive=2|' \
/boot/firmware/config.txt.hdmi-mode
注:hdmi_force_hotplug=1は両モードで有効のままにします(LCDモードでは偽装480x320のため、HDMIモードでは実モニタ検出のため、どちらにも必要)。
編集結果を確認:
diff /boot/firmware/config.txt.lcd-mode /boot/firmware/config.txt.hdmi-mode
8行分の差分が表示されれば成功です。
Step 3:HDMIモードの手動適用と動作確認
スクリプト化する前に、まず手動で切り替えが動くことを確認します。
# config.txt をHDMIモードに切り替え
sudo cp /boot/firmware/config.txt.hdmi-mode /boot/firmware/config.txt
# bash_profileのstartx自動起動を無効化
cat > ~/.bash_profile << 'EOF'
# HDMI mode: startx auto-launch disabled
EOF
# 99-calibration.conf を退避
sudo mkdir -p /etc/display-mode/x11-bak
sudo mv /etc/X11/xorg.conf.d/99-calibration.conf /etc/display-mode/x11-bak/
# lightdm有効化、graphical.targetをデフォルトに
sudo systemctl enable lightdm
sudo systemctl set-default graphical.target
# 再起動
sudo reboot
再起動後、HDMIモニタにlabwcのデスクトップが表示され、タスクバーにRaspberry Pi Connectのアイコンが出現すれば成功です。SSHで再接続してrpi-connect doctorを実行:
$ rpi-connect doctor
Screen sharing is supported by this version of rpi-connect
✓ Wayland compositor available
✓ Screen sharing services enabled and active
✓ Communication with Raspberry Pi Connect WebSocket server
✓ Communication with Raspberry Pi Connect API
✓ Authentication with Raspberry Pi Connect API
✓ Peer-to-peer connection candidate via STUN
✓ Peer-to-peer connection candidate via TURN
全項目 ✓ になれば、screen shareが完全に使える状態です。
Step 4:LCDモードへの手動復帰と動作確認
逆方向の切り替えも手動で確認しておきます。
# config.txt をLCDモードに切り替え
sudo cp /boot/firmware/config.txt.lcd-mode /boot/firmware/config.txt
# bash_profile を復元
cp ~/.bash_profile.lcd-bak ~/.bash_profile
sudo chown pi:pi ~/.bash_profile
# 99-calibration.conf を復元
sudo mv /etc/display-mode/x11-bak/99-calibration.conf /etc/X11/xorg.conf.d/
# lightdm無効化、multi-user.targetをデフォルトに
sudo systemctl disable lightdm
sudo systemctl set-default multi-user.target
# 再起動
sudo reboot
再起動後、3.5インチLCDに従来通りのデスクトップが表示されればOKです。
両方向の切り替えが手動で動くことが確認できたら、いよいよスクリプト化します。
Step 5:display-modeスクリプトの設置
スクリプトを/usr/local/bin/display-modeとして配置します。スクリプトの全文は記事末尾の折りたたみセクションに掲載しています。
# スクリプトを ~/display-mode.sh として作成(全文は折りたたみ参照)
# 作成後:
chmod +x ~/display-mode.sh
# 動作テスト(LCDモードで実行している前提)
~/display-mode.sh status
# /usr/local/binに正式配置
sudo cp ~/display-mode.sh /usr/local/bin/display-mode
sudo chown root:root /usr/local/bin/display-mode
sudo chmod 755 /usr/local/bin/display-mode
# ログファイル準備
sudo touch /var/log/display-mode.log
sudo chmod 644 /var/log/display-mode.log
# 動作確認
which display-mode
display-mode --help
display-mode status
Step 6:dmエイリアス作成
短縮コマンドとしてdmを作ります。シンボリックリンク方式を使います。
sudo ln -s /usr/local/bin/display-mode /usr/local/bin/dm
# 確認
ls -la /usr/local/bin/dm
which dm
dm status
sudo dm status
シンボリックリンクなので、display-modeを更新すればdmも自動的に最新版を指します。
bashエイリアス(alias dm='display-mode')ではダメな理由
aliasはsudo越しに展開されないため、sudo dm hdmiが動きません。シンボリックリンクなら通常のコマンドとして扱われ、sudo付きでも問題なく動作します。
display-modeスクリプトの設計ポイント
スクリプトの主要な工夫点を抜粋して説明します。
1. ファイル丸ごとコピーで切り替え
# HDMIモードへ切り替えのコア部分
cp "${CONFIG_TXT}" "${CONFIG_LAST_STATE}" # 直前バックアップ
cp "${CONFIG_HDMI_MODE}" "${CONFIG_TXT}" # 完成形をコピー
sedで書き換えるのではなく、テンプレートファイルを丸ごとコピーするだけ。実装がシンプルで、失敗時の挙動も予測可能。
2. 状態管理ファイル
# モード判定の優先順位
get_current_mode() {
if [[ -f "${STATE_FILE}" ]]; then
cat "${STATE_FILE}" | tr -d '[:space:]'
else
# state file が無い場合は config.txt から推定(フォールバック)
if grep -q '^dtoverlay=mhs35' "${CONFIG_TXT}" 2>/dev/null; then
echo "lcd"
elif grep -q '^dtoverlay=vc4-kms-v3d' "${CONFIG_TXT}" 2>/dev/null; then
echo "hdmi"
else
echo "unknown"
fi
fi
}
/etc/display-mode/currentに"hdmi"または"lcd"を書き込み、これを真の状態として使います。config.txtの内容から推測するフォールバックも用意。
3. 冪等性
# 既に目的モードなら何もしない
local current
current="$(get_current_mode)"
if [[ "${current}" == "hdmi" ]]; then
warn "既に HDMI モードです。何もしません。"
exit 0
fi
同じコマンドを何度実行しても同じ結果になる。誤操作で意図しない再起動が起きることを防ぎます。
4. ログ記録
log() {
local level="$1"
shift
local msg="$*"
local ts="$(date '+%Y-%m-%d %H:%M:%S')"
if [[ $EUID -eq 0 ]]; then
echo "[${ts}] [${level}] ${msg}" >> "${LOG_FILE}" 2>/dev/null || true
fi
}
操作履歴が/var/log/display-mode.logに残ります。トラブル時の追跡に有用。
5. 必須ファイル存在チェック
check_required_files() {
local mode="$1"
if [[ ! -f "${CONFIG_LCD_MODE}" ]]; then
err "テンプレートファイルがありません: ${CONFIG_LCD_MODE}"
exit 1
fi
# ... 他のテンプレートも同様
}
切り替え処理の途中でファイルが見つからなくて中途半端に終わるのを防ぎます。
スクリプト全文は記事末尾の折りたたみセクションを参照してください。
動作確認とトラブルシューティング
rpi-connect doctorの見方
screen shareの動作確認はこのコマンド一発でできます。
$ rpi-connect doctor
- 全項目 ✓:screen share正常動作
- 「Wayland compositor available」が ✗:システムがX11で起動している(LCDモードか、HDMIモード切替失敗)
- 「Screen sharing services enabled and active」が ✗:wayvncサービスが起動できていない(Waylandは動いているが内部サービスに問題)
「Cannot find any crtc or sizes」が出た時
dmesgにこのエラーが出る場合、HDMIモニタが認識されていません。
[ 0.905567] vc4-drm axi:gpu: [drm] Cannot find any crtc or sizes
対処:
- HDMIケーブルが両端しっかり挿さっているか確認(半挿しはホットプラグ検出失敗の典型原因)
- Pi 5は HDMI0 ポート(電源側)に挿すのが基本
- それでも検出されない場合は
config.txtにhdmi_force_hotplug=1を追加(既に有効化済み)
LCDが映らない時の手動緊急復帰
スクリプトが何らかの理由で動かない場合の、コマンドだけでLCDモードに戻す手順です。
# === 緊急LCDモード復帰 ===
sudo cp /boot/firmware/config.txt.lcd-mode /boot/firmware/config.txt
cp ~/.bash_profile.lcd-bak ~/.bash_profile
sudo chown pi:pi ~/.bash_profile
sudo mv /etc/display-mode/x11-bak/99-calibration.conf /etc/X11/xorg.conf.d/ 2>/dev/null
sudo systemctl disable lightdm
sudo systemctl set-default multi-user.target
sudo reboot
# === 緊急HDMIモード復帰 ===
sudo cp /boot/firmware/config.txt.hdmi-mode /boot/firmware/config.txt
cat > ~/.bash_profile << 'EOF'
# HDMI mode: startx auto-launch disabled
EOF
sudo chown pi:pi ~/.bash_profile
sudo mv /etc/X11/xorg.conf.d/99-calibration.conf /etc/display-mode/x11-bak/ 2>/dev/null
sudo systemctl enable lightdm
sudo systemctl set-default graphical.target
sudo reboot
技術メモ:bashの var="$(cmd)" || ... の罠
スクリプトを書いている時にハマったので共有します。
statusサブコマンドでlightdmの状態を表示する際、最初こんな書き方をしました。
RESULT="$(systemctl is-enabled lightdm 2>/dev/null)" || RESULT="unknown"
意図:is-enabledが成功したら出力をRESULTに入れる。失敗したら"unknown"をフォールバック。
実際の動き:
$ systemctl is-enabled lightdm
disabled
$ echo $?
1
is-enabledは「disabledである」という情報を出力しつつ、exit code 1 を返します。すると:
-
$(...)の内側が実行 → stdout にdisabled出力、exit 1 -
RESULT="disabled"で代入が成功 - 代入文全体のexit codeは、コマンド置換のexit code(=1)を引き継ぐ
- exit 1 なので
||の右辺が実行 →RESULT="unknown"で上書き - 結果:RESULTは
unknownになる(disabledが消える)
正しい書き方:
RESULT="$(systemctl is-enabled lightdm 2>/dev/null || true)"
[[ -z "${RESULT}" ]] && RESULT="unknown"
|| trueで代入文のexit codeを強制的に0にしてから、改めて空文字チェックでフォールバックを判定します。
set -eは無関係で、これは変数代入のexit codeがコマンド置換のexit codeを引き継ぐというbashの仕様です。systemctlのように「情報を出力しつつnon-zeroを返す」コマンドと組み合わせる際は要注意。
まとめ
達成したこと
- LCD-show系で塞がれていたPi Connect screen shareを復活させた
- 3.5インチLCD表示とHDMIモニタ表示を、1コマンド+再起動で切り替え可能にした
- 状態管理、ログ、安全策を含むプロダクション品質のスクリプトを構築した
完成したファイル構成
/usr/local/bin/display-mode ← スクリプト本体
/usr/local/bin/dm ← エイリアス(シンボリックリンク)
/etc/display-mode/current ← 現在モード記録
/etc/display-mode/x11-bak/ ← X11設定退避先
/var/log/display-mode.log ← 操作ログ
/boot/firmware/config.txt ← 現在モードの実体
/boot/firmware/config.txt.lcd-mode ← LCDモードテンプレート
/boot/firmware/config.txt.hdmi-mode ← HDMIモードテンプレート
/boot/firmware/config.txt.last-state ← 直前状態(毎回更新)
/boot/firmware/config.txt.original-bak ← 最初期バックアップ
~/.bash_profile ← 現在モード用
~/.bash_profile.lcd-bak ← LCDモード版バックアップ
日常運用コマンド一覧
# 現在のモードを確認
display-mode status # または dm status
# HDMIモード(開発時、screen share可)
sudo display-mode hdmi # または sudo dm hdmi
sudo display-mode hdmi -y # 確認スキップ
# LCDモード(本番運用)
sudo display-mode lcd # または sudo dm lcd
sudo display-mode lcd -y # 確認スキップ
# ヘルプとログ
display-mode --help
sudo cat /var/log/display-mode.log
参考リンク
付録:display-modeスクリプト全文
クリックして展開(約400行)
#!/bin/bash
#
# display-mode: 3.5インチLCD と HDMIモニタの切り替えスクリプト
#
# 使い方:
# sudo display-mode hdmi HDMIモード(Wayland + Pi Connect screen share可)
# sudo display-mode lcd LCDモード(X11 + 3.5インチLCD直接描画)
# display-mode status 現在のモードを表示
# display-mode --help このヘルプを表示
#
# オプション:
# -y, --yes 再起動確認プロンプトをスキップ
#
set -euo pipefail
# ===== 定数定義 =====
readonly USER_NAME="pi"
readonly USER_HOME="/home/${USER_NAME}"
readonly USER_BASH_PROFILE="${USER_HOME}/.bash_profile"
readonly USER_BASH_PROFILE_LCD_BAK="${USER_HOME}/.bash_profile.lcd-bak"
readonly CONFIG_TXT="/boot/firmware/config.txt"
readonly CONFIG_LCD_MODE="/boot/firmware/config.txt.lcd-mode"
readonly CONFIG_HDMI_MODE="/boot/firmware/config.txt.hdmi-mode"
readonly CONFIG_LAST_STATE="/boot/firmware/config.txt.last-state"
readonly XORG_CONF_DIR="/etc/X11/xorg.conf.d"
readonly CALIBRATION_CONF="${XORG_CONF_DIR}/99-calibration.conf"
readonly X11_BAK_DIR="/etc/display-mode/x11-bak"
readonly CALIBRATION_BAK="${X11_BAK_DIR}/99-calibration.conf"
readonly STATE_DIR="/etc/display-mode"
readonly STATE_FILE="${STATE_DIR}/current"
readonly LOG_FILE="/var/log/display-mode.log"
# HDMIモード用 bash_profile の中身
readonly BASH_PROFILE_HDMI_CONTENT="# HDMI mode: startx auto-launch disabled
# This file is managed by display-mode script.
# To restore LCD mode, run: sudo display-mode lcd
"
# ===== カラー出力(端末対応時のみ) =====
if [[ -t 1 ]]; then
readonly C_RED=$'\033[31m'
readonly C_GREEN=$'\033[32m'
readonly C_YELLOW=$'\033[33m'
readonly C_BLUE=$'\033[34m'
readonly C_BOLD=$'\033[1m'
readonly C_RESET=$'\033[0m'
else
readonly C_RED=""
readonly C_GREEN=""
readonly C_YELLOW=""
readonly C_BLUE=""
readonly C_BOLD=""
readonly C_RESET=""
fi
# ===== ロギング =====
log() {
local level="$1"
shift
local msg="$*"
local ts
ts="$(date '+%Y-%m-%d %H:%M:%S')"
if [[ $EUID -eq 0 ]]; then
echo "[${ts}] [${level}] ${msg}" >> "${LOG_FILE}" 2>/dev/null || true
fi
}
info() { echo "${C_BLUE}[INFO]${C_RESET} $*"; log "INFO" "$*"; }
ok() { echo "${C_GREEN}[ OK ]${C_RESET} $*"; log "OK" "$*"; }
warn() { echo "${C_YELLOW}[WARN]${C_RESET} $*"; log "WARN" "$*"; }
err() { echo "${C_RED}[ERR ]${C_RESET} $*" >&2; log "ERROR" "$*"; }
# ===== ヘルプ =====
show_help() {
cat << 'EOF'
display-mode: Raspberry Pi のディスプレイモード切り替えスクリプト
使い方:
sudo display-mode hdmi HDMIモード(Wayland + Pi Connect screen share可)
sudo display-mode lcd LCDモード(X11 + 3.5インチLCD直接描画)
display-mode status 現在のモードを表示
display-mode --help このヘルプを表示
オプション:
-y, --yes 再起動確認プロンプトをスキップ
ログ:
/var/log/display-mode.log
EOF
}
# ===== 必要ファイル存在チェック =====
check_required_files() {
local mode="$1"
local missing=0
if [[ ! -f "${CONFIG_LCD_MODE}" ]]; then
err "テンプレートファイルがありません: ${CONFIG_LCD_MODE}"
missing=1
fi
if [[ ! -f "${CONFIG_HDMI_MODE}" ]]; then
err "テンプレートファイルがありません: ${CONFIG_HDMI_MODE}"
missing=1
fi
if [[ "${mode}" == "lcd" ]]; then
if [[ ! -f "${USER_BASH_PROFILE_LCD_BAK}" ]]; then
err "LCD用 bash_profile バックアップがありません: ${USER_BASH_PROFILE_LCD_BAK}"
missing=1
fi
if [[ ! -f "${CALIBRATION_BAK}" ]] && [[ ! -f "${CALIBRATION_CONF}" ]]; then
err "99-calibration.conf がどこにも見つかりません"
missing=1
fi
fi
if [[ ${missing} -ne 0 ]]; then
err "必要なファイルが揃っていません。中止します。"
exit 1
fi
}
# ===== 現在モード取得 =====
get_current_mode() {
if [[ -f "${STATE_FILE}" ]]; then
cat "${STATE_FILE}" | tr -d '[:space:]'
else
if grep -q '^dtoverlay=mhs35' "${CONFIG_TXT}" 2>/dev/null; then
echo "lcd"
elif grep -q '^dtoverlay=vc4-kms-v3d' "${CONFIG_TXT}" 2>/dev/null; then
echo "hdmi"
else
echo "unknown"
fi
fi
}
# ===== モード書き込み =====
set_current_mode() {
local mode="$1"
mkdir -p "${STATE_DIR}"
echo "${mode}" > "${STATE_FILE}"
}
# ===== status サブコマンド =====
cmd_status() {
local current
current="$(get_current_mode)"
echo "${C_BOLD}=== display-mode status ===${C_RESET}"
echo ""
echo "現在のモード: ${C_BOLD}${current}${C_RESET}"
echo ""
echo "--- 詳細 ---"
echo "config.txt の主要設定:"
grep -E '^[^#]*(vc4-kms-v3d|mhs35|hdmi_force_hotplug|disable_fw_kms_setup)' "${CONFIG_TXT}" 2>/dev/null | sed 's/^/ /'
echo ""
local lightdm_enabled lightdm_active default_target
lightdm_enabled="$(systemctl is-enabled lightdm 2>/dev/null || true)"
[[ -z "${lightdm_enabled}" ]] && lightdm_enabled="unknown"
lightdm_active="$(systemctl is-active lightdm 2>/dev/null || true)"
[[ -z "${lightdm_active}" ]] && lightdm_active="unknown"
echo "lightdm: ${lightdm_enabled} / ${lightdm_active}"
default_target="$(systemctl get-default 2>/dev/null || true)"
[[ -z "${default_target}" ]] && default_target="unknown"
echo "default target: ${default_target}"
echo ""
echo "テンプレートファイル:"
for f in "${CONFIG_LCD_MODE}" "${CONFIG_HDMI_MODE}"; do
if [[ -f "${f}" ]]; then
echo " ✓ $(basename ${f})"
else
echo " ✗ $(basename ${f}) ${C_RED}(missing)${C_RESET}"
fi
done
if [[ -f "${LOG_FILE}" ]]; then
echo ""
echo "--- 最近のログ(末尾5行)---"
tail -5 "${LOG_FILE}" 2>/dev/null | sed 's/^/ /' || echo " (ログ読み取り不可、sudoが必要かも)"
fi
}
# ===== root権限チェック =====
require_root() {
if [[ $EUID -ne 0 ]]; then
err "このコマンドは root 権限が必要です。sudo を付けてください。"
exit 1
fi
}
# ===== 確認プロンプト =====
confirm_reboot() {
local skip_confirm="$1"
if [[ "${skip_confirm}" == "yes" ]]; then
info "再起動確認をスキップします(-y 指定)"
return 0
fi
echo ""
echo "${C_YELLOW}${C_BOLD}設定変更が完了しました。反映には再起動が必要です。${C_RESET}"
echo -n "今すぐ再起動しますか? [y/N]: "
read -r answer
case "${answer}" in
[yY]|[yY][eE][sS]) return 0 ;;
*)
warn "再起動はスキップされました。手動で 'sudo reboot' を実行してください。"
return 1
;;
esac
}
# ===== HDMIモード切り替え =====
cmd_hdmi() {
local skip_confirm="$1"
require_root
check_required_files "hdmi"
local current
current="$(get_current_mode)"
if [[ "${current}" == "hdmi" ]]; then
warn "既に HDMI モードです。何もしません。"
info "現在の設定を確認するには: display-mode status"
exit 0
fi
info "HDMI モードへ切り替えます (現在: ${current})"
cp "${CONFIG_TXT}" "${CONFIG_LAST_STATE}"
ok "config.txt を ${CONFIG_LAST_STATE} にバックアップしました"
cp "${CONFIG_HDMI_MODE}" "${CONFIG_TXT}"
ok "config.txt を HDMI モード設定に切り替えました"
echo "${BASH_PROFILE_HDMI_CONTENT}" > "${USER_BASH_PROFILE}"
chown "${USER_NAME}:${USER_NAME}" "${USER_BASH_PROFILE}"
chmod 755 "${USER_BASH_PROFILE}"
ok "${USER_BASH_PROFILE} を HDMI モード版に更新しました"
mkdir -p "${X11_BAK_DIR}"
if [[ -f "${CALIBRATION_CONF}" ]]; then
mv "${CALIBRATION_CONF}" "${CALIBRATION_BAK}"
ok "99-calibration.conf を ${X11_BAK_DIR}/ に退避しました"
else
info "99-calibration.conf は既に退避済みです"
fi
systemctl enable lightdm >/dev/null 2>&1
systemctl set-default graphical.target >/dev/null 2>&1
ok "lightdm を有効化、graphical.target をデフォルトに設定しました"
set_current_mode "hdmi"
ok "状態ファイル ${STATE_FILE} を 'hdmi' に更新しました"
info "HDMI モードへの切り替えが完了しました"
if confirm_reboot "${skip_confirm}"; then
info "再起動します..."
log "INFO" "Rebooting to apply HDMI mode"
sleep 1
reboot
fi
}
# ===== LCDモード切り替え =====
cmd_lcd() {
local skip_confirm="$1"
require_root
check_required_files "lcd"
local current
current="$(get_current_mode)"
if [[ "${current}" == "lcd" ]]; then
warn "既に LCD モードです。何もしません。"
info "現在の設定を確認するには: display-mode status"
exit 0
fi
info "LCD モードへ切り替えます (現在: ${current})"
cp "${CONFIG_TXT}" "${CONFIG_LAST_STATE}"
ok "config.txt を ${CONFIG_LAST_STATE} にバックアップしました"
cp "${CONFIG_LCD_MODE}" "${CONFIG_TXT}"
ok "config.txt を LCD モード設定に切り替えました"
cp "${USER_BASH_PROFILE_LCD_BAK}" "${USER_BASH_PROFILE}"
chown "${USER_NAME}:${USER_NAME}" "${USER_BASH_PROFILE}"
chmod 755 "${USER_BASH_PROFILE}"
ok "${USER_BASH_PROFILE} を LCD モード版に復元しました"
if [[ -f "${CALIBRATION_BAK}" ]]; then
mv "${CALIBRATION_BAK}" "${CALIBRATION_CONF}"
ok "99-calibration.conf を ${XORG_CONF_DIR}/ に復元しました"
elif [[ -f "${CALIBRATION_CONF}" ]]; then
info "99-calibration.conf は既に正しい場所にあります"
else
warn "99-calibration.conf が見つかりません(タッチ機能に影響する可能性あり)"
fi
systemctl disable lightdm >/dev/null 2>&1
systemctl set-default multi-user.target >/dev/null 2>&1
ok "lightdm を無効化、multi-user.target をデフォルトに設定しました"
set_current_mode "lcd"
ok "状態ファイル ${STATE_FILE} を 'lcd' に更新しました"
info "LCD モードへの切り替えが完了しました"
if confirm_reboot "${skip_confirm}"; then
info "再起動します..."
log "INFO" "Rebooting to apply LCD mode"
sleep 1
reboot
fi
}
# ===== 引数パース =====
main() {
if [[ $# -eq 0 ]]; then
show_help
exit 0
fi
local subcommand=""
local skip_confirm="no"
for arg in "$@"; do
case "${arg}" in
-y|--yes) skip_confirm="yes" ;;
-h|--help) show_help; exit 0 ;;
hdmi|lcd|status)
if [[ -z "${subcommand}" ]]; then
subcommand="${arg}"
else
err "サブコマンドは1つだけ指定してください"
exit 1
fi
;;
*)
err "不明な引数: ${arg}"
show_help
exit 1
;;
esac
done
case "${subcommand}" in
hdmi) cmd_hdmi "${skip_confirm}" ;;
lcd) cmd_lcd "${skip_confirm}" ;;
status) cmd_status ;;
"") show_help; exit 0 ;;
*) err "未知のサブコマンド: ${subcommand}"; exit 1 ;;
esac
}
main "$@"