t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

PS4コントローラーのアナログレバー(L2、R2)によるスクロールの改良案

今回は以前作成したPS4コントローラーをマクロキーボードとして使う件の改良案を紹介。
thom.hateblo.jp

従来のコードでスクロールを実現する仕組み

ブラウザや電子メールの閲覧ウインドウをスクロールする際は通常マウスホイールを使うが、上記のマクロキーボードではカーソルキーの「↓」と「↑」で代用している。
スクロールのスピードは、キー入力とキー入力の間に「↓、delay、↓、delay、↓、delay」のように遅延を挟み、この遅延量によってコントロールしている。

以前のコードは次のとおりである。

    if (PS4.getAnalogButton(R2)) {
      Keyboard.press(KEY_DOWN_ARROW);
      delay(256-PS4.getAnalogButton(R2));
      Keyboard.releaseAll();
    }

L2、R2キーはレバー式のボタンになっていて、押し込み量によって0~255までの値を取得できるので、256からボタンの押し込み値をマイナスした数値をDelayに使うことで押し込み量によってDelay値が1~255までの値になる。

問題点

問題その1

1つ目の問題はリモート環境でこれを使うと、最大まで押し込んでしまうとボタンを離してもしばらくスクロールが続いてしまうこと。
実際には記事を書いてリリースした直後には既に気づいていたんだけどまぁ使えるからいいやって感じで放置してた。。

原因は、Delayが少なすぎてアプリ側の処理が追い付かず、キー入力が遅れて到達する為だ。
パソコンのOSはマルチタスクで動作しているのでアプリの遅延などは日常茶飯事であるが、だからと言って入力したはずのキーがスキップされてしまうようなことがあっては使い物にならない。よって、キーボード入力はシステムメッセージキューに蓄えられ、それからアプリに送信される仕組みになっているようだ。
蓄えられるといっても通常人間が入力するようなスピードだと溜まることなくアプリケーションに到達するが、今回のようにプログラムから間髪入れずにキーが届く場合はアプリ側の処理が追い付かず、ボタンを離しても既にキューに溜まっている分がすべて出ていくまではアプリケーションにキーが送られ続けるということが起きる。

(参考)
wisdom.sakura.ne.jp

以前のコードではDelayの最小値が1となり、少なすぎた。色々検証した結果、私のケースではDelay が12以上あればアプリ側の処理を超えることが無いことが分かったので、最小Delayを12にすることにした。

問題その2

2つ目の問題はボタンの押し込み量で適切なスピードに調節するのが難しいこと。
そもそも1~255までの値を取れるといっても、狙って1ずつUpさせるのは無理である。
せいぜいこれくらい↓の操作が限界。
f:id:t-hom:20210105201028p:plain

そして、画面スクロールに必要なスピードの種類を考えてみると、3種類で十分であることが分かった。

低速スクロール:表示位置に拘りたい場合の位置合わせ用
中速スクロール:普通に文章を読む際のページ送り用
高速スクロール:読み飛ばしながら目的箇所を探す用

解決策

最終的に次のようなコードでやりたいことができた。

    int r2 = PS4.getAnalogButton(R2);
    if (r2) {
      Keyboard.press(KEY_DOWN_ARROW);
      if (r2 < 100) {
        delay(80);
      } else if (r2 < 255) {
        delay(24);
      } else {
        delay(12);
      }
      Keyboard.releaseAll();
    }

今回没にしたがアイデア備忘録として

L2とR2それぞれ3状態が取れるとしたら、それぞれの強弱を組み合わせて5段階くらいのスピード調節もできることに気づいたので一応アイデア備忘録として書いておく。
f:id:t-hom:20210105204240p:plain
まぁ今回は3段階で十分なのでこのアイデアは使わないんだけど。

また、押し込み量のみでなく押し込み時間で加速させていくという案も考えたので備忘録っておく。
たとえば最初の1秒は低速、2秒目から1秒ごとに加速する。緻密な操作が必要なときは、1秒押し込む度に放せば良い。

今回の学び

さて、今回学んだのはアナログボタンだからといってアナログ値をそのまま使うことに拘らなくて良いということ。
実現したいことが何なのかという目的に立ち返って考えることが重要だと思った。

こういう試行錯誤を繰り返すことで、直感的に操作できるデバイスになっていくんだろうなと思う。

技術的概念の説明で使用する3つのアプローチ「本質・具象・比喩」とその落とし穴

今回は技術的概念の説明において、なぜ受け手が混乱してしまうのか気づいたことがあるので備忘録がてらつらつらと書き記しておこうと思う。ちなみに私自身もそんなに説明がうまいと思っていないので、誰かの説明をDisる意図は全くない。自戒も込めての記事である。

技術的概念にも色々あるが、見たり触ったりできない概念も多く、そもそも説明が難しいことが多い。そこでなんとか受け手に理解してもらおうと様々なアプローチがとられる。

私はこのアプローチを表題の3種類に大別できるのではないかと考えた。

説明に使用する3つのアプローチ

# 種類 岩波国語辞典による定義
1 本質
(ほんしつ)
そのものとして欠くことができない、最も大事な根本の性質。
2 具象
(ぐしょう)
物が実際にそなえている形。
3 比喩
(ひゆ)
物事の説明や描写に、ある共通点に着目した他の物事を借りて表現すること。


例えばプログラミングによる「変数」の説明をする場合を例に挙げて、それぞれのアプローチによる説明を例示してみる。

アプローチ 説明の例示
本質的な説明 変数とは、データに名前を付けて扱いやすくするための、プログラミング言語の機能である。
具象を用いた説明 変数はプログラミング言語の実装系によって記号表で管理され、その実態はメモリ上の電気信号である。
比喩を用いた説明 変数はデータを入れる箱のようなものである。

