t-hom’s diary

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

技術的概念の説明で使用する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の丸コピで作ったんだけど、私が意味を分かってないままここで再掲しても単なる劣化版記事になってしまう。わざわざ書くからには、先人が書いた記事を参考にしつつもそこに何等かの付加価値をつけたいと思い、少し余分に解説してみた。

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

以上

Arduino Pro MicroとラズパイとAmbientで空気モニター(温度、湿度、気圧、PM2.5、CO2)

今回は以前プロトタイプを作成した環境センサーを普段使いできる形に整えていく。

以前の記事はこちら。
thom.hateblo.jp

作成するもの

前回のハード

f:id:t-hom:20201108113845p:plain
前回はこんな感じでブレッドボードにセンサー類を挿していただけなのですべてむき出し。
普段使いするにも邪魔になる。

今回のハード

f:id:t-hom:20201129122733p:plain
今回はケースに収めて壁に設置した。見た目にもスッキリしてとても良い。

前回のソフト

f:id:t-hom:20201108114614p:plain
前回はArduinoのシリアルモニタに表示させただけ。PCで常にシリアルモニターを開いていないと見えない。

今回のソフト

f:id:t-hom:20201129123252p:plain
今回はラズパイ経由でAmbientというクラウドサービスに連携することでデータをグラフ化することにした。

材料

センサー類

センサー類は前回の記事と変更ないので説明を省略する。

Arduino

PS4コントローラーの記事では3.3V版だったが、今回は5V版のPro Microを使用した。
二酸化炭素センサーとホコリセンサーが5Vで動作するためである。
※温湿度気圧センサーはレギュレーターが入っているので入力は5Vでも3.3Vでもどちらでも動作するらしい。

ミニブレッドボード

これはArduino Pro Microを差し込む用のブレッドボード。
裏が両面テープになっているのでケースに張りつけられる。

あと、名刺くらいのサイズのブレッドボードの電源ラインだけ分離させて、電源集約用の材料として使っている。

ケース

タカチ電機工業の「PF15-5-15W」というケースを選択。
これ、型番で検索してもドンピシャでヒットせず、シリーズのページにたどり着くので割と困る。
写真と商品が違ってたりするので、同じ物を購入する場合は型番とサイズに注意。
f:id:t-hom:20201129125027p:plain

あとPFとPPFは似てるけど形状が違うのでそこも注意。

ケースのサイドパネル

サイドパネルは付属しているが、穴あけ加工に失敗すると取り返しがつかないため最初から3Dプリンターで作成することにした。
f:id:t-hom:20201129125648p:plain

寸法はこちらのサイトで確認できる。
https://jp.misumi-ec.com/vona2/detail/222005614912/?HissuCode=PF15-5-15W

接着式基盤スタンド

サンハヤトのSPT-300を使用した。
https://www.sunhayato.co.jp/material2/afp03/item_1115

これはセンサー類をケースに固定するパーツである。
両面テープでケースに貼り付けることで、取り付け用のネジ穴になる。

センサーケーブル類

センサ類のケーブルが長いとごちゃっとするので圧着端子と圧着工具を買ってきて自分でジャンプワイヤーを作った。

ワイヤ

ワイヤストリッパー(被膜を剥く工具)

コネクタ

圧着工具

あと埃センサーは日本圧着端子のEHコネクター5ピンで接続されてたのでそちらも購入。
http://www.jst-mfg.com/product/pdf/jpn/EH.pdf

既存のケーブルを切っても良かったけどもったいない気がして。。これは上記のコネクタとはサイズが違う為EHコネクタ用の圧着端子が別途必要である。大阪のシリコンハウス(実店舗)でケーブルに圧着済のものが売られていたので、コネクタと圧着済ケーブルを購入した。

USB

ケースにUSB差し込み口を作りたいのでUSB Micro-DIP化基盤も準備

基盤からケース内部でUSBケーブルに戻してArduino Pro Microに接続するためのアダプタケーブル

ケースファン

ケースファンは冷却の為ではなく、ケース外の空気を取り込んで埃センサーを正しく反応させるためのもの。

ホットボンド

これはケースのサイドパネルにケースファンとUSB基盤を取り付けるのに使用した。
本当は3Dプリンターで固定用の機構まで出力できれば良いのだが、そんな技術力がないのでホットボンドで代用。

配線図

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

ピン番号等はほとんど前回と同じであるが、Arduino Pro MicroはSDA、SCLが2番・3番に割り当てられているので、そこだけ違う。
ピンアサインは以下のサイトで調べられる。
qiita.com

実際の配線

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

まずケース内にパーツを仮配置してブレッドボード・接着式基盤スタンドを貼り付けてからセンサー類を固定。
その後、ケーブルの長さを決めて圧着し接続。
ケースファンとUSB受け口の基盤はそれぞれホットボンドで固定すれば完成。

サイドパネルの3Dモデリング

Autodesk Fusion 360は非商用ならも無償で利用できる。
本格的な解説はしないけど、雰囲気だけ紹介する。

まず、スケッチ作成ボタンをクリックする。
f:id:t-hom:20201129141716p:plain

