ESP32へのスイッチ入力をMicroPythonの割り込みハンドラで検出する

ESP32マイコンのIOポートにプッシュスイッチを取り付け、スイッチのOn/OffをMicroPythonの割り込みハンドラを使って検出するプログラムを幾つか試したので、記事にまとめる。

スポンサーリンク

ESP32マイコンとプッシュスイッチの接続

IO23をプッシュスイッチ入力に割り当て確認用のLED出力をIO22に割り当てた。プッシュスイッチ入力は10kΩでプルアップ、LEDとIO22との間には1kΩを挟んでいる。プッシュスイッチでLEDをOn/OffするMicroPythonプログラムを作るのが目標。

図

割り込みハンドラ関数で割り込みを検出する

Pinオブジェクトのirqメソッドで割り込みハンドラ関数を指定すれば、IOピンの入力変化で割り込みを発生させる事が出来る。ピン入力の立ち上がり/立ち下がりのどちらをトリガーとするかをirqメソッドの引数で指定出来るのだが、上の図の回路ではプッシュスイッチを押すとIO23がHighからLowへ変化するため、trigger=Pin.IRQ_FALLINGによって立ち下がりをトリガーに指定する。

from machine import Pin

def swhandler(pin):
    print('Pushed the switch')

if __name__ == '__main__':
    sw = Pin(23, Pin.IN)
    sw.irq(handler=swhandler, trigger=Pin.IRQ_FALLING)

このプログラムをampy runで実行後シリアルコンソールを接続すると、スイッチを押すたびに’Pushed the switch’の文字列が表示される。(スイッチのチャタリングのせいで割り込みが沢山掛かってしまう場合があるが、ここでは気にしないことにする)

ラムダ式を使ってLEDに割り当てたPinオブジェクトを割り込みハンドラに渡す

スイッチ操作でLEDを点滅させるためには、割り込みハンドラからLEDを接続したPinオブジェクトを参照出来る必要がある。しかし割り込みハンドラ関数には割り込みの原因を作ったPinオブジェクト、つまりスイッチに割り当てたPinオブジェクトしか渡されない。そこで、割り込みハンドラの中から他のPinオブジェクトを参照するためにラムダ式(無名関数)を使う。ラムダ式の詳しい解説はラムダ式のオンラインドキュメントを参照のこと。

下のプログラムをampy runで実行すると、スイッチを押すたびに文字列のシリアルコンソール出力されると同時にLEDが点滅する。

from machine import Pin

def swhandler_lambda(pin_led):
    print('Pushed the switch')
    if pin_led.value():
        pin_led.off()
    else:
        pin_led.on()

if __name__ == '__main__':
    sw = Pin(23, Pin.IN)
    led = Pin(22, Pin.OUT)
    sw.irq(handler=lambda t:swhandler_lambda(led), trigger=Pin.IRQ_FALLING)

このプログラムではラムダ式の仮引数tに渡されるPinオブジェクトは使わず、ラムダ式の本体で’swhandler_lambda(led)’を実行させることで変数led(=LED出力のPinオブジェクト)をswhandler_lambda関数に渡している。

オブジェクトのメソッドを割り込みハンドラに指定する

割り込みハンドラにはオブジェクトのメソッドを指定することも出来る。IrqTestクラスを定義することにより、上のプログラムと機能は全く同じで割り込みハンドラにオブジェクトのメソッドを使うように書き直すと以下のようになる。

from machine import Pin

class IrqTest(object):
    def __init__(self, pinid_sw, pinid_led):
        self.pin_sw = Pin(pinid_sw, Pin.IN);
        self.pin_led = Pin(pinid_led, Pin.OUT);
        self.pin_sw.irq(handler=lambda t:self.swhandler(), trigger=Pin.IRQ_FALLING)

    def swhandler(self):
        print('Pushed the switch')
        if self.pin_led.value():
            self.pin_led.off()
        else:
            self.pin_led.on()

if __name__ == '__main__':
    irt = IrqTest(23, 22)

この場合は割り込みハンドラからクラス内で定義された全ての変数を参照出来るし、Pinオブジェクトの生成や割り込みハンドラの設定など初期化を全てIrqTestのコンストラクタ(initメソッド)に書くことが出来るので、プログラムがスッキリする。

割り込みハンドラではESP32のスリープ解除が出来ない

上記方法で設定した割り込みハンドラはMicroPythonが動作中の時み有効であり、lightsleep()関数deepsleep()関数でスリープさせると効かなくなってしまう。スイッチ入力でMicroPythonの動作を再開させたい場合は下記の記事の方法に従う必要がある。

まとめ

スイッチ入力で割り込みをかけてLEDを制御するMicroPythonプログラムを試してみた。
この手のプログラムはC言語などだとグローバル変数を使ってプログラムが読み辛くなりがち。
けれどPythonであればラムダ式を使うことで割り込みハンドラに直接オブジェクトを渡すことが出来、更にクラスの仕組みを組み合わせることでプログラムをコンパクトでスッキリ書くことが出来る。

但し、スリープ中はMicroPython自体が停止してしまいスイッチ入力に反応しなくなるので、別記事の方法を参照のこと。

コメント