分かりにくい説明・混乱を招く説明のアンチパターン

比喩のみの説明

説明する側は、比喩による説明はきっと分かりやすいだろうという思い込みが強くなりがちである。
確かに比喩表現は、既知の概念との共通点をヒントに本質の理解を助けてくれる。
ただし、本質の説明をすっ飛ばしていきなり比喩に入ってしまう失敗がよくある。
本来は比喩表現から、本質的な概念を抜き出して欲しいのだが、本質をストレートに説明することなく比喩だけを用いてしまうと、受け手は本質的な概念ではなく具象を抜き出してしまう。

具象のみの説明

比喩表現の失敗によるアンチテーゼ的な考えで、私が一時期陥ったアンチパターンがこれ。
例えば変数を理解するには、具体的に言語系やメモリがどのように働いているのかを示すことが重要だと考えた。
確かに一部の人には有効だった。変数が箱やメモと言われてもふわっとしていて具体的に何なのか分からずに躓いてしまう方への処方箋として実装がどうなっているかを明確に示すことでスッキリ理解してもらうことが出来た。

しかしあるとき、具象は本質を実現するための手段にすぎず、その概念の本質そのものではないということに気づいた。

本質のみの説明

そもそも本質的な概念というのは目に見えなかったり、言語で表現する難しさがあったりする。
ズバッと本質を言ったつもりでも、予備知識のない受け手にとってはなんだかフワッとした説明に感じられる場合が多い。
それだけで理解するのはなかなか難しいように思う。私も一時期このアンチパターンをやってしまったことがある。

どのアプローチを取っているのか不明瞭な説明

比喩で説明しているのに、受け手は具象や本質だと勘違いしてしまう失敗も多々ある。
たとえば変数は箱であるという説明で、コンピューターの中に本当に箱ないし箱を模した何かがあると本気で信じていた人を見たことがある。いや冗談ではなく。大抵の一般人はPCの中身を開けたことが無いのだ。メモリに顕微鏡で見ないと見えないくらいの小さな箱が物理的にびっしり詰まっていることを想像していてもおかしくない。

この記事を書くきっかけになったのもこのアンチパターン。
先日、材料力学の勉強でYouTubeで応力の説明を聞いていて、「内力を調べるために梁を切断し、その断面が云々」という表現が出てきたのだが、私は実際に切って分析したりするんだろうかと勘違いしてしまった。別途書籍を買って内力の説明を読んだ際、「頭の中で仮想的に切った断面を考える。仮想断面。」という表現が使われていて、ようやく説明の意味が分かった。

こういった少しの言葉選びや前置きの説明でこのアンチパターンを避けることができる。

私が考える分かりやすい説明

私は、本質・具象・比喩をすべて組み合わせて説明し、受け手が混乱しないように今どんなアプローチで説明しているのか分かるように慎重に言葉を選ぶというのが良いと思う。

受け手には個人差があるので、必ずしもすべて組み合わせなくても理解してもらえたという経験はあると思う。でも、たまたま本質の説明は既に別のところで受けていて、比喩表現がパズルの最後のピースだっただけかもしれない。

また、受け手の前提知識やどこまで分かれば腑に落ちるかという納得閾値には個人差がある。だから、納得閾値の低い受け手は簡単な説明で分かった気になってさくさく次に進めるが、納得閾値の高い受け手は細かい具象をある程度イメージできるようになるまでは腑に落ちずなかなか先に進めない。残念ながら私はこのタイプで、損な性格だなと感じている。

よって、受け手のタイプや前提知識にかかわらず伝わる説明をしようと思うと、まず本質をズバッと言い切って、比喩で簡易的なイメージ持たせ、最終的には具象を細かく追っていき、具象で混乱した受け手を回収するために本質に戻って締めるという説明がベストなのではないかと思う。

以上

PS4のアナログスティックの押し込み角度を逆三角関数で求める。

以前、PS4コントローラーをマクロキーボードとして使うという記事を書いた。
thom.hateblo.jp

その中で、アナログスティックをズームイン・ズームアウトに使うコードを書いたが、コントローラーから取得できるアナログスティックのX・Y座標をそのまま判定に使っていたのでかなり無理やり感があった。本当はスティックの角度を判定に使いたかったんだけど、取れるのがX・Y座標だったから仕方なくそうしてたのだ。

ところが最近勉強し始めた材料力学で三角関数を学びなおす機会があり衝撃を受けた。

なんだこの便利なものは!!

サイン・コサイン・タンジェント。名前は聞いたことある。
中学だったか高校だったか忘れた。でも当時はそれを学ぶ意義を1ミリも理解していないので、大人になってもなんだかよく分からないものとして切り捨てていたのだ。

要するに、直角三角形のうち90°以外の角度が一つ分かれば2つの辺の比は求まるということ。
試しにFusion360でスケッチしてみた。確かに角度と底辺の寸法を入力すると、それ以上は過剰拘束だと言われる。つまり「もうサイズ確定したよ、これ以上寸法入れる意味ないよ」というメッセージだ。
f:id:t-hom:20210103132902p:plain

ここまでは直感的にまぁそうなんだろうなと分かる部分だけど、この先は円で考えることになる。
ここでは詳しく解説しないけど、基礎から学びなおすにはこちらのYouTube動画がとてもよかったので紹介しておく。


さて、実際のコードはこちら。(関係する部分だけ抜粋)

#include <math.h>
#define PI 3.141592653589793
//~中略~
void loop() {
    //~中略~
  degree = 
    atan2(
      255 - PS4.getAnalogHat(LeftHatY)-128,
      PS4.getAnalogHat(LeftHatX)-128)
    * 180.0 / PI
    + 180;
    //~中略~
}

まず、「PS4.getAnalogHat(LeftHatY)」でY座標が取得できるのだが、ジョイスティックの座標軸は次のようになっている。
f:id:t-hom:20210103134433p:plain