次にスケッチする面を選択。ここでは低面をクリック
f:id:t-hom:20201129141822p:plain

線分を選択
f:id:t-hom:20201129141930p:plain

てきとうな台形を書く。書いてる途中で寸法も一応出るけど無視して超テキトーな形でOK。ただし底辺と上辺の平行は出しておきたい。
f:id:t-hom:20201129142023p:plain

寸法ボタンをクリック。
f:id:t-hom:20201129142159p:plain

角度や寸法を入れていく。
f:id:t-hom:20201129142418p:plain

フィレットボタンをクリック。
f:id:t-hom:20201129142534p:plain

フィレットを上辺の隅に設定する。
f:id:t-hom:20201129142634p:plain

スケッチを終了すると視点が斜めにもどる。
f:id:t-hom:20201129142730p:plain

押し出しツールを選択する。
f:id:t-hom:20201129142812p:plain

矢印が現れるので、上に引っ張る。
f:id:t-hom:20201129142844p:plain

最後に寸法を手入力して立体の完成。
f:id:t-hom:20201129142924p:plain

あとは穴ツールでファン用の穴を空けたりするだけ。
f:id:t-hom:20201129143050p:plain

実際にはもう少しだけ複雑なスケッチを書いてるけど雰囲気はこんな感じである。

完成したらSTL形式でエクスポートし、さらにUltimaker Curaというソフトで3Dプリンター用のデータに変換する。
Curaは基本的にSTLを読み込んでそのまま3Dプリンター用データをSDカードに書き込んでるだけなので、特に説明することはない。本当はここで材料設定とかサポートとか密度とかを設定するらしいんだけど、知識不足のためほぼデフォルトでしか使ったことがない。

Arduino側のコーディング

前回との変更点は、データ取得タイミングをまとめて最後にスペース区切りのデータとしてシリアル送信するようにしたことである。他は特に変わっていない。書き込みはArduino Leonardoボードを選択する。ただし、このあとPCからラズパイに接続しなおす再に結構トラブルがあって、データが来てるのかどうか良く分からない事象が発生したので、私の場合はラズパイ自体にArduino IDEをインストールしてそこから書き込むようにした。

//for CO2
#include <SoftwareSerial.h>

//for BME
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#define SEALEVELPRESSURE_HPA (1013.25)

//for CO2
SoftwareSerial swSer(8, 7);
uint8_t cmd[9] = {0xFF,0x01,0x86,0x00,0x00,0x00,0x00,0x00,0x79};
uint8_t reset[9] = {0xFF,0x01,0x87,0x00,0x00,0x00,0x00,0x00,0x78};
uint8_t res[9] = {};
uint8_t idx = 0;
bool flag = false;
uint16_t co2=0;

//for BME
Adafruit_BME280 bme;

//for DustSensor
int pin = 9;
unsigned long t0;
unsigned long ts = 30000; // 30000ms
unsigned long lowOc = 0;
float ratio = 0;
float concent = 0;

void setup() {
  //for Common
  Serial.begin(9600);

  //for CO2
  swSer.begin(9600);
  //swSer.write(reset,9);//calib
  //delay(60000UL);

  //for BME
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }

  //for DustSensor
  pinMode(9,INPUT);
  t0 = millis();
}

void loop() {
  uint16_t val=0;
  float t;
  float temperature;
  float pressure;
  float humidity;
  swSer.write(cmd,9);

  //BME
  temperature = bme.readTemperature();
  pressure = bme.readPressure() / 100.0F;
  humidity = bme.readHumidity();

  //DustSensor
  t0 = millis();
  lowOc = 0;
  while((millis() -t0) <= ts) {
    lowOc += pulseIn(pin, LOW);
  }
  ratio = lowOc/(ts*10.0);
  concent = 1.1 * pow(ratio,3) - 3.8 * pow(ratio,2) + 520 * ratio + 0.62;
  
  delay(30000);

  //CO2
  while(swSer.available()==0){
    Serial.println("bad com");
  }
  while(swSer.available()>0){
    res[idx++]=swSer.read();
    flag=true;
  }
  idx = 0;
  if(flag){
    flag=false;
    co2 = 0;
    //delay(100);
    co2 += (uint16_t)res[2] <<8;
    //delay(100);
    co2 += res[3];
    t = res[4];
  }

  Serial.println("data: " +
    String(temperature) + " " +
    String(pressure) + " " +
    String(humidity) + " " +
    String(co2) + " " +
    String(concent));
}

Ambientの登録

Ambientにアカウント登録をしておく。個人利用なら無償のプランで十分である。
チャネルIDとライトキー(書き込み用のID)が入手できれば準備OK。
ambidata.io

ラズパイ側のコーディング

コードの前にラズパイのpythonにambient用のライブラリを導入しておく。

sudo pip install git+https://github.com/AmbientDataInc/ambient-python-lib.git

詳細はAmbientサイトのリファレンスにラズパイでの使用方法が書かれているのでうまくいかなければそちらを参照して欲しい。

ラズパイ側で作成するコードはこちら。ファイル名は任意で良い。
一部可変の箇所があるので後述する。

import ambient
import time
import serial

