Raspberry Pi でボタンによる LED の点灯制御ができるライブラリの検証です。
今回は以下のライブラリを検証しました。
HPi は bcm2835 のラッパー、wiringPi は Wiring Pi のラッパーです。その他に Hackage には system-gpio という仮想ファイルを読み書きするライブラリもありますが、これは試すまでもないので除外しました。
結論から言うと、HPi も wiringPi も、どちらも GPIO の割り込みには対応しておらず、ボタンの状態をポーリングしなければなりません。wiringPi はオリジナルの C ライブラリには wiringPiISR という割り込みコールバック関数があるので期待していたのですが、Haskell のライブラリには実装されていませんでした。
なので今回のサンプルはどちらもポーリングです。ちなみに Node.js だとポーリングではなくコールバックで書けます。このエントリの最後におまけとしてサンプルを提示しておきます。
回路
今回は LED ひとつとボタンひとつの回路です。ボタンはタクトスイッチではなくマイクロスイッチを使用しているので論理が逆になっています。
回路図は上記のとおりです。
ブレッドボードに配線した様子が以下です。
HPi のサンプル
HPi は物理ピンでコーディングするスタイルです。
import Control.Concurrent
import Control.Monad
import System.RaspberryPi.GPIO
led = Pin08
button = Pin07
main = withGPIO $ do
setPinFunction led Output
setPinFunction button Input
forever $ do
threadDelay 10000
state <- readPin button
writePin led $ not state
withGPIO 関数はおそらく setPinFunction の後始末をしてくれるのだと思いますが、今回のサンプルは SIGINT で強制終了するので有効に機能しないと思います。
wiringPi のサンプル
wiringPi は GPIO でコーディングします。私はこちらの方が好みです。
wiringPi は暗黙的に wiringPiSetupGpio が呼び出されるようで、明示的に呼び出す必要がありません。C の Wiring Pi 同様、実行にはルート権限が必要です。なお、ルート権限が不要な wiringPiSetupSys 相当の関数は提供されていません。
import Control.Concurrent
import Control.Monad
import System.Hardware.WiringPi
led = Gpio 14
button = Gpio 4
main = do
pinMode led OUTPUT
pinMode button INPUT
forever $ do
threadDelay 10000
state <- digitalRead button
digitalWrite led $ if state == HIGH then LOW else HIGH
内容はほとんど HPi と同じです。digitalRead と digitalWrite は独自型を読み書きするので、Bool で読み書きする HPi に比べて面倒です。
まとめ
- ボタンぐらいならポーリングでも良いが、PWM 等には使えない
- 消費電力が増えるので、バッテリー駆動の機器には向かない
- ポーリングはダサい
Node.js のサンプル(おまけ)
npm で gpio を検索すると 400 以上ヒットします。上から順に簡単そうなライブラリを探して見つけたのが onoff です。おそらく内部的には仮想ファイルの読み書きで実現しているため、I2C や SPI は扱えませんが、扱う範囲が GPIO だけなら良い選択肢だと思います。
const Gpio = require('onoff').Gpio;
const led = new Gpio(14, 'out');
const button = new Gpio(4, 'in', 'both');
button.watch((err, value) => {
if (err) {
throw err;
}
led.writeSync(value ? 0 : 1);
});
process.on('SIGINT', () => {
led.unexport();
button.unexport();
process.exit();
});
Node.js は process.on() で割り込みが検知できるので、SIGINT が来たら後始末して終了するようにしています。