これだと扱いにくいので、255からYを引いてやることで、座標軸を変換している。
f:id:t-hom:20210103134557p:plain

さらにX、Yともに-128することで中点を0とした数学でよくある軸に変換する。
f:id:t-hom:20210103134858p:plain

取得できたX・Y座標の値を逆タンジェント関数atan2に引数として渡すと、角度がラジアンとして求まる。
ラジアンについてはきちんと調べてないんだけど、180 / PIを掛けると度数に変換できるということは調べがついている。

そうすると右端3時の方向を0°として反時計回りに0°~180°、時計回りに0°~-180°が取得できる。
f:id:t-hom:20210107222859p:plain

最後に180を足すと、左端9時の方向から反時計回りに0°~360°の形式で角度を求めることができる。
f:id:t-hom:20210107223026p:plain

以上がジョイステックから取得できるX・Yの値を角度に変換する方法である。

ちなみに、ジョイステックをどれくらい倒しこんでるのかを求めるのはもっと簡単。
f:id:t-hom:20210103140812p:plain

上図のrは直角三角形の斜辺ととらえることができるので、xとyが分かれば三平方の定理で求まる。
f:id:t-hom:20210103141147p:plain

数式はこんな感じ。
 r^{2}=x^{2} + y^{2}

ここからrを求める式をCで書くとこんな感じ。

r = sqrt(pow(x,2) + pow(y,2))

VBAだと累乗は演算子があるのでこんな感じ。

r = sqr(x^2 + y^2)

以上。

DIYついでに材料力学に入門してみた

皆さん明けましておめでとうございます。本年もよろしくお願いいたします。



さて、先日ふとしたことをきっかけに木工をしてみたところ案外うまくいったので、2021年はDIYを少しやっていきたいなと思っている。

これまで全く手を出してこなかった理由として、賃貸の集合住宅に住んでいるため大きな音を立てるということができないからだ。
しかしホームセンターで木材をカットしてもらえば、そのほかの作業は大きな音を立てずに済むということが分かったので試してみた。

作ったのはこちら。洗面所の横スペースに設置する。妙に足が長いのは下のスペースにゴミ箱を置くためである。
f:id:t-hom:20210101200547p:plain

作成にあたっては頭の中でなんとなくこんな感じでというイメージはあったんだけどそのままいきなり材料の買い出しに行くとおそらく失敗する。
そこでホームセンターのサイトで角材・板材を仮選定し、板の厚みと角材の太さを元にFusion360でモデリングを行った。最初1x1材で作ろうと思ってたんだけど、モデリングしてみると明らかに足が頼りなく、2x2材に切り替えてちょうどいい感じになった。やはり設計は重要だ。
f:id:t-hom:20210101200755p:plain

今回設計に際して困ったのが、板の厚みや角材の太さをどれくらいにすれば良いのかということ。
ビスの太さや長さ、板と角材をつなぐ金具の厚みなんかも分からない。

分からないので今回は見た目のバランスで適当に選んだが、どうにも気分的にスッキリしない。
やはり今後何か作る際には、きちんと根拠をもって材料選定や設計ができるようになりたい。

ということで、材料力学という分野に入門してみることにした。
小難しい書籍が並ぶなか、目に留まったのはこちら。

マンガでわかる材料力学

マンガでわかる材料力学

書店で立ち読みしてみると、棚とかベンチとかまさにDIYに直接役立ちそうなものが題材になっていて今回の学習目的にとても良さげなので購入してきた。


漫画なので序盤はスラスラと読み進められるのだが、数式が出てくるとやはり難しい。

ちょうどさっき困っていた問題があるので備忘録を兼ねて紹介しておこうと思う。

単純梁の反力計算というもので、左右2つの支点で支える梁の任意の箇所に荷重をかけた際に、それぞれの支点でどれくらいの反力が生じるかという問題である。
図で書くとこんな感じ。(書籍内の表現とは異なり、計算しやすいように変数を設定している。)
f:id:t-hom:20210101204722p:plain

ここで、次のような式が成り立つということが書かれていた。

力のつり合い式 P = Va + Vb
モーメントのつり合い式 Va * La = Vb * Lb

これを解くと次のようになると紹介されている。

Va = (P * Lb) / (La + Lb)
Vb = (P * La) / (La + Lb)

なるほど、分からん。。

。。。


。。。

小一時間ほどしてようやく方程式を解くプロセスが分かったので後から見直して分かるようにここに書いておこうと思う。
とりあえずVbを求める方法について。

(1) 力のつり合い式:
P = Va + Vb
(2) モーメントのつり合い式:
Va * La = Vb * Lb
(3) (1)を移項して、
Va = P - Vb
(4) (3)を(2)に代入して、
(P - Vb) * La = Vb * Lb
(5) (4)の左辺のかっこを外し、
P * La - Vb * La = Vb * Lb
(6) (5)を移項し、
P * La = Vb * Lb + Vb * La
(7) (6)の右辺を変形し、
P * La = (Lb + La) * Vb
(8) (7)を移項し、
P * La / (Lb + La) = Vb
(9) (8)の両辺を交換してちょっと体裁をいじれば解の完成。
Vb = (P * La) / (La + Lb)

式さえできてしまえばそのままプログラムに突っ込んでやれば簡単に計算できる。
たとえば1mの板の両端をそれぞれ支点a、支点bが支え、支点aから0.6mの位置に120kgの錘が乗ったとき、それぞれの支点の反力をVBA言語で求めてみる。

Sub 反力計算()
    Const P = 120 'kg
    Const La = 0.6 'm
    Const Lb = 0.4 'm
    
    Dim Va: Va = (P * Lb) / (La + Lb)
    Debug.Print "支点aの反力は" & Va & "kg."
    
    Dim Vb: Vb = (P * La) / (La + Lb)
    Debug.Print "支点bの反力は" & Vb & "kg."
