🛠️

Raspberry Piで作るTHINKLET Viewer

2024/06/11に公開

はじめに

 2024年4月からフェアリーデバイセズ株式会社のプロダクト開発部に入りました伊達です。

 入社前から開発機としてTHINKLETの貸し出しを受けていましたが、一番衝撃を
受けたのはGUIのないAndroid端末というのが存在するということでした。

 THINKLETの設定は、PCに接続した状態で、scrcpyコマンドを動かし、adbを経由してPC上に操作画面を表示して行うよう案内されています。これで必要十分ではあるのですが、設定などをちょっと変更したり確認したりしたい場合には、PCを出さなくてはならず、けっこう不便です。


macOSでscrcpyを動かす画像

 経験的に、エンドユーザーでのトラブルの6−7割はネットワークに接続可能になる前に発生します。THINKLETは、単体で作業の現場で利用することを想定されているのに、トラブルがあった場合にPCを持参していないと、状況もよくわからないというのは困りものです。

 そこで、THINKLETといっしょに持ち運んで使えるような、画面表示端末が作れないかと考えてみました。scrcpyコマンドはLinuxシステムでも動作するので、Raspberry Piのような小型のLinuxマシンにディスプレイを接続して、scrcpyを動かしてしまえば良さそうです。

ターゲットデバイス

 ターゲットデバイスとしては、ここではRaspberry Pi 4 4GBを用意しました。Raspberry Pi 4,5の64bit環境以外は、ここで解説する方法と大きく手順が異なり、そのまま適用できませんので注意してください。

 ディスプレイは、スマートフォンと同様のタッチ操作を簡単に実現するためにDSI接続のLCDを用意します。DSI(Display Serial Interface)は、ディスプレイを接続するためのシリアルインターフェイスの規格で、主にモバイルデバイスや小型の組み込みシステムで使用されています。フラットケーブル1本で、LCDとタッチパネルの接続を行うことができます。

 DSI接続のディスプレイは、Amazonなどで単体で購入できます。今回は、OSOYOO 5インチDSIタッチスクリーンを使用しました。

 他にも、3Aが供給できるACアダプタ、電源用のUSBケーブル、システム用のmicroSDカード、THINKLETと接続するUSBケーブル(Type-A/C)が必要です。


機材一覧

 また、Raspberry Pi 5で使用する場合は、DSI用のフラットケーブルの端子が変更されていますので、専用のフラットケーブルの用意が必要です。

 DSIのフラットケーブルの配線は、コネクタの黒い部分を引き出し、フラットケーブルを差し込んで、黒い部分を押し込むようにしてもとに戻します。ケーブルの差し込み方向は、写真を参考にしてください。


 OSは、Raspberry Pi OSのBookworm版を使用します。Bookwormでは、ディスプレイサーバーとしてUNIXシステムでは長いこと採用されてきたX11が廃止され、かわりにWaylandが採用されています。SDカードへのインストール方法などは、ここでは解説しませんので、オフィシャルサイトなどの解説に従ってBookwormの64bit Desktop環境をインストールしてください。

 作業はsshを使ったリモート接続で行います。Raspberry Pi Imagerでのインストール時には、ネットワークの設定とパスワード認証でのssh接続を許可してください。ユーザーは、「fduser」に設定していますので、違うユーザー名を使用する場合は、文中での指定を適宜読み替えてください。

画面輝度の変更

 初期状態では、LCDの輝度設定は最大値になっていて、見ているだけで眩しい状態です。

 輝度設定を変更するには、ACPIをsysfsから操作します。"/sys/class/backlight/"以下を見ると、「10-0045」が生えているのが確認できます。どうやら、これがDSI接続のLCDのようです。

fduser@pi4:~ $ ls /sys/class/backlight/10-0045/
actual_brightness  brightness  max_brightness  scale      type
bl_power           device      power           subsystem  uevent

 輝度に関係しそうなファイルを見てみます。初期状態では、輝度の値は255で一番明るい設定になっています。

