ボタンスイッチからの入力をESP32で処理する場合、プルアップ、プルダウンという言葉が出てきますが理解が怪しかったので、実機で試しつつまとめてみました。
ESP32の開発ボードは、ESPr Developer 32で試しています。
スイッチにプルアップ、プルダウンがなぜ必要か?
スイッチの回路にプルアップ、プルダウンがなぜ必要かについてですが、もし以下のようにスイッチがGNDと入力用のピンのみに接続されている回路で内部抵抗によるプルダウンなしの場合
このとき読み取られる値は直感的には0Vになりそうですが、そうはなりません。
実際にどこにも接続されていないピンをanalogReadした値は以下のようになぞの電圧が観測されます。
Wikipedia「プルアップ抵抗」によると、これを「電位不定」な状態と言い、回路中には存在しないように気をつける必要があるようです。
以前にコピペテックの記事で「micro:botでもぐら叩きゲームを作ってみよう」の工作をしていたときに、「なんか期待した通りに動かないなあ」と悩んでしまったのですが、プルダウンしたことで安定して動作するようになりました。初心者あるあるなのかもしれません。
内部プルアップ、プルダウンとは
内部プルアップ抵抗は、Arduinoと同様にESP32にも用意されています。
プルアップ抵抗が内部にあれば、物理的なプルアップ用の抵抗を回路から省力できるため非常に便利です。
内部プルアップの使用は、pinMode関数で指定できます。
pinMode(2, INPUT_PULLUP);
ESP32では、GPIO34-39は上記のソフトウェアプルアップは使えないので注意が必要です。
参考: GPIO & RTC GPIO – ESP32 – — ESP-IDF Programming Guide latest documentation
ESP32はプルダウン抵抗も使用できますが、ArduinoのAPIではプルダウンは指定できないため、ESP32のgpio_set_pull_mode関数を使用します。
参考: ESP32 入力ピンのPULL UP PULL DOWNテスト
抵抗値はなぜ10kΩなのか?
プルアップ、プルダウンに使用する抵抗値は10kΩを使用することが一般的ですが、なぜだろう?とおもって調べてみたところ、以下のマクニカのコラムが正解ぽいです。しかし、私の技量では理解不能でしたw
参考:プルアップとプルダウン抵抗の値ってどうやって求めるの? – 半導体事業 – マクニカ
この記事にあるように10kΩが独り歩きして、なんとなく10kΩが通例となった歴史があるようですし、そういうもんだと理解しておくことにします。。
実機ESPr Developer 32で検証
さて、以上を踏まえてESPr Developer 32を使って順に動きを確認してみます。
プルアップ 外部抵抗
プルダウンは10kΩ抵抗を3v3に接続します。
実際に配線したところ
スケッチ例です。ボタンからの読み取った値がLOWのときにLEDを点灯させます。
const int buttonPin = 14; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin
int buttonState = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT);
}
void loop() {
buttonState = digitalRead(buttonPin);
// LOWのときにLEDを点灯
if (buttonState == LOW) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
スケッチは、スケッチ例のButtonを修正したものです。
ボタンのピンは14を使用していますが、ESP32でプルアップに外部抵抗を使用する場合はピンの選択には注意が必要らしいです。
GPIO 0,2,5,12,15 は 起動モード設定に使用される為、外部での プルアップ、プルダウン時は注意する必要がある。
はじめスケッチ例のままIO02を使用しましたが、HIGHになる電圧まで上がらず「あれ?」となってしまいました。
上記サイトで、注意が必要なピンを一通り試してみたところ、この回路で実際にNGだったのはIO2、IO15でした。あとはIO12はプルアップしておくとプログラムの書き込みに失敗するので、書き込み時は外しておく必要があります。
プルダウン 外部抵抗
プルダウンは抵抗をGNDに接続します。
スケッチは、スケッチ例のButtonそのままです。
const int buttonPin = 14; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin
int buttonState = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT);
}
void loop() {
buttonState = digitalRead(buttonPin);
// HIGHのときにLEDを点灯
if (buttonState == HIGH) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
ちなみにプルダウンとIO02でも正しく動作します。
プルアップ 内部抵抗
次に内部抵抗を使ってプルアップしてみます。
タクトスイッチには、GNDと信号入力IO14のみつなぎます。
部品点数が減ってすっきりしました。
スケッチは、pinMode関数にINPUT_PULLUPを指定するだけです。
const int buttonPin = 14; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin
int buttonState = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT_PULLUP);
}
void loop() {
buttonState = digitalRead(buttonPin);
// LOWのときにLEDを点灯
if (buttonState == LOW) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
注意点としては、IO34から39ピンは内部プルアップ/プルダウンは使えません。
GPIO34-39 can only be set as input mode and do not have software pullup or pulldown functions.
参考: GPIO & RTC GPIO – ESP32 – — ESP-IDF Programming Guide latest documentation
プルダウン 内部抵抗
最後に内部抵抗を使ってプルダウンしてみます。
ArduinoのAPIにはプルダウンが無いので、ESP32のAPI gpio_set_pull_mode関数を使用します。
ピンの番号は、enum型の値gpio_num_tを取ります。
タクトスイッチには、3v3と信号入力IO14のみつなぎます。
スケッチ例です。
const int buttonPin = 14; // the number of the pushbutton pin
const int ledPin = 13; // the number of the LED pin
int buttonState = 0;
void setup() {
pinMode(ledPin, OUTPUT);
pinMode(buttonPin, INPUT);
gpio_set_pull_mode(GPIO_NUM_14, GPIO_PULLDOWN_ONLY);
}
void loop() {
buttonState = digitalRead(buttonPin);
// HIGHのときにLEDを点灯
if (buttonState == HIGH) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
buttonPinとenumのGPIO_NUM_14の変数が重複しているのが気になりますね。
まとめ
以上、ESP32のプルアップ/プルダウンについて大まかに把握できたのではないかと思います。
公式ソースを終えていないところもあるので引き続き調べて行きたいところです。