End Sub

以下は出力結果である。

支点aの反力は48kg.
支点bの反力は72kg.

これくらいなら別にプログラムを書くほどのことではないかもしれないけど、やはり実際に慣れ親しんだコードの形で数式を表現してみるとようやく本当の意味で理解できたようでうれしい。


さて、序盤で既に苦戦し始めているので先が思いやられるが、最終的には微積分まで必要になるようで、そのための補助書籍もいくつか購入済である。
以前にも微積分にトライしたことはあるが、当時はファッション感覚で学ぼうとしてただけでさしたる目的もなく、途中で挫折してしまった。
今回はDIYに活かすというはっきりとした目的があるので、部材の選定や構造設計で多少使いこなせるところまではしっかり学びたいと思う。

Windows10のIMEで「かな入力」後の半角英語のShiftキーが効かなくなる件

先日から度々Shiftキーが効かない不具合に悩まされていたのだが、一応の回避策が見つかったので紹介。

【注意】今回の記事で言及している「かな入力」は日本語入力方式の「ローマ字入力/かな入力」のことです。半角・全角の話と混同しがちなのでご注意ください。多くの方が採用しているローマ字入力では今回紹介する事象は発生しません。紹介する暫定の対処方法自体は他のIME不具合にも有効だと思われるのでお困りの方はお読みください。

症状

Twitter、Officeソフト等で度々Shiftキーが効かなくなる。
どのような場合でShiftが効かなくなるのかしばらく謎だったが、再現方法が見つかったので一例として紹介する。

環境

症状が発生している私のPCのWindowsバージョンは次のとおり。
f:id:t-hom:20201231104411p:plain

「新しいIME」で発生する。見分け方としては右クリックしたときのポップアップで分かる。

新しいIMEの右クリックメニュー

f:id:t-hom:20201231104930p:plain

旧IMEの右クリックメニュー

f:id:t-hom:20201231104846p:plain

再現方法

1) メモ帳等を開き、「かな入力」をオンにする。
f:id:t-hom:20201231105055p:plain

2) 適当に日本語を入力し、Enterで確定する。

3) 半角/全角キーで英数字入力に切り替える。

4) Shift + 任意の英字キーを押下すると、Shiftが効かずに小文字が入力される。
 このとき、押下したShiftキーを放さずに別の文字キーを押しても同様に小文字となる。
 押下したShiftキーを一度開放してから再度Shift + 文字キーを押した場合は正しく大文字が入力される。

暫定回避方法

1) Windowsメニューから設定を開く
f:id:t-hom:20201231110102p:plain

2) 検索窓にIMEと入力し、候補に出てきた日本語IMEの設定をクリックする
f:id:t-hom:20201231110202p:plain

3) 全般カテゴリーをクリックする
f:id:t-hom:20201231110306p:plain

4) 下の方にスクロールして互換性セクションから以前のバージョンのIMEを使うをオンにする
f:id:t-hom:20201231110542p:plain

以上で一旦回避することができる。

おわりに

今回の不具合はWindowsのフィードバック機能からMicrosoftにレポート済み。
もしかしたら私がレポートする前に既に最新Updateで修正されているかもしれないけど、とりあえず文句の一つも言いたくなるような不具合なので。。
特に「かな入力」は人口が少ないので不具合を検索しても同じ症状がヒットせず、事象の特定に時間がかかったので今回記事にまとめることにした。

同じ症状で悩んでる方に情報が届くと良いなと思う。

なお、私が「かな入力」を使う理由は日本語の文章を入力する場合、単純にかな入力のほうがストロークが少なくて済むから。
調査によると、同じ文書を入力するのにローマ字入力では約1.6倍のストロークが必要になる。なるべく優雅にタイピングしたいのに、ローマ字入力では1.6倍ガチャガチャしないといけない。かな入力に慣れてしまうとこれは相当にストレスフルである。

かな入力に目覚めた経緯については以下の記事で紹介しているのでよろしければどうぞ。
thom.hateblo.jp

Raspberry Pi 4とAmbientで時間帯ごとのインターネット回線スピードをグラフ化する

最近インターネットスピードがすこぶる遅い気がしたので、実際に計測してみることにした。
今回はラズパイを使用したけど、Windowsでも似たようなことができるので追加費用をかけたくない方は調べてみると良いかと思う。

背景

以前から回線スピード計測サイトで何度か試したところ、概ね10Mbps程度しか出ていない。ひどいときは下り 3Mbpsなんてことも。

最大 1Gbps(1024Mbps)と謳われているのにさすがに 10Mbpsはひどい。まぁ快適なときはわざわざ測ろうと思わないので、時間帯によってはもう少しマシなのかもしれない。

私もIT運用に携わる人間なので、1Gbpsでる訳ないのは知ってる。
せいぜい数百Mbpsだろうとは思っていたけど、その100分の1ってのはさすがに酷いのでは。

計測結果

実際に計測した結果がこちら。1分毎に計測したデータを1時間ごとの平均値で集計している。精々100Mbpsがいいところ。
f:id:t-hom:20201215063235p:plain

月曜は少しマシだった。
※形が違って見えるのは上のグラフとは開始時刻が違う為で、混雑時間帯はどたちらも同じ。
f:id:t-hom:20201215063715p:plain

ちなみに集計せずに1分ごとの速度を出したところ、日曜の最大瞬間スピードは約500Mbpsだった。
f:id:t-hom:20201215064723p:plain

最大1Gbpsというのはこの最大瞬間スピードの理論上の上限のことで、実際はマンション内で最大で32分岐されてる。自分以外の31回線が全く使用されていない場合の、NTT網までのスピードである。

