本記事で制作したコードはGitHubにて公開しています https://github.com/FlyingObject1024/information_board
あらすじ
前回の記事では、Raspberry Pi zeroを使ってドットマトリクスSMD2121に日本語を表示させました。今回は、これを使って発車案内などの情報を表示してみます。
ドットマトリクスLEDの増設
ここで取り扱っているドットマトリクスLED(64x32px)1枚ではあまりに表示できる情報が少ないです。実際に内容を表示すると、このように詰め詰めとなってしまいます。

これでは5文字はおろか、4文字の駅名表示にも難儀してしまいます。「京王八王子」「東葉勝田台」「代々木上原」といった駅名は表示できないわけです。かと言ってフォントの解像度を落とし、一文字8ピクセル単位にすれば可読性が大幅に低下してしまいます。
というわけで、同じ商品をもう一つ購入して、OUTPUT端子ともう一つのINPUT端子をケーブルで接続します。勿論電源ケーブルも両方のドットマトリクスLEDに接続しておきます。

写真は1枚のドットマトリクスLEDです。接続が上手くいっていれば、後はライブラリ側から制御可能です。
電光掲示板に表示する情報を集める
掲示板のキモとなる列車情報は基本的にリアルタイムに取得する必要があります。今回は、PythonからWebの情報をスクレイピングするコードを作成します。
※この手法はかなりグレーです。個人的なものなのでスクレイピングで済ませていますが、商用・公的なものでは使わないようにしてください。サーバ負荷を減らすため、通信頻度が多くならないようにしているつもりですが、それでも相当な回数呼び出しているという点にご留意ください。
発車時刻検索
Yahoo乗り換え案内は、URLパラメータを調整してやることで乗り換え情報を取得することができます$^{(1)}$。fromに出発駅、toに到着駅...というように直接文字列を指示することでhtml情報を取得することができます。例えば、以下のURIは東京から新宿までの乗り換え案内を表示するページにつながります。
https://transit.yahoo.co.jp/search/result?from=東京&to=新宿
他にも、type(重視するもの)、y m d hh m1 m2(日時を示す6つのパラメータ)などを詳細に指定することで、列車案内のデータがHTML形式で返ってきます。
運行情報の取得
同じくYahooでは「関東の列車運行情報」が公開されているWebページがあるため、こちらのデータも取得することが可能です$^{(2)}$。 ここでは、列車の運転見合わせや遅延に関する情報を取得できます。
https://transit.yahoo.co.jp/diainfo/area/{エリアコード}
エリアコードの部分を変更すれば、各地方別の情報を取得できます。関東のエリアコードは"4"です。
天気予報
天気関連の情報は、気象庁が提供しているAPIを利用することで、json形式で取得することができます$^{(3)}$。 jsonは以下のURLで取得することができます。
https://www.jma.go.jp/bosai/forecast/data/forecast/{エリアコード}.json
「エリアコード」はあなたの街の防災情報(https://www.jma.go.jp/bosai/)で選択した先に出てくるURLパラメータと同様です。例えば、東京都の情報は以下のURIで得られるページで確認できます $^{(4)}$。
https://www.jma.go.jp/bosai/#pattern=default&area_type=offices&area_code=130000
area_code=130000となっていますが、この値がエリアコードになります。東京の予報情報を得たいのであれば
https://www.jma.go.jp/bosai/forecast/data/forecast/130000.json
と指定してやればよいということです。
また、天気以外にも更新時間情報がjsonの"reportDatetime"要素に入っています。
メインのコード
上記の内容を元にGeminiにコードの大枠を作らせました。 完成したコード群は、GitHubへ公開しています。なお、Raspberry Pi Zeroで動いたものを直接アップロードしているわけではないので、そのままでは動かないことが多いと思われます。
基本ファイル構造
/infomation_board
┣/rpi-rgb-led-matrix // ライブラリファイル群(リポジトリ段階では未配置)
┃
┣/fonts
┃ ┗ Bestten-DOT.bdf // .bdf形式の10x10フォントファイル
┃
┣ /infomation_json_files
┃ ┣ departure.json // 発車情報
┃ ┣ first_last_train.json // 始発&終電情報
┃ ┣ operation.json // 運行情報
┃ ┣ jma_forecast_raw.json // 天気情報生データ
┃ ┗ weather_forecast.json // 天気情報
┃
┣ get_train_info.py // 列車情報取得
┣ get_weather_info.py // 天気情報取得
┣ draw_matrix.cc // 表示系のプログラム
┣ json.hpp // json解析ライブラリ
┣ MakeFile // c++のコンパイル用
┗ infomation_board.py // メインのプログラム(エントリーポイント)
想定される使用方法
"information_board"をRaspberry Pi Zeroのホームディレクトリ直下に配置したら、cdで"information_board"直下へ移動します。その状態で、"rpi-rgb-matrix"ライブラリをクローンしてください。
cd ~/information_board/ # 想定箇所
git clone https://github.com/hzeller/rpi-rgb-led-matrix/
"information_board"フォルダ内に"rpi-rgb-led-matrix"ライブラリが配置されたら、draw_matrix.ccをコンパイルします。Makefileを作成してあるので、それを呼び出すmakeコマンドを呼び出せば正しくコンパイルが通るはずです。ビルドが正しく終了したら、"information_board.py"を実行できるようになるはずです。
make
sudo pyhton3 ./information_board.py
ssh接続などターミナルが切断されても点灯したままにしたい場合は以下のように"nohup"と"$"を付けて実行するのが手っ取り早いです。
nohup sudo sudo pyhton3 ./information_board.py $
プロセスを停止させたいときはps auxコマンドから該当のプロセスIDを探し、killコマンドを打ち込む必要があります。
コードの概要
厳密な流れではありませんが、コードがおおよそやっていることを示すと以下のようになります。
function information_board.py :
(セットアップ部分)
get_train_info.py を呼び出し :
/infomation_json_files/departure.json を生成(上下線情報を取得)
/infomation_json_files/first_last_train.json を生成(始発終電情報を取得)
/infomation_json_files/operation.json を生成(運行情報を取得)
get_weather_info.py を呼び出し :
/infomation_json_files/jma_forecast_raw.json を生成(生データをそのまま保存)
/infomation_json_files/weather_forecast.json を生成(抽出した天気情報を保存)
draw_matrixを起動(サブプロセス) :
① /infomation_json_files内の情報を取得
② 時刻情報を取得
③ ①と②を整理して情報を表示
④ ①へ戻る
(メインループ開始点)
もし "発車時刻の一定期間前(15分前)である" 場合 :
search_thread_taskを立ち上げる(スレッド) :
get_train_info.py を呼び出し
get_weather_info.py を呼び出し
もし キーボードによる停止操作があった 場合 :
draw_matrixを停止
スレッドを停止し、ループを抜ける
(メインループ終了点)
コラム1: AIにコードを段階的に組ませる
本記事では、完成したコードを紹介するという体を取っていますが、これらのコードはGoogle Geminiとの応答を繰り返した末に生成されたものとなっています。AIへの指示を行う際は、大枠の処理を分けて記述しておき、各々の関数、ファイル、クラスごとに指示を与えたほうが上手くいきます。
筆者はまだ企業や団体で大きな成果物に携わったことが無いので推測にはなりますが、こういったコンポーネント管理(≒カプセル化)というのは実際の現場でも非常に有効だと考えられます。(有効どころか当たり前にやらなければならないレベルだと思われますが)
特に、今回は「Pythonで生成したjsonファイルをc++から読み込む」というコードを書いています。こういった場合は細かいコンポーネント分けを仕様として先んじて考えておき、文書化・言語化・図示によって可視化しておきます。AIにはこれらの大枠の内容を見せ、○○のXX部分を作れ。という指示をすると比較的楽にAI駆動させることができると考えています。
現在のLLMでは大量のトークンを処理することができません。サービスを提供する側の心理としても、大量の生成を良しとするとは考えにくいです。「0から100万まで数字を数えて」という指示に1回で従うLLMは今のところ無いといって良いでしょう。これを成し遂げたいのであれば、100刻み、1000刻みといった小さな単位で数えさせる必要があります。
結局のところ、「詳細まで想定して区分けし、可視化する」という行為が重要な要因となります。AIも人を真似ているので、曖昧な指示では碌な成果が出せません。
c++の利用
このコードにはc++とPythonが混在しています。そのワケは単純に"Pythonが遅すぎて電光掲示板として使いものにならなかった"からです。記事の前編の段階では全てのコードをPythonで記述しています。 詳細を書くことはしませんが、Pythonで作業を続けていった結果出来上がった電光掲示板はあまりにも明滅が激しいものとなってしまいました。Pythonの処理が遅すぎてチカチカするのです。その上、スクレイピングの処理中はリソースがそっちに食われてさらに表示が乱れます。
この件に関しては"rpi-rgb-led-matrix"ライブラリの作者も以下のように言及しています。
On a Pi-1/Pi Zero, the difference is even worse: 1/24 of the speed to the corresponding C++ program. Given that this Pi is already about 1/10 the speed of a Pi-3, this almost makes Python unusable on a Pi-1 (~0.015 Megapixels/s Python vs. ~0.36 Megapixels/s C++) (https://github.com/hzeller/rpi-rgb-led-matrix/tree/master/bindings/python より引用)
筆者的に端折って翻訳するとこうです。
Raspberry Pi ZeroでPythonを実行すると、最悪c++の1/24のスピードになる。ただでさえスピードが1/10になっているのに、この有様では全く使い物にならない
私たちが普段見ている画面は一般的に30fps以上の描画速度を持っていると言ってよいでしょう。筆者が肉眼で確認できる程に画面がチラついているということは、1フレームの描画に0.1秒(=100ms)以上かかっているということが推定されます。つまり10fpsを下回っているということです。古びた蛍光灯が如くチラつく電光掲示板など、誰が見たがるか。という文句が出てきます。
こういった問題はソフトウェアだけで予想することが難しく、実際に動作させるまでわかりません。これらの工程があるからこそ、まだまだ人間の手が必要だと感じさせられます。
動作テスト
ソフトウェアが出来上がったので実際に動作させます。小田急線の快速急行を表示してほしいという友人の要望があったため、fromに「登戸」、toに「新宿」と「町田」を指示してテストしてみます。
この状態で何日か連続動作をさせて、列車情報、運行情報、天気、時刻など表示に問題がなさそうだということを確認できました。
Raspberry Piとドットマトリクスの接続を基板化
ソフトウェアが終わったのでハードウェアの仕上げに入りましょう。
ここまで、ドットマトリクスLEDとRaspberry Pi Zeroはジャンパワイヤで接続していました。実験するにはこれでよいですが、ジャンパワイヤはすっぽ抜けやすく、折れやすく、再配線も一本一本確認しなければならず非常に不便です。加えて、電源端子とコネクタをワニ口クリップで接続しています。これも不格好です。
なので、これらの接続を一発で行える基板を作ります。回路を書き出し、ユニバーサル基板で手はんだします。

電源端子はターミナルブロック、Raspberry Pi ZeroとドットマトリクスLEDの接続にはピンヘッダを用います。
完成した接続部はこのようになりました。

ねじでドットマトリクス2つをくっつける
筆者が購入したドットマトリクスLEDには付属していませんでしたが、同系統のドットマトリクスLEDには「マグネットねじ」が付属している場合があります。
このねじをドットマトリクスLEDの裏側にある穴にはめる事で、鉄製の壁に貼り付けられる仕様になっています。
2枚のドットマトリクスLEDをバラバラに置こうとするのは不格好です。なので2枚のドットマトリクスLEDをねじとステー(ねじ穴が等間隔に空いた板)で固定します。
ほぼ同じであろうドットマトリクスLEDの販売サイトの情報を見ると、マグネットねじはM3ねじであることが分かりました。
計測したところ、2枚のドットマトリクスLEDの穴の距離は16mmなので、穴の間隔が16mmとなるステーを購入すれば良いことが分かります。
近所のホームセンターへ向かい、8mmのm3ねじのセットと"16"の刻印があるステーを購入しました。

実際取り付けて見ると、ぴったりとはまってくれました。 余ったねじは、基板を固定するために使いました。
コラム2: ねじの寸法がわからない
上記のマグネットねじは「m3ねじ」であると断定的に書いていますが、これを特定するのには苦労しました。 筆者の知識と調べた情報ではマグネットねじがm3なのかm4なのか断定することができず、ねじを持って行って店員に聞こうと考えていました。向かったホームセンターには、客がねじ穴を自分で推定できる台が置いてありました。
台にあるねじ穴に、持っていったねじを嵌めてみることで寸法をm3と断定する事ができた上、ネット上で調べてもヒットしなかった16mm間隔のステーも置いてあり、2重の意味で助けられました。
物理媒体は現物合わせする。という話は時々聞き及んでいましたが、こんなところで経験するとは思いませんでした。マイナーな部品であればこのような事も今後増えるでしょう。自身でこういった問題に対処できるようにしていきたいものです。
完成
組みあがった列車案内板を研究室へ持っていき、ネットワークの接続、電源供給、設置箇所をチェックします。少々の改良を加え、プログラムを起動します。

1日経っても問題が無い事を確認しました。本作はこれで完成です。
学生生活も終盤にさしかかり、研究室へ顔を出す機会も一気に減りつつあります。本作品は研究室への置き土産として作成したものとなります。案外いいものができて、筆者自身満足しております。
それではまた別の記事でお会いしましょう。
付録
作業中、bdfフォントの確認用でシミュレータっぽいものをGeminiに作らせました。以下のリンクにアップロードしておきます。
https://www.flyingobject-lab.tokyo/src/matrix_viewer/
HTML/CSS + JSで動作する(サーバ側の処理が無い)ものなので、どなたでも容易に使用できます。 ※ただし、bdf形式のフォントファイルが手元に無いと動きません
問題点・注意点
解決していない問題や懸念点を列挙します。
①権限絡みのトラブル
実行権限の問題により、うまく実行が通らない場合があります。特に、フォントの読み込みは何度か問題に遭遇したものの根本的な問題解決に至っておりません。rpi-rgb-led-matrixライブラリでは、daemonによる権限の弱い呼び出しが挟まっている箇所があるため、ファイルの実行権限や読み込みが通っていないとうまく働かない事例を確認しています。
②フォントの問題
筆者が変換したbdf形式の「ベストテンフォント」には、1ピクセルはみ出した領域がなぜか残っています。この問題はドットマトリクスの処理で「上から輝度0の直線で塗りつぶす」ことで場当たり的に対処しています。(draw_matrix.cc 434・435行目)

③SDカード容量の懸念
nohupコマンドを利用してきプログラムを実行した場合、標準出力がnohup.outというテキストに保存されるようになります。筆者が利用したRaspberry Pi Zeroに搭載しているSDカードの容量は8GBです。同じような容量のSDカードを用いて、長時間稼働するような場合はテキストデータが容量を食い尽くす可能性があります。出力を無くしたり、定期的に削除するなどして対処してください。
本記事で制作したコードはGitHubにて公開しています https://github.com/FlyingObject1024/information_board
参考文献
- (1) yahoo路線乗り換え案内の情報を抽出したい @hirohiroto522(Hiroto Ishikawa) | Qiita: https://qiita.com/hirohiroto522/items/2fc33cbc36ea8600f867
- (2) yahoo路線の運行情報を取得したい @hirohiroto522(Hiroto Ishikawa) | Qiita: https://qiita.com/hirohiroto522/items/6ff29be1344be805ecb0
- (3)【Python】気象庁のAPIから天気予報を取得する方法 | Python超入門部: https://python.joho.info/scraping/get-weather-forecast-jma/#toc2
- (4) 気象庁APIから天気予報を取得する際のエリアコード一覧 @ようかん | Zenn: https://zenn.dev/inoue2002/articles/2e07da8d0ca9ca