fduser@pi4:~ $ cat /sys/class/backlight/10-0045/actual_brightness
255
fduser@pi4:~ $ cat /sys/class/backlight/10-0045/brightness
255
fduser@pi4:~ $ cat /sys/class/backlight/10-0045/max_brightness
255

 sysfsに50を書き込むと、画面が眩しくなくなりました。sysfsなので、値を書き込むには管理者権限が必要です。

fduser@pi4:~ $ sudo su -c "echo 50 > /sys/class/backlight/10-0045/brightness"

 再起動すると、起動時のスプラッシュスクリーンの途中から画面が暗くなるのが確認できます。一度値を設定すると、再起動しても値は維持されます。心配な場合は、udevルールを書いたり、sysfsutilsを使って/etc/sysfs.conf経由で設定してください。

画面を縦表示にする

 THINKLETの画面を表示するには、縦表示のほうが見やすくて便利です。画面表示を縦に変更しておきます。

 Bookwormでは、ディスプレイサーバーとしてWaylandが採用されています。Waylandで使用できるwlr-randrコマンドを使ってディスプレイの設定情報を確認し、ディスプレイの回転を設定します。wlr-randrは、Waylandのディスプレイサーバープロトコルに基づく、ディスプレイ設定ツールです。

 wlr-randrコマンドは、対象のディスプレイを環境変数WAYLAND_DISPLAYで設定してから起動します。ディスプレイは1つしかつながっていない状態なので、LCDの画面は"wayland-1"です。実行すると、ディスプレイの詳細情報が表示されます。

fduser@pi4:~ $ WAYLAND_DISPLAY="wayland-1" wlr-randr
DSI-1 "(null) (null) (DSI-1)"
  Physical size: 154x86 mm
  Enabled: yes
  Modes:
    800x480 px, 60.049000 Hz (preferred, current)
  Position: 0,0
  Transform: normal
  Scale: 1.000000

 パラメータにTransformがあるので、これを使って設定ができそうです。値は、normal、90, 180, 270が設定できます。実際に設定してみます。

fduser@pi4:~ $ WAYLAND_DISPLAY="wayland-1" wlr-randr --output DSI-1 --transform 270


画面の縦表示

 transformに270を設定することで、ディスプレイ表示が回転すること確認できました。次に、この設定を永続化します。

 Waylandディスプレイサーバーは、クライアントが描画した内容の合成と表示、入力デバイスからのイベントの管理、ウインドウの管理などを行うソフトウエアです。Waylandだけでは、デスクトップの機能は実現できません。Waylandのライブラリを利用するWaylandコンポジターと呼ばれるソフトウエアが、GUIの実現を担当しています。

 Raspberry Pi OSでは、WaylandコンポジターとしてWayfireが採用されています。Wayfireはプラグインを追加することで機能を増やすことができる設計になっています。

 画面の回転の設定は、いろいろなレイヤーで設定ができるのですが、ここでは、Wayfireのユーザー設定で指定します。

 ユーザーディレクトリに、.config/wayfire.iniというWayfireの設定ファイルが作られているので、これに先ほどの回転設定を追記します。wlr-randrコマンドでディスプレイの指定は「DSI-1」とわかっているので、DSI-1にtransformを設定しておきます。

fduser@pi4:~ $ vi .config/wayfire.ini

[output:DSI-1]
transform = 270

 これで、再起動しても回転が保持されるようになりました。wayfire.iniへの設定変更は、再起動しなくても即時に反映されます。

scrcpyのインストールと起動

 Raspberry Pi OSでは、1つ前のBullseyeまでは、scrcpyはパッケージで提供されていました。最新のBookwormでは、scrcpyはdebパッケージでの提供が行われていないので、ソースコードからビルドする必要があります。

ビルドに必要なライブラリを追加でインストールする

 scrcpyをソースコードからビルドするには、Raspberry Pi OSに標準でインストールされているパッケージでは必要なライブラリが不足しています。これらを先にインストールしておきます。指定ライブラリと、依存するパッケージがインストールされます。