[参考]
notoken.hatenadiary.com

この先更にプロバイダーが居て、その先に接続先サイトがあるといった具合なので、1Gbpsはまず無理だ。500Mbpsは上出来だと思う。
それはそれとして最大1Gbpsを大々的に喧伝していることには腹が立つけど。

快適にYouTubeを視聴できる速度

さて、どれくらいの回線スピードが出ていれば快適にYouTubeを視聴できるのかを確認する。
動画の容量もピンキリだけど、以下のサイトを参考に、10分のFull HDを400MBと仮定して計算してみた。
www.appps.jp

MBという単位はメガバイト、Mbpsはメガビット パー セコンドなので、まずはバイトとビットを変換する。
1バイト=8ビットなので、400MBは3,200Mbit。 3,200メガビット/10分以上の速度があれば、快適に視聴できることが分かる。

つまり
3,200Mbit/10分

320Mbit/1分

5.3Mbit/1秒

5.3Mbps

5.3MbpsあればYouTubeのFull HD動画は快適に視聴できる計算になる。

日曜の10分毎の平均速度データで確認すると、大体19時半から23時50分くらいまでが快適スピードを下回っていることが分かる。遅いなぁと感じた時間帯とも感覚的には一致しているので正しく測れているように思える。
f:id:t-hom:20201215083440p:plain

ただこれはYouTubeが高効率の圧縮率を達成しているからだともいえるので他の動画視聴サイトでは場合によってもっと速度が出ないと頻繁に読み込み中になったりするかもしれない。

使用したもの

Raspberry Pi 4B 8GB

Raspberry Pi 3Bではそもそも100Mbps、3B+でも300Mbps程度しか速度が出ないので、正確に計測するには4を選択する必要がある。

以下、ラズパイ3Bの速度について書かれてる。(最新と書かれてるが既に記事が古いだけで4のことではない)
xtech.nikkei.com

メモリは4GBモデルでもOK。

ケース

Raspberry Pi 4からは発熱が大きくなるのでCPUファンが必要になる。
こちらの製品はヒートパイプまでついてて相当に冷えそうな感じなのでこれにした。先日知ったのだが、ヒートパイプはただの銅の棒ではなく、中に液体が入っている。加熱部でこの液体が蒸発して瞬時に放熱フィンに熱が伝わり、フィンで熱を十分に分散させたあとにファンによる風でそれを冷やすという構造。冷却部では蒸発した気体が液体に戻り加熱部に戻っていく。

まぁ、ラズパイにこの熱対策は大分オーバーキルな気がするが。。

とても良いケースなんだけど、1点だけ注意点がある。
土台みたいなパーツが付いてて、まずそれに乗せるのかと思いきやこれはケース無しで使う用なので使わない。
それ用の組み立て説明書を読み始めてしまい、大分混乱した。正しい説明書はカード1枚で裏表にカラー写真があるもの。

ちなみにラズパイ3と4では形状が違うのでケースの使いまわしは不可。

ACアダプター

こちらもコネクタ形状が異なるためラズパイ3のものは使用できない。

モニターケーブル

片側が普通のHDMI、もう片方がHDMIマイクロタイプのものを選択する。
HDMIミニと間違えないように注意。

その他

キーボード、マウス、HDMI対応ディスプレイ、microSDカード(最低8GB)、Cat 6LANケーブル(無線だと電波干渉の影響がぬぐい切れないので)

セットアップ

ラズパイ

ラズパイ自体のセットアップは様々なサイトで解説されてるのでここでは割愛する。

PythonでAmbientとspeedtestを使う為に以下のコマンドでライブラリを導入しておく。

sudo pip3 install git+https://github.com/AmbientDataInc/ambient-python-lib.git
sudo pip3 install speedtest-cli

Ambient

以下のサイトでユーザー登録をする。
ambidata.io

8チャネルまで無料で利用でき、チャネルごとにデータが8種類登録できるので十分である。制限事項の詳細は以下参照。
ambidata.io

Pythonコード

以下を/home/pi/sp.pyとして保存する。

import ambient
import speedtest

servers = []
threads = None

ambi = ambient.Ambient(チャンネルID, "ライトキー")
s = speedtest.Speedtest()
s.get_servers(servers)
s.get_best_server()

d = s.download(threads=threads)
ambi.send({"d1":d/1024/1024})
u = s.upload(threads=threads)
ambi.send({"d2":u/1024/1024})
s.results.share()

results_dict = s.results.dict()

※チャンネルIDとライトキーはAmbientでチャンネル作成した際に確認できる情報を記入する。

実行

sp.pyは1度実行すると通信速度を計測後にAmbientにデータを送信し、ブログラムを終了する。
継続して計測するには、Linuxのcronに登録しておく。

cronの指定は以下のとおり。

*/1 * * * * python3 /home/pi/sp.py

あとはAmbient側のチャンネルにアクセスしてグラフを追加し、元データの設定を行うだけ。

終わりに

最近特にコロナ禍で動画視聴の重要が高まっていて、回線速度低下が起きているらしい。
以下の総務省資料がこのあたり詳しく解説している。
https://www.soumu.go.jp/main_content/000692609.pdf


通信速度改善の手立てとして、NURO光も検討したけど、最近脆弱性が見つかったという記事を見たのと賃貸オーナーへの相談が面倒なのでやめておいた。

とりあえず、もう少し計測データを集めたらIPv6サービスへの申し込みを検討中。IPv6でも速度の上限は変わらないけど回線がまだ空いてるようなのでマシになることを期待。

12/16追記 IPv6サービス利用開始

IPv6サービスの利用を開始したので測定結果を追記。
以下は1分ごとの測定結果を10分単位で平均したグラフ。最も混雑する22時でさえ80~100Mbps出ている。
f:id:t-hom:20201216230840p:plain