def pcs2ugm3(pcs):
    pi = 3.14159
    density = 1.65 * pow (10,12)
    r25 = 0.44 * pow(10, -6)
    vol25 = (4/3) * pi * pow (r25, 3)
    mass25 = density * vol25
    K = 3531.5
    return pcs * K * mass25

ser = serial.Serial('/dev/ttyACM0', 9600)
ambi = ambient.Ambient(★チャネルID★, ★"ライトキー"★)

while True:
    if(ser.in_waiting > 0):
        line = ser.readline().split()
        print(line)
        if line[0] == b'data:':
            try:
                r = ambi.send({ \
                    "d1": float(line[1]), \
                    "d2": float(line[2]), \
                    "d3": float(line[3]), \
                    "d4": float(line[4]), \
                    "d5": pcs2ugm3(float(line[5])), \
                    })
            except:
                print("Connection Error")
        else:
            print("Error")
    time.sleep(1)

可変の箇所

以下の2行は環境によって可変となる

ser = serial.Serial('/dev/ttyACM0', 9600)
ambi = ambient.Ambient(★チャネルID★, ★"ライトキー"★)

まずUSBのデバイス名が「/dev/ttyACM0」としているが、これを調べるにはまずUSBを抜いた状態で「ls /dev/tty*」コマンドを実行する。するとデバイスの一覧が出てくる。次にUSBを指した状態で「ls /dev/tty*」を再実行する。ここで出てきたデバイスの一覧と先ほどの実行結果を比べて、増えたデバイスが今挿したUSBである。

まぁ通常は順番に振られるのでArduinoを1台だけ繋いでいたら/dev/ttyACM0になるはず。

次に★チャンネルID★と★"ライトキー"★の部分は自分のチャンネルIDとライトキーに書き換える。
★は目立つようにしただけなので実際には書いてはいけない。

チャンネルIDは整数なのでそのまま記載、ライトキーは文字列なのでダブルクォートで囲む。

実行

Arduino上のセンシングデータはUSBを繋いだ時点で随時ラズパイに送られてるので、あとはラズパイ側で上記のプログラムを動かすだけである。私の場合はコマンド画面を立ち上げっぱなしにしているけど、バックグラウンド実行でも良いかと思う。デーモン化までは面倒なのでやっていない。

Ambient側のグラフ作成は直感的に分かると思うので説明は割愛する。

トラブルシューティング

接続があってるのにArduinoがデータをよこさないことがある。
切り分けとしては、ラズパイ側にArduino IDEを入れてそこでシリアルモニターでデータが来ているか確認する。
ttyデバイス名が合ってるかどうかも再確認。

あとは、電源つないだまま配線いじったりするとプログラムの挙動がおかしくなることが多い。書き込みなおすとうまく動いたりする。USBのつなぎ直しでプログラムがおかしくなるケースにも遭遇したので、やはりラズパイにArduino IDEを入れてしまってそっちから書き込んでしまうのが早いかもしれない。

ちなみに、Arduino Pro Microではそもそも私のPCでは書き込み不可トラブルが発生することがあった。これはNZXTのCAMというソフトを終了させたら改善したので、類似のトラブルが発生した場合は稼働中のソフトウェアとの競合を疑うのも手だと思う。特にCAMのようなハードウェア連携するツールは怪しい。

Ambientのグラフに関してもちょっと変だなと思う挙動がある。最初のグラフ配置はページの左上に収まっていたのにグラフをドラッグしたら左上にマージンができてしまい、元の位置には戻せなくなる。これは気にせずそういうもんだと思ってそのまま使うことにした。

あとがき(余談)

思ったよりボリューム満載の記事になってしまった。モノを作るのって結構大変。
さて、今回はArduinoとラズパイとクラウドサービスAmbientを連携させたが、ラズパイの役割って単にネットワーク機器としてArduinoのデータをAmbientに橋渡ししてるだけなので、無線LAN付きのArduinoを使えばラズパイをかまさなくても同じことができると思う。

そう思って一旦ESP8266という無線付きのマイコンを買ったんだけど、3.3V動作なのでどうもホコリセンサーの信号がうまく拾えず断念した。電圧変換とか色々試したけど今のところうまくいかず。

ただ、最終的には空気の質が落ちたときにアラートを出したりと色々機能を持たせたいのでArduinoはセンシングに専念してラズパイ上でPythonで色々処理できる今回の構成が、ある意味正解かもしれない。

ちなみに技適のことをよく分かってなかったんだけど、そもそも私が買ったESP8266は日本国内で使っちゃダメみたいで、数時間だけ検証したものの廃棄予定である。Amazon.co.jpで売っちゃまずいだろこれ。。

別途技適マーク取得済のESP-WROOM-02というモジュールを買ってきたのでArduino+無線で何かやりたいときはそっちを使うことにする。

参考サイト

概ね前回と同じであるが、今回はラズパイ側でダストセンサーの値をpcs/0.01cfからug/m^3へ変更するのに以下のサイトの関数を使用させていただいた。
algorithm.joho.info

以下は前回と同じ。
algorithm.joho.info
lastminuteengineers.com

以上

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