fduser@pi4:~ $ sudo apt upgrade
fduser@pi4:~ $ sudo apt install libavformat-dev libsdl2-dev libavdevice-dev libusb-1.0-0-dev meson

adbコマンドをインストール

 scrcpyの実行には、adbコマンドが必要になるので、パッケージでインストールします。

fduser@pi4:~ $ sudo apt install adb

scrcpyソースコードをcloneする

 scrcpyのソースコードをcloneします。

fduser@pi4:~ $ git clone https://github.com/Genymobile/scrcpy

 cloneされた内容は以下のような内容でした。

fduser@pi4:~ $ ls scrcpy/
app              cross_win64.txt    gradlew.bat         release.mk
assets           doc                install_release.sh  release.sh
build.gradle     FAQ.md             LICENSE             run
bump_version     gradle             meson.build         server
config           gradle.properties  meson_options.txt   settings.gradle
cross_win32.txt  gradlew            README.md

ビルドスクリプトで自動ビルドとインストールを行う

 ビルド用のスクリプトが用意されているので実行します。ビルド時間は数分で終わります。

fduser@pi4:~ $ cd scrcpy/
fduser@pi4:~/scrcpy $ ./install_release.sh

 ビルドが終了すると、scrcpyがインストールされました。インストールパスとバージョンを確認しておきます。

fduser@pi4:~/scrcpy $ which scrcpy
/usr/local/bin/scrcpy
fduser@pi4:~/scrcpy $ scrcpy --version
scrcpy 2.4 <https://github.com/Genymobile/scrcpy>

Dependencies (compiled / linked):
 - SDL: 2.26.5 / 2.26.5
 - libavcodec: 59.37.100 / 59.37.100
 - libavformat: 59.27.100 / 59.27.100
 - libavutil: 57.28.100 / 57.28.100
 - libavdevice: 59.7.100 / 59.7.100
 - libusb: - / 1.0.26

scrcpyを動かしてみる

 THINKLETを起動し、Raspberry PiにUSB接続して、scrcpyを動かしてみます。

 リモートのsshからscrcpyを起動するので、Raspberry PiのLCDに表示するために環境変数DISPLAYを指定します。

fduser@pi4:~ $ DISPLAY=:0 scrcpy
scrcpy 2.4 <https://github.com/Genymobile/scrcpy>
INFO: ADB device found:
INFO:     -->   (usb)  P16M116D5252533                 device  THINKLET_LC01
/usr/local/share/scrcpy/scrcpy-server: 1 file pushed, 0 skipped. 150.6 MB/s (69007 bytes in 0.000s)
[server] INFO: Device: [QUALCOMM] FD THINKLET LC01 (Android 8.1.0)
[server] WARN: Audio disabled: it is not supported before Android 11
INFO: Renderer: opengl
INFO: OpenGL version: 3.1 Mesa 23.2.1-1~bpo12+rpt3
INFO: Trilinear filtering enabled
WARN: Demuxer 'audio': stream explicitly disabled by the device
INFO: Texture: 1080x1920


scrcpyの起動画面

 THINKLETを抜くと、scrcpyも終了します。

WARN: Device disconnected

 うまくいったら、フルスクリーン表示で起動してみます。タッチで操作できるのも確認してみましょう。

fduser@pi4:~ $ DISPLAY=:0 scrcpy --fullscreen


画面の縦表示(フルスクリーン)

scrcpyの自動起動

 THINKLETをRaspberry PiのUSB端子に接続すると、scrcpyが自動的に起動するように設定します。

 Linuxシステムでは、USBデバイスが接続されたのを検知すると、udev(ユーザースペースで動作する動的デバイス管理を行うためのデバイスマネージャ)にイベントが通知されます。udevは、デバイスがシステムに接続されたり取り外されたりする際に自動的に対応し、必要な設定を行う役割を担っています。簡単なルールファイルを書くことで、USBのベンダーID(VID)やプロダクトID(PID)などを使って、対応するプログラムを自動起動することができます。

 まず、THINKLETのUSBデバイスとしてのVID, PIDを調べます。

 THINKLETをRaspberry PiにUSB接続し、lsusbコマンドで接続されているUSB機器の情報を調べます。THINKLETは「Qualcomm, Inc. SDM450-QRD」と表示されています。VID/PIDは、「05c6:90b8」として認識されています。コロンの前がVID、コロンの後がPIDになります。