ストリーミング動画の視聴もすこぶる快適でIPv6が空いているというのは本当だった。
とりあえずこれでインターネット速度の不満は解消した。

Raspberry Piでネットワーク対応の電光掲示板(16×96ドット)

今回はRaspberry Pi Zero WHで電光掲示板を動かしてみた。
ただ動かすだけではなく、無線LANに接続して他の端末から受信したメッセージを表示させる。

私がこれを作成した目的は、前回紹介した空気モニターの内容を掲示板に常時表示させるためである。
※前回記事
thom.hateblo.jp

作成するもの

動作イメージはこんな感じ。

※mp4からGIFアニメ化したときにかなり高速になってしまったけど、実物はだいたいこれの3分の1くらいのスピードでスクロールする。

色々と調べた結果をベースとしてプログラムで空気品質の基準値を定めていて、基準内の項目は緑の文字で、閾値付近はオレンジで、基準オーバーは赤の文字で表示させるようにした。

材料

Raspberry Pi Zero WH

最初はArduinoで動かすことを考えたが、掲示板のドット数からしてメモリが不足するという情報があったのでラズパイにした。
最初はRaspberry Pi 3B+で実験していたが、以前購入したまま眠っているZero WHがあるので私はそちらを使用した。
無線LANに対応したラズパイがあれば何でも動くと思う。

LEDマトリクスパネル(16x32を3枚)

ラズパイZero用ユニバーサル基盤

ビニール被膜の単線ワイヤー

電気ワイヤーは単線タイプと撚り線タイプがある。
針金のように一本だけ入ったのが単線タイプで、細い線が複数ねじりあわされたのが撚り線タイプ。
撚り線のメリットは柔らかいことであるが、今回は配線が細かくてカチっと位置決めしないとハンダ付けしにくいので単線タイプを使用する。

熱収縮チューブ

これはLEDマトリクスに電源を供給するためのコードを改造する為に使用した。

LEDマトリクスに付属の電源コードは1本がパネル2枚分に枝分かれしていて、それがLEDパネル1枚に1セット入っている。
1本で2パネルなので2本使えば良いのだが、そうすると1パネル分あまって邪魔だし、大本の電源は1本化したいので、1本を3パネルに分岐させるようなコードを作成した。

アルミチャネル材

これはホームセンターとかで売ってるコの字型の長いアルミ棒。
2本買って、3つのパネルを連結するのに使用した。なんでも良いと思うけど私が使ってるのは以下の商品のようだ。(JANコードで検索した)
tcss.vivahome.com

工具

アルミ用ヤスリ

切断面を危なくない程度に仕上げるために使用。
私の場合はパネルの上用と下用で切断する長さが2ミリほどずれてしまったのでヤスリで削って合わせた。
たった2ミリだけどヤスリで削るのはとても時間がかかった。。

ボール盤

今回これのためにボール盤を購入。評価は賛否分かれてたけど、普通に使用できて満足。
代用としてはセンターポンチと手持ちタイプの電動ドリルで穴開けする方法もあるが私は何度やってもズレる。これでアルミ材を1つダメにした。
これはネジで止める固定用の穴なので、ズレると3枚のLEDにスキマができたり上下ズレたりで綺麗に1枚にならない。
1万ちょっとでこの手の工具が購入できるということでテンションが上がったこともあり、そのままポチってしまった。

ただ付属のバイスでは縦方向の材料の位置合わせが難しく、他のアルミ材を一緒に挟んで位置を調整したりと、位置合わせにとても苦労した。
細かい位置合わせ用のテーブルが別途あるようなのでそういうのを買うと良いのかもしれない。
ちなみにこの会社、ちゃんと日本で検品してから出荷してくれてるらしく、一度開封されているので未開封品ではない。
また、本体の袋が機械油まみれで届くので新聞紙を敷いたりキッチンペーパー等で余計なところに付いた油を取ったり色々汚れる覚悟は必要。

使用上の注意を守らないとかなり危ないので、一般的なボール盤の注意点や、この製品の注意点についてよく調べてから使うこと。
特に油まみれになるので軍手とかしたくなるが、巻き込み事故の原因になるので絶対に素手で扱うこと。
金属片が飛んでくる恐れがあるのでセーフティーゴーグルをすること。

集合住宅なので音が心配だったけど、空ける穴が小さいためかかなり静かだった。
卓上ボール盤は普通にマンション住まいでも使えると思う。

鉄工用の丸軸ドリルビット

3ミリを使用した。

アルミ用ってのがあればそれで良いけどホームセンターで見つけられなかったので鉄工用を購入。

ハンドドリル用だと六角軸のものが主流で、そのまま使えなくはないがおススメはしない。
ボール盤のドリルチャックは3つの歯でドリルをくわえるような形になっていて、無理な力がかかった時に丸軸ドリルがスリップして本体の破損を防ぐらしい。六角だと滑れないので本体がダメージを受ける。

作成手順

配線計画

細かい配線の前にまずは全体図。

ラズパイに接続されたユニバーサル基盤に、データ信号用のボックスソケットと電源用のネジ端子が接続されていて、ネジ端子から各LEDパネルへ電源が供給されている。データ信号はLEDパネルのInputから入り、Outputから出た信号が次のパネルのInputへ接続される。それぞれのパネルをフラットケーブルで接続する際に、Output→InputとつなげばOK。

次にボックスコネクターとラズパイのピンの対応について説明する。
ボックスコネクタをアップで確認するとそれぞれのピンに記号が書かれているが、残念ながら基盤が隠れて右半分しか確認できない。

ラズパイとの対応も併せて、残りはこちらのサイトで確認することになる。
github.com

最初に出てくる画像は忘れて構わない。2つ目のこれ↓がLEDパネルのInputピンアサインである。

