路線バス情報表示サイネージ 新潟交通版
バスの情報を確認できるデジタルサイネージを作ってみました。現在時刻、最寄りバス停の時刻表、現在のバス位置情報を1画面に表示するシステムです。今回、制作したものは新潟交通用ですが、Navitimeのシステムを使っているので他のバス会社でもNavitimeを使っていれば同様のモノは作れると思います。スマホでも同じ情報を見れますが、このシステムで1画面で必要な情報を見られ便利です。
ハードウェア
Raspberry Piと縦型液晶モニタを使います。Raspberry Piは現在だとRaspberry Pi 5を選ぶのが良いでしょう。私は手元にあったRaspberry Pi 3bを使いました。3bは今となっては遅くてレスポンス悪いですが、サイネージとして表示するだけであればギリ使える感じ。
液晶パネルはこちら、Amazonから購入しました。
タッチパネルは必要なかったのですが、タッチパネル無しは見つけられなかったのでこちらを選択。HDMIケーブルでRaspberry Piと接続。USB type-cで給電。RasPiにUSBで繋ぐとタッチパネルが使えます。
Raspberry Pi OS
OSはRaspberry Pi Imagerを使ってRaspberry Pi OS(64bit)を選択。wi-fiのSSID、パスワード、ユーザーネーム、ユーザーパスワードを設定してSDカードに書き込み。今回はユーザ名をraspiとして作成しました。
書き込んだSDカードをRaspberry Piに差し込んで起動すればOSの画面が起動します。昔に比べるとすごい簡単。
2024年の12月現在ですが、waylandやlabwcというあたりがかなり変更が加わっていて古い環境からアップデートすると色々と面倒らしいです。Raspberry Pi Imagerを使って最新をインストールすると面倒が無いようです。
SDカードに書き込んでRaspberry Piに差し込んで電源を入れるとRaspberry PI OSが起動します。
初期設定
sshの接続設定と画面の向きの設定だけしておきます。Chromiumをkioskモードで起動することになります。その時にsshでの接続ができないと何も操作ができないことになってしまうのでsshを有効にします。画面の向きは端子を下にしたかったので反対むきに設定しました。最初の設定の時にだけキーボードとマウスを繋いでおいた方が便利です。
ここら辺の設定はRaspberry PiのメニューのPrefarenceからGUIで操作できます。
画面の向き
Raspberry PiメニューのPreferencesからScreen Configurationを選びます。
端子が下になるようにInvertedを選択。
SSHを有効にするためにPreferencesからRaspbetty Pi Configurationを選択。
InterfacesのSSHをonにします。画面幅の都合で見にくいですが、右端のボタンでon/offできます。
固定IPを設定する
sshで接続するために固定のIPアドレスを振っておいた方が簡単です。ルーター側の設定で固定しても良いですし、RasPi側で固定のIPアドレスを設定してもOKです。私はルーターのDHCPの設定でMACアドレスを登録して固定IPにしました。
sshでの接続
様々な設定はPCからターミナルを使いsshで接続して行います。Raspberry PiのIPアドレスが192.168.68.197の場合、次のコマンドで接続できます。
ssh -l raspi 192.168.68.197
接続できるとパスワードを聞かれますので設定で入れたパスワードを入力します。(画面には何も表示されません)
接続完了するとこんな画面になります。
新潟交通のwebページをHack
新潟交通のwebページの仕組みについて調べてみます。新潟交通はNavitimeのシステムを使っています。トップページこちらです。
URLを見るとhttps://transfer.navitime.biz/niigatabrt-newsystem/pc/map/Topとなっています。ここでpcというところはPC向けのwebサイトでスマートフォン向けはそこがsmartになるようです。ただしパソコンからURLをsmartに書き換えてアクセスしてもpc版を開いてしまいます。サーバー側でユーザーエージェントを見て判断しているようです。
時刻表のページ
まずは時刻表のページを見てみましょう。上記のページで「時刻表・バス停」のタブに切り替えて最寄りのバス停名を入力して検索します。バス停を選択し、乗り場(番線、上り・下り)を選択すると時刻表が表示されます。この時のURLは次のようになっています。
https://transfer.navitime.biz/niigatabrt-newsystem/pc/diagram/BusDiagram?orvCode=00060179&course=0001400955&stopNo=13
orvCodeがバス停、courseが番線になっているようです。stopNoはなんだかわかりませんが、他のバス停でも13になっているので、そのままにしておけば良いようです。このアドレスで任意のバス停の時刻表を表示できるようになります。PCで開くと全時刻のバス時刻が表示、スマホで開くと現在時刻から3時間後までの時刻が表示されるようになります。
バスの現在位置のページ
「バス接近情報」のタブに切り替えて出発地と目的地を入力します。PCでは地図の入ったページが表示されます。スマホでは地図が入らないページが表示されます。アドレスは次のようになります。
https://transfer.navitime.biz/niigatabrt-newsystem/pc/location/BusLocationResult?startId=00060179&goalId=00060081
startIdが乗りたいバス停、goalIdが目的地になっています。このページは1分間隔で自動的にリロードされるようになっています。
kioskモード
ブラウザ(Chromium)をkioskモードで起動するためのシェルスクリプトを作成します。kioskモードは全画面で表示してエラーダイアログやアドレスバーなどを非表示にできるモードです。
自分のディレクトリ(/home/raspi/)にkiosk.shを作成します。viエディタで作成するにはターミナルを起動して
raspi@raspberrypi:~ $ vi kiosk.sh
と打ち込みます。
「raspi@raspberrypi:~ $ 」の部分は最初から表示されてる部分なので、キーボードを叩くのはその後の部分からになります。
私はエディタにviを使ってますがこれから始める人はnanoとかの方が使いやすいと思います。
空の新規のファイルが作成されるので以下のような内容にします。
sleep 4
/bin/chromium-browser \
--kiosk \
--user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit
/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"\
--ozone-platform=wayland \
--start-maximized \
--noerrdialogs \
--disable-infobars \
--enable-features=OverlayScrollbar \
https://transfer.navitime.biz/niigatabrt-newsystem/pc/location/BusLocationResult?startId=00060179&goalId=00060081
ちなみにバックスラッシュ\はoptionキーを押しながら|のキーを押すと入力できます。
次にこのスクリプトに実行権限を設定します。以下のコードを実行します。
raspi@raspberrypi:~ $ chmod +x kisosk.sh
kiosk.shを実行すると全画面で指定したwebページを表示できます。ただkiosk.shを実行するにはssh接続した端末からではなく表示したい画面でターミナルを開いて実行する必要があります。キーボードを接続するかタッチスクリーンの仮想キーボードを使う必要があります。
raspi@raspberrypi:~ $ ./kiosk.sh
またはGUIから操作すればマウスだけでできます。エクスプローラ(メニューバーのフォルダのアイコン)を起動してkiosk.shをダブルクリックします。
実行して良いかの確認のダイアログが出るのでExecuteをクリックします。
1画面にまとめる
現在時刻、時刻表、バスの現在位置を1画面に表示したいのでそのためのページを作る事にします。このページはローカルに作っておき、その中のiframeで上記の必要なページを読みこむ事にします。ホームディレクトリにautoreload.htmlというファイルを作成し、次のような内容にします。
<!DOCTYPE html>
<html lang="en">
<style type="text/css">
body{
margin:0px;
padding: 0px;
}
#clock{
color: white;
background-color: black;
font-size:xx-large;
text-align: center;
padding:15px;
font-family: Arial, Helvetica, sans-serif;
}
#timetableDIV{
width: 480px;
height: 720px;
margin: 0px;
padding: 0px;
overflow: hidden;
}
#timetable {
width: 480px;
height: 1200px;
margin: 0px;
padding: 0px;
border: 0px;
margin-top: -170px;
}
#sep{
background-color: black;
width: 100%;
height: 20px;;
}
#locationDIV{
width: 480px;
height: 1200px;
margin: 0px;
padding: 0px;
overflow: hidden;
}
#location {
width: 480px;
height: 1320px;
margin: 0px;
padding: 0px;
border: 0px;
margin-top: -120px;
}
</style>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="600">
<title>Document</title>
</head>
<body>
<div id="clock"> </div>
<div id="timetableDIV">
<iframe id="timetable"
src="https://transfer.navitime.biz/niigatabrt-newsystem/pc/diagram/BusDiagram?orvCode=00060353&course=0001401011&stopNo=13">
</iframe></div>
<div id="sep"> ... </div>
<div id="locationDIV">
<iframe id="location"
src="https://transfer.navitime.biz/niigatabrt-newsystem/pc/location/BusLocationResult?startId=00060353&goalId=00060473">
</iframe></div>
</body>
<script>
function displayTime() {
//こちらを参考:https://www.pyxofy.com/how-to-make-a-digital-clock-using-javascript/
// ゼロパディングして2桁にする関数
function padZero(value) {
return value.toString().padStart(2, '0');
}
const now = new Date();
// 関数padZeroを呼び出す
const hour = padZero(now.getHours());
const minute = padZero(now.getMinutes());
const second = padZero(now.getSeconds());
const currentTime = `${hour}:${minute}:${second}`;
document.getElementById('clock').textContent = currentTime;
}
displayTime();
setInterval(displayTime, 1000);
</script>
</html>
軽く内容を説明します。
現在時刻はJavaScriptで表示しています。
時刻表もバス位置も一番上のヘッダーやメニュー部分は表示する必要がないので表示位置を調整しています。iframeをdivで囲みoverflow: hiddenではみ出た部分を非表示にしています。その上でmargin-topで表示開始位置を下に下げています。(上にはみ出てて、はみ出た部分は非表示になってる)
バス位置のページは1分ごとに自動で更新されますが、時刻表の部分は更新されません。そこでページ全体を一定時間ごとに再読み込みをするようにします。
<meta http-equiv="refresh" content="600">
がそのための設定です。600秒(10分)間隔でページを読み込みなおします。その時に時刻表の部分が現在時刻に合わせた表示になるという仕組みです。
ローカルにあるhtmlファイルを開くシェルスクリプト
次にシェルスクリプトを次のように修正します。
sleep 4
/bin/chromium-browser \
--kiosk \
--user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit
/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25"\
--ozone-platform=wayland \
--start-maximized \
--noerrdialogs \
--disable-infobars \
--enable-features=OverlayScrollbar \
/home/raspi/autoreload.html \
最後のアドレスを変更しています。先ほど作成したautoreload.htmlを開くようにしています。
自動起動
電源が入った時に自動的に起動するようにします。ここら辺、最近(2024年12月)色々と変わっているらしくlabwcという物を使うのが最新の方法らしいです。
~/.config/labwcに移動します。最初は何もファイルがありませんがautostartというファイルを作成して中身を次のようにします。
bash /home/raspi/kiosk.sh &
これで再起動すればchromiumがキオスクモードで起動してくれます。
chromiumの強制終了
kioskモードで起動しているとGUIでの操作で終了はできなくなります。キーボードからkioskモードの終了はできるらしいのですが、ターミナルからもchromiumを終了することもできます。次のコマンドで動いている処理を一覧表示します。
raspi@raspberrypi:~ $ ps x
動いている処理が一覧表示されます。
982 ? Ssl 0:00 /usr/libexec/xdg-desktop-portal
988 ? Ssl 0:00 /usr/libexec/xdg-document-portal
997 ? Ssl 0:00 /usr/libexec/xdg-desktop-portal-gtk
1002 ? Ssl 0:00 /usr/libexec/gvfs-udisks2-volume-monitor
1011 ? Ssl 0:06 /usr/libexec/gvfs-afc-volume-monitor
1012 ? Sl 26:59 /usr/lib/chromium/chromium --force-renderer-accessi
1022 ? Ssl 0:00 /usr/libexec/gvfs-goa-volume-monitor
1036 ? Ssl 0:00 /usr/libexec/gvfs-mtp-volume-monitor
1037 ? Ss 0:00 /usr/libexec/xdg-desktop-portal-wlr
1041 ? Ssl 0:00 /usr/libexec/gvfs-gphoto2-volume-monitor
1046 ? Sl 0:00 /usr/libexec/gvfsd-trash --spawner :1.17 /org/gtk/g
1054 ? Sl 0:00 /usr/lib/chromium/chrome_crashpad_handler --monitor
1057 ? S 0:00 /usr/lib/chromium/chromium --type=zygote --no-zygot
1058 ? S 0:00 /usr/lib/chromium/chromium --type=zygote --string-a
1060 ? S 0:00 /usr/lib/chromium/chrome_crashpad_handler --no-peri
1063 ? Sl 0:00 /usr/bin/gnome-keyring-daemon --start --foreground
1064 ? Ssl 0:00 /usr/bin/gnome-keyring-daemon --foreground --compon
1073 ? S 0:07 /usr/lib/chromium/chromium --type=zygote --string-a
1080 ? Sl 0:00 /usr/lib/menu-cache/menu-cached /run/user/1000/menu
1197 ? Sl 24:48 /usr/lib/chromium/chromium --type=gpu-process --ena
1221 ? Sl 7:02 /usr/lib/chromium/chromium --type=utility --utility
1277 ? Sl 0:34 /usr/lib/chromium/chromium --type=utility --utility
1335 ? S 0:00 /usr/lib/chromium/chromium --type=broker
1343 ? Sl 26:14 /usr/lib/chromium/chromium --type=renderer --string
1972 ? Sl 63:41 /usr/lib/chromium/chromium --type=renderer --string
16032 ? S 0:00 sshd: raspi@pts/0
16033 pts/0 Ss 0:00 -bash
16076 pts/0 R+ 0:00 ps x
このリストの中からchrmiumの番号を見つけ、その番号をkillします。複数のchromiumの処理が走ってますが、それっぽいのをkillすればchromiumが強制終了できます。上の例では1012をkillします。
raspi@raspberrypi:~ $ kill 1012
これでchromiumが終了してデスクトップが表示されます。
電源
電源はなんでも良いですが、私はUSB2ポートのUSB電源を使っています。家に転がってたもので詳細不明ですが、2ポートの1つが2.5A、もう1つが1A出力。こんな感じのものです。
一方がRasPiへ、もう1つのポートがディスプレイに繋ぎます。RasPiのUSBポートからディスプレイへ給電しても良いのですが、タッチパネルを無効にしたいのでこうしています。
ケースを作る
3DプリンタでRasPiとケーブル、電源アダプタが入るいい感じのケースを作ってあげます。
RasPiのCADモデルはこんな感じで公開されているものもあるので使わさせてもらいます。
rasPi 5 + Active Cooler
rasPi 4
rasPi 3
3Dプリンタでケースを作成してRasPIとケーブルを良い感じに収まるうようにしました。
起動と終了
24時間以上動かしていたら固まりました。やはり定期的にshutdownして再起動した方が良いようです。夜に自動で終了して、朝に起動するようにします。
終了
終了はcronを使います。cronは指定した時刻に決まった処理を走らせる仕組みです。次のコマンドでsudo権限で実行できるcronを作成します。
raspi@raspberrypi:~ $ sudo crontab -e
最初にこのコマンドを実行すると編集に使うエディタを選択できます。
no crontab for root - using an empty one
Select an editor. To change later, run 'select-editor'.
1. /bin/nano <---- easiest
2. /usr/bin/vim.tiny
3. /bin/ed
こんな表示が出るのでエディタを選びます。私はviを使いたいので2を選択しました。最初は何も設定していないのでコメント文だけのファイルが開きます。ファイルの最後に次の1行を追加します。
00 22 * * * /usr/sbin/shutdown -h
これで毎日22時にshutdownするようになります。
起動
起動はRasPiだけではできません。RasPiは内部にバッテリーを持っていないので現在時刻の保持ができません。起動後にネットワーク上の時計を見に行って時刻合わせをしています。というわけで起動にはアナログな方法を使います。
https://www.monotaro.com/g/06053445/
こんなのとか
https://www.monotaro.com/g/01282441/
こんなのを使います。RasPiが22:00にshutdownするので、ちょっと余裕を持たせて22:30に電源OFF、翌日の朝6:00とかに電源が入るようにします。そうするとRasPiも起動してくれます。
その他
chromiumが「翻訳しますか?」みたいなことを聞いてくるダイアログが出てくる時があります。このメッセージはエラーでは無いのでnodialogにしてても出てきてしまうようです。kioskモードではなく普通にデスクトップからchromiumを開いて該当のwebページを開き、このページでは翻訳しない、みたいな選択をしてやれば良いです。
参考サイト
JavaScriptの時計
キオスクモード
情報ありがとうございます!