fduser@pi4:~ $ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 05c6:90b8 Qualcomm, Inc. SDM450-QRD _SN:87421062
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

 VID, PIDを検知すると指定のプログラムを起動するルールを作成します。USB機器が抜かれると、プログラムを自動的に終了するようにしましょう。

 udevルールの実行には制約があり、長時間実行されるプロセスやフォアグラウンドで実行されるGUIアプリケーションは、udevルールから直接呼び出すことができません。udevから直接実行されるプロセスがタイムアウトするためです。

 その対策として、udevではサービスの制御を行うことにして、scrcpyの起動と終了はsystemdでフックするサービスで行うことにします。まず、そのサービスscrcpy.serviceを作成します。

fduser@pi4:~ $ sudo vi /etc/systemd/system/scrcpy.service

[Unit]
Description=Start scrcpy for USB device
After=graphical.target

[Service]
Type=simple
User=fduser
Environment="DISPLAY=:0"
ExecStart=/usr/local/bin/scrcpy --fullscreen
Restart=on-failure
RestartSec=3

 設定が追加されたので、サービスをリロードして、scrcpy.serviceを有効にしておきます。

fduser@pi4:~ $ sudo systemctl daemon-reload
fduser@pi4:~ $ sudo systemctl enable scrcpy.service

 Raspberry PiにTHINKLETを接続して、作成したscrcpy.serviceを有効にしたあと、起動・終了して、scrcpyが動作するのを確認します。

fduser@pi4:~ $ sudo systemctl start scrcpy.service
fduser@pi4:~ $ sudo systemctl stop scrcpy.service

 次にUSBデバイスを検知したときにscrcpy.serviceを起動するudevルールを作成します。デバイスが抜かれた時の処理も追加しておきます。

fduser@pi4:~ $ sudo vi /etc/udev/rules.d/99-THINKLET.rules

ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05c6", ATTR{idProduct}=="90b8", RUN+="/bin/systemctl start scrcpy.service"
ACTION=="remove", SUBSYSTEM=="usb", ATTR{idVendor}=="05c6", ATTR{idProduct}=="90b8", RUN+="/bin/systemctl stop scrcpy.service"

 udevのルールをリロードして、リセットしておきます。

fduser@pi4:~ $ sudo udevadm control --reload-rules
fduser@pi4:~ $ sudo udevadm trigger

 THINKLETをRaspberry PiのUSB端子に接続して、scrcpyが自動起動するか確認してみてください。

まとめ

 長くなりましたが、THINKLETにUSB接続して画面を表示するデバイスの構成方法について紹介しました。

 Raspberry Piに小型のLCDモニタを接続し、USBをトリガーにscrcpyコマンドを起動することで、現場にPCを持参しなくてもTHINKLETの設定や操作が可能になります。

 業務で使用するためには、システムのROM化や起動の高速化など、まだまだ細かい調整や機能が不足していますが、開発目的で日常的に使うためには、この設定で問題ないと思います。

https://www.youtube.com/watch?v=h5CQOncBGO0

 この事例が、THINKLETをより効率的に活用するための一助になれば幸いです。皆さんもぜひ試してみてください。

 当社では、THINKLETのような独自のハードウエアを使った開発や、その支援ツール/サービスの開発に携わってくれるエンジニアを募集しています。興味がある方は、ぜひ以下のリンクからカジュアル面談や選考への応募をご検討ください。

https://open.talentio.com/r/1/c/fairydevices/homes/4010

フェアリーデバイセズ公式

Discussion