つまり切り欠きを左としたときに、次のようなピンアサインになっている。

次に、以下の図を確認する。LEDパネルはラズパイから最大3レーン接続でき、それぞれのレーンに最大12枚ずつチェーンできる。

今回は1レーンしか使わないので、ニコチャンマークに注目する。

次にラズパイ側のピンとの対応図が出てくる。

そのままExcelに貼り付けて、余計な記号等を消し込む。

10番ピンの(for 64 row matrix, 1:32)と、22番ピンの(for 32 row matrix, 1:16)はどちらも消してOK。
rowってのが行数を表すが、今回買ったLEDパネルは16行x32列なので関係ない。

ボックスコネクタ側であるが、GNDはおそらく3か所中1か所繋いでおけばOKなのと、Dは今回使用しないので実際には次のようになる。

あとはユニバーサル基盤にラズパイと重ね合わせるためのピンヘッダと16ピンソケットを取り付けて、配線の対応どおりに繋いでいくだけ。


繋ぐ前からひどいことになるのは見えているわけだが。。根気よく頑張る。

実際の基盤上の配線

ユニバーサル基盤上の配線が完成するとこんな感じになる。

裏面

※実はユニバーサル基盤の裏と表を間違えたっぽい。。せっかく5Vとかのラズパイのピンアサインが書かれているのに上下左右逆になってしまい余計配線で混乱することになった。

表面

対応表を見ながらハンダで繋いでいくのだが、ハンダをつけるのに何度も裏返すので、途中で頭がこんがらがってくる。
これだけで3時間かかった。めちゃくちゃ大変。

※実は自分で作らなくても専用ハットが売ってるらしいんだけど、もともと参考にしたサイトがそういうのを使わずにジャンパーピンで全部つないじゃったということだったので、真似してうまくいった成功体験からハットは使わずに自分で作った次第。皆さん真似される方はぜひハットを購入して、それをまた記事にしていただけたらと。。

ちなみにパネル用の電源はラズパイの5Vピンと、余っているGNDピンからネジ端子まで引っ張ってきている。

ネジ端子の基盤に挿すピンは太くてそのままでは挿せないので、ドリルで少し穴を広げてそのまま配線用のハンダで固定した。

電源コードの配線は配線図通りで良いので写真での解説は割愛する。赤線は赤線同士、黒線は黒線同士くっついていればそれで動くので適当に切断してしかるべき場所の被膜を剥いて熱収縮チューブを通してから半田づけで線同士を結合し、熱収縮チューブをハンダごてで撫でて収縮させて金属部分を覆えば完成。

ラズパイのセットアップ

ラズパイゼロには、Raspberry Pi OS Liteをインストールしておく。
これはGUIを持たないコマンドだけのOSで、余計なリソース消費が無いのでIoTにはおススメ。

Micro USBにOSを書き込んだ後、所定のフォルダにWifi設定を記述したテキストファイルを入れておくとモニターもキーボードも繋ぐことなくインストールできる。「ラズパイZero ヘッドレスインストール」などと検索すると方法が見つかるかと思う。

セットアップして起動まで終わったらSSHで接続して、sudo apt-get update、sudo apt-get upgradeを済ませておく。
メモリが少ないせいか、割と時間がかかる。

次にライブラリ等の入手とインストールを実施。

2022/3/29 Raspberry Pi OS BullsEyeへアップグレードした際に少し変わったので更新
gitがインストールされてないので、そのインストールコマンドも書いておく。gccは入ってたかどうか忘れたけど一応。。

# 以下コメントアウトしたのはRasbperry Pi OS(Buster)での古いやり方
#cd /home/pi/
#sudo apt-get install git
#sudo apt-get install gcc
#git clone https://github.com/hzeller/rpi-rgb-led-matrix/
#cd rpi-rgb-led-matrix
#make -C examples-api-use
#cd /home/pi/
#sudo pip3 install Pillow
#sudo pip3 install feedparser
#sudo apt-get install ttf-takao-mincho

# 以下がRaspberry Pi OS(BullsEye)でのやり方
cd /home/pi/
sudo apt install git
git clone https://github.com/hzeller/rpi-rgb-led-matrix/
cd rpi-rgb-led-matrix
make -C examples-api-use
cd /home/pi/
sudo apt install python3-pip
sudo pip install Pillow
sudo pip install feedparser
sudo apt install fonts-takao-mincho
sudo apt-get install libopenjp2-7

そしてサンプルを実行

sudo  /home/pi/rpi-rgb-led-matrix/examples-api-use/demo --led-no-hardware-pulse --led-rows=16 --led-cols=96 -D 1 -m 20  /home/pi/rpi-rgb-led-matrix/examples-api-use/runtext16.ppm

実行するとこんな感じで文字が流れていく。

電光掲示板側のプログラムコード

いろんなところからお借りしたコードを混ぜてるので余計なインポートが残ってると思うけど気にせず公開。。
このコードはソケット通信でデータを待ち受けて、データが来たらppmファイルを生成し、先ほどのテキストスクロールデモを開始して作成したppmファイルを再生させている。
データはUnicode文字列・RGBコードで構成されたリストをjson形式で受け取る形式である。ちょうどコード中に「電光掲示板テスト」と書かれているあたりがデータ形式である。

注意点として、コード中のIPアドレスの部分は自分のラズパイのアドレスを入れること。
50007は待ち受けポートなのでそのままでも良い。

プログラムファイル名はserver.pyとしておく。

import socket
import json
from subprocess import Popen
from time import sleep
from PIL import Image, ImageFont, ImageDraw

