Raspberry PiによるSOS発報システム その2 ～GPIOを利用したスイッチの状態検知

#もくじ
[その1 ～システム概要](https://qiita.com/maitech/items/a1497892c80826c12330)
その2 ～GPIOを利用したスイッチの状態検知 ←本記事です
[その3 ～アルゴリズム設計](https://qiita.com/maitech/items/f44eb0652fdb268eb9d0)
[その4 ～ハードウェアの準備](https://qiita.com/maitech/items/58a1dc2aab50afe9c5ab)
[その5 ～自動メール送信でハマる](https://qiita.com/maitech/items/9cda9861fd59453fd66e)
[その6 ～キャリアグレードNAT下の端末に外部からSSHでアクセスする](https://qiita.com/maitech/items/b51abd1d78588acf3607)
[その7 ～総括](https://qiita.com/maitech/items/4864a1ddf991f48d728d)

#はじめに
<A HREF="https://qiita.com/maitech/items/a1497892c80826c12330">前回</A>は今回開発したシステムの概要について述べましたが，今回はそのシステムを開発する上で最も大きな課題と考えていたスイッチの状態検知方法について書いてみようと思います。

#GPIO入力の基本
Raspberry Piには，GPIO（General Purpose Input/Output）と呼ばれる外部入出力端子が標準で実装されています。
私がRaspberry Piに感銘を受けたのはまさにこの点で，単なる小さな省電力コンピュータにとどまらない大きな可能性を秘めています。
そして今回述べるスイッチの状態検知にもこのGPIOを使おうと考えたのでした。

##GPIO端子の性質
さて，Input/Outputというからには入力と出力があるわけですが，入力と出力とで物理的な端子が分かれているわけではなく，同じ端子でも設定によって入力にできたり出力にできたりします。それぞれの機能を簡単に書くと以下のようになります。

- 入力：端子にかかっている電圧がハイ（H：1.3～3.3V）かロー（L：0～0.8V）かを検出する（デジタル入力）<A HREF="https://signal-flag-z.blogspot.com/2018/01/raspberry-pi-hardware-data.html">[1]</A>
- 出力：端子の電圧をハイ（H：3.3V）かロー（L：0V）に固定できる（デジタル出力）

入力モードで使用する際は，入力電圧が0～3.3Vの範囲を超えないようにすることと，検出結果が不定となる0.8V～1.3Vの電圧を避けることがポイントとなります。
出力モードで使用する際は端子の電圧が固定されますので，電位の違う部分と短絡した場合には大電流が流れ，最悪の場合Raspberry Piが壊れてしまいますので注意が必要です。

##スイッチの状態検知
今回はGPIOをドアスイッチやSOSボタンなどのスイッチの状態検知に使いたいので，入力モードで使用します。
GPIO端子に接続するスイッチ回路は，ドアの開閉，あるいはスイッチのON-OFFでGPIO端子にかかる電圧がHかLになる仕掛けにすればよいわけです。
###基本に則った構成例
この基本的な考えに基づけば以下の図のような回路を作ればよいことになります。
<img src="https://i.imgur.com/wHIwCqz.png" width=60%>
スイッチを実線の位置にするとGPIO端子の電圧は0V(L)，点線の位置にすると3.3V(H)となり，スイッチの状態検知ができることになります。
しかしこの例の場合は

- Raspberry Piとスイッチの間の配線が3本必要
- 単なるON-OFFスイッチではなく，切り替えスイッチが必要

という点で，改善の余地がありそうです。
直感的には配線は2本で何とかなりそうな気がしませんか？

###配線を2本にした例 その1（ダメな例）
ではスイッチへの配線を2本にした下の例はどうでしょう？
<img src="https://i.imgur.com/Vgb4oVN.png" width=60%>
(A)と(B)はスイッチの位置の違いですが，(A)ではGPIO端子に3.3Vがかけられ，(B)ではGPIO端子には何の電圧もかかりません。
実はこれが曲者で，(B)ではGPIO端子の電圧が不定となってしまい，Hとなる場合もLとなる場合もありうるという結果になります。
「GPIO端子に何も接続しない≠0V(L)」というわけです。
これではスイッチの状態検知はできません。

###配線を2本にした例 その2（うまくいく例）
そこで，GPIO端子が開放されたときの電圧を固定するため，GPIO端子を抵抗を介して3.3Vあるいは0Vと接続しておくということをします。
下の図は，GPIO端子と3.3Vを50kΩの抵抗で接続しています。
<img src="https://i.imgur.com/WbvjDNc.png" width=60%>
この場合，スイッチを閉じた(A)ではGPIO端子の電圧は0V(L)，スイッチを開放した(B)では3.3V(H)となり，基本に則った例と同様にスイッチの状態検知ができることになります。
このように，入力端子と電源端子やGND端子を抵抗で接続して入力端子開放時の電圧を固定することをプルアップやプルダウンと言い，それに用いられる抵抗をプルアップ抵抗，プルダウン抵抗と呼びます。今回の例ですと「50kΩのプルアップ抵抗でプルアップした」ということになります。

ちなみに，(A)では，3.3V端子から抵抗とスイッチを経由して0V端子に向けて電流が流れることになりますが，抵抗値は50kΩと大きいので，流れる電流は
I=E/R=3.3/50000=0.000066A=0.066mA
となり，これはごく微小なのでRaspberry Piが壊れることはありません。
尚，今回のようにGPIO端子を入力端子として使った場合には，電流は流入することも流出することもありませんので，(B)の場合もRaspberry Piが過電流で壊れることはありません。

尚，Raspberry Pi ZeroのGPIOの場合，GPIO3，GPIO5以外のGPIO端子については約46kΩ<A HREF="https://s51517765.hatenadiary.jp/entry/2018/02/03/100530">[2]</A>のプルアップ/プルダウン抵抗が内蔵されており，設定により有効/無効を選ぶことができます。

以上でスイッチの状態検知の仕組みは一応の完成となりますが，GPIO端子は入力にも出力にも設定できるという性質から，実際にはもう一工夫加えた方が安全です。

###保護抵抗の追加
下の図(A)は，前節で説明したプルアップした入力回路と同じものですが，GPIOを誤って3.3Vの出力に設定してしまったものです。
この場合，回路は同じでも，GPIO端子から0V端子に向けて大電流が流れてしまい，最悪の場合Raspberry Piを壊してしまいます。
<img src="https://i.imgur.com/fDpPRwW.png" width=60%>
(B)の場合は，GPIOとスイッチの間に5kΩの抵抗を挿入していますが，この場合はGPIOを誤って3.3Vの出力に設定してしまっても，電流は0.66mAしか流れず，Raspberry Piが壊れることはありません。ここでは，この抵抗のことを保護抵抗と呼ぶことにします。

固定されたシステムであればこのような異常状態が起こる可能性は小さいと考えられますが，様々なプログラムを試しているうちはうっかりということもありますので，保護抵抗はつけておいた方が良いと考えます。

###保護抵抗の抵抗値の影響
さて，この保護抵抗の抵抗値は電流さえ十分に制限できればどのような値でも良いのでしょうか？
下の図(A)は保護抵抗を5kΩ，(B)は500kΩにしたものです。
<img src="https://i.imgur.com/LqS6RRf.png" width=60%>
(A)も(B)も，スイッチを閉じた状態では3.3Vが2つの抵抗で分圧されることになりますが，その分圧のされ方が異なり，GPIO端子での電圧は(A)は0.3V(L)，(B)は3V(H)となってしまい，スイッチの状態が同じにもかかわらず入力レベルが変わってしまっています。
この例ではスイッチを閉じているので当然Lになることが期待されるわけですが，プルアップ抵抗がある場合には，保護抵抗の抵抗値によっては所望の動作が得られない場合があるのでその値には注意が必要です。

###本開発で採用した仕様
本開発では，内部プルアップ抵抗（約46kΩ）によるプルアップと，10kΩの保護抵抗を利用して以下のようなスイッチ回路とすることにしました。
<img src="https://i.imgur.com/ys8RQs0.png" width=60%>
保護抵抗をもう少し抵抗値の小さいものにした方がより「Lらしい」電圧にできますが，今回は手元にたくさんあったという理由で10kΩとしています。

#N.C. か N.O. か
さて，論理的には開閉，あるいはON-OFFに対応する入力レベルがそれぞれ1と0，あるいはそれぞれ0と1でも区別さえできれば問題ありません。
しかし，SOSスイッチは緊急時にのみ使われるものなので，いざというときに「実は配線が断線していて作動しなかった」という事態は極力避けなければいけません。
この考えから，本開発では以下の図のように平常時(A)には回路が閉じていて，非常時(B)に回路が開放されるような構成を採用することにしました。
<img src="https://i.imgur.com/S87j7IA.png" width=90%>
こうすることで，断線が発生した際(C)には非常時と同じ状況になり，(誤報という形になってしまいますが)断線に気づくことができます。

章題の「N.C. か N.O. か」というのは，Normally Closed（常時閉）かNormally Open（常時開）かということを言っており，結論としては「N.C. で行く」ということになります。

#実際に使用するスイッチの選択
というわけで，本開発では作動前は閉じていて，作動後は開くようなスイッチを選択することにしました。
幸いにもドアスイッチもSOSボタンも適当なものが見つかりましたのでこの点については特段問題はありませんでした。

- [ドアスイッチの例](https://www.monotaro.com/p/0296/4509/)
- [SOSボタンの例](https://www.monotaro.com/p/3765/5904/)

ちなみに，このような接点構成のスイッチを探す際には，「N.C.」，「NC」，「b接点」というキーワードを使うと見つかりやすいです。
逆に，作動時に閉じるスイッチの場合には「N.O.」，「NO」，「a接点」というキーワードが適当と思います。
尚，SOSボタンについては，ボタンが押された後，リセット操作をしない限り継続して作動し続けるタイプのものとしています。

#次回予告
今回の開発における最も大きな課題と考えていたスイッチの状態検知ができる見込みが得られたので，<A HREF=https://qiita.com/maitech/items/f44eb0652fdb268eb9d0>次回</A>はソフトウェアの内容・構成について書いてみようと思います。

#参考にさせていただいた情報
[1][Signal Flag "Z"/Raspberry Piにハードウエアをつなぐための資料を集めてみる](https://signal-flag-z.blogspot.com/2018/01/raspberry-pi-hardware-data.html)
[2][プログラミング素人のはてなブログ/ラズパイゼロのプルアップ抵抗を調べる](https://s51517765.hatenadiary.jp/entry/2018/02/03/100530)