ESP32 + MicroPythonで、PWMオブジェクトを生成した直後にdutyをセットすると、なぜか反映されずにdutyが100% (常時HIGH) になってしまうという問題に遭遇し、ちょっと困った。再現性は不明。毎回ではなく何らかの条件が揃ったときに発生し、同じコードでも発生したりしなかったりする。原因はよくわからないが回避策がわかったので共有する。
なお、今回したMicroPythonのバージョンは1.19.1である。
ESP32+MicroPythonでPWM
まずは基本の話。MicroPythonでPWMを使用するには、machine.PWMクラスを使う。ESP32では、出力に使えるピンならどれでもPWMができる。(GPIO34〜GPIO39は入力専用なので使えないよ! 気をつけてね!) 詳しくはここを読むと良い。
machine.Pinとmachine.PWMを使う。
from machine import Pin, PWM
好きなピンを選んでこんなふうに初期化したら
pin = Pin(25, Pin.OUT)
pin.value(0)
pwm = PWM(pin)
dutyを0 (常時OFF)〜1023 (常時ON)の間の整数でセットする。
pwm.duty(512) # 50%
周波数はこんなふうにセットできる。
pwm12.freq(1000)
コンストラクタでdutyとfreqをセットすることもできる。
PWM(12, freq=500, duty=512)
なかなかシンプルな話のように見えるのだが……。
dutyが反映されず困る
PWMオブジェクトを初期化した直後にdutyをセットすると、何らかの条件下では反映されないという現象に遭遇し、大変つらい思いをした。
最初duty=0%でスタートし、のちのち必要に応じて出力を変えていくつもりだったのだが……
pin = Pin(25, Pin.OUT)
pwm = PWM(pin)
pwm.duty(0)
なぜかpwm.duty(0)
が実行された直後、実際の出力ではdutyが100%になる。引数が0でなくても発生する。発生しないこともある。どういうケースで発生するのかがいまいちわからないし、同一のコードを実行しても発生したりしなかったりする。特に、os.reset()
を呼び出して再起動した直後だとほぼ確実にこの現象が発生する。なんだこりゃ。
回避策のようなもの
色々試したら、恐らくこれで大丈夫といえる回避策にたどり着いた。
「PWMオブジェクト生成時に確実にdutyをセットしたかったら、コンストラクタを呼んだ直後にduty()を呼ぶのはやめる。コンストラクタにdutyを渡す」
machine.PWMのコンストラクタは、オプション引数としてfreqとdutyを渡すことが出来る。ここで渡したdutyの値は決して無視されることはない。さっきのコードをこんなふうに書き換える。
pin = Pin(25, Pin.OUT)
pwm = PWM(pin, duty=0)
これで、私が試した限り、さきほどの問題は発生しなくなった。