def createPPM(text):
    font = ImageFont.truetype("/usr/share/fonts/truetype/takao-mincho/TakaoMincho.ttf", 16)
    all_text = ""
    for text_color_pair in text:
        t = text_color_pair[0]
        all_text = all_text + t
    
    width, ignore = font.getsize(all_text)
    im = Image.new("RGB", (width + 30, 16), "black")
    draw = ImageDraw.Draw(im)
    
    x = 0;
    for text_color_pair in text:
        t = text_color_pair[0]
        c = tuple(text_color_pair[1])
        draw.text((x, 0), t, c, font=font)
        x = x + font.getsize(t)[0]
    
    im.save("/home/pi/message.ppm")
    return Popen(["exec /home/pi/rpi-rgb-led-matrix/examples-api-use/demo --led-no-hardware-pulse --led-rows=16 --led-cols=96 -D 1 -m 20 /home/pi/message.ppm"], shell=True)


proc = createPPM([
    [u"    " + "電光掲示板テスト",[255,255,0]],
    [u"    " + "これは初期メッセージです。",[0,255,255]],
    [u"    " + "ネットワーク経由でメッセージを送付してください。",[255,0,255]]])

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind(('192.168.1.5', 50007))
    s.listen(1)
    while True:
        try:
            conn, addr = s.accept()
            with conn:
                data = conn.recv(1024)
                if not data:
                    break
                proc.terminate()
                proc = createPPM(json.loads(data.decode('utf-8')))
                conn.sendall(b'Received: ' + data)
        except KeyboardInterrupt:
            proc.terminate()
            s.close()

クライアント側のコード

これは同一ネットワーク上に存在している別のPCなり別のラズパイから実行させるコードである。
ファイル名はclient.pyとしておく。

import socket
import json

msg = ((
	(u"    " + "オレンジ色のメッセージテスト",(255,165,0)),
	(u"    " + "ライムグリーン色のメッセージテスト",(50,205,50)),
	(u"    " + "ターコイズ色のメッセージテスト",(64,224,208))))

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
	s.connect(('192.168.1.5', 50007))
	s.sendall(bytes(json.dumps(msg), 'utf-8'))
	data = s.recv(1024)
	print(repr(data.decode('utf-8')))

実行

まずラズパイゼロでサーバープログラムを実行する

sudo python3 server.py

※一般ユーザーでも実行できるけどなぜかLEDがちらつく。おそらくsudoで安定するのはリソースを優先的に使用できるのではないかと思われる。

これでサーバー側はデータ受信待ちになる。

次にクライアント端末からクライアントプログラムを実行する。

python3 client.py

これでクライアントプログラムで指定した文字が、指定した色で電光掲示板に表示されたら成功。
サーバーは起動しっぱなしでOK。

クライアントのメッセージをいじって再度実行すれば、表示メッセージも変わる。

12/10追記

sshで実行してる間、ターミナル開きっぱなしにして運用していたがメインPCの電源を落としたりネットワークが瞬断した際にSSHも切断されてしまい、server.pyが落ちて掲示板が消えてしまう。
対策として、nohupコマンドと「&」によってバックグラウンド実行させることにした。

sudo nohup python3 server.py &

ただ、初回のメッセージ受信時に既存のdemoプロセスとは別にdemoプロセスが起動してしまい、2つのメッセージが被って表示がおかしくなる事象が発生した。これは後述のトラブル対応で片方のdemoプロセスをKillすることで以降は発生していない。

実行時のトラブル対応

サーバープログラムは記述ミスやなんらかの原因で終了したりすると、demoというプロセスが残り続け、pythonコードが終了しても掲示板メッセージが消えない。この状態で再実行すると、表示が重なったような感じで実行されてしまう。
こうなったらdemoプロセスを終了させればよいので、「ps -All | grep demo」でPIDを特定し、「sudo kill PID」で終了させると改善する。

GUIが無いので基本的にコマンドでなんとかするしかない。Linuxの基本的なコマンド(特にリブートやシャットダウンなど)はここでは解説しないので検索するなどしてトラブルに対処してもらえると良いかと思う。

組立と設置

これも割と苦労したポイントではあるが、どちらかというと材料と加工ツールの選定に時間を取られただけなので、ここでの説明は簡単に。

まずアルミチャネル材をLEDパネル3枚分の長さに切断する。これを2本用意する。
私の場合はあとで壁にネジで取り付ける想定で、LEDパネルよりも4センチほど長めにした。
結局壁の有孔ボードのフックにちょうど引っかかったので、今後ネジ止めするかどうかは不明。

次にヤスリで切断面を均す。

穴を空ける位置に油性マジックで印をつけ、ボール盤で穴あけ。

8ミリ長のM3ネジでアルミチャネルとLEDパネルを止めていく。
LEDパネル側がネジ穴になっているので特にナットとかは使わなくてOK。

任意の方法で壁に取り付けて完成。

主な参考サイト

qiita.com
digirakuda.org

以下再掲
github.com

以下はそもそもこれをやりたくなったキッカケ
dailyportalz.jp
Arduinoでビットパターン書いてるっぽいんだけど、コードの掲載はないのでどうやってるのか不明。

後書き

モノが完成してから記事にするまでずいぶんと時間がかかってしまった。記事を書き終わるまで大体4時間。なかなか大変な作業であるが、人に説明するとなってようやく理解できることもある。
たとえばLEDとラズパイのピン接続について、最初私が作るときは英語サイトで書いてる内容はあんまりちゃんと読んでなくて、ほぼQiitaの丸コピで作ったんだけど、私が意味を分かってないままここで再掲しても単なる劣化版記事になってしまう。わざわざ書くからには、先人が書いた記事を参考にしつつもそこに何等かの付加価値をつけたいと思い、少し余分に解説してみた。

これを見て自分でやりたくなった誰かがまた別の切り口で記事を書き、解説レベルが上がることで真似する方もハードルが下がって応用例が増え、それをまた誰かが真似て発展させるという好循環が生まれると良いなと思う。

以上

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。