M5StickC PLUSの内蔵ADコンバータで電圧測定
この記事では、M5StickC PLUSの内蔵ADコンバータを使って、電圧測定する方法を解説します。 M5StickC PLUSは、Arduino互換機であるESP32 PICOをベースに、バッテリーやボタンをパッケージしたM5Stackシリーズ製品のひとつです。本記事の内容はESP32の資料を参考にしました。また、M5StickC PLUSの使い方は M5StickC PLUSでArduinoをはじめよう! をご覧ください。
▼ 半導体不足のため入手困難な場合は、ひとつ前のモデルのM5StickCも選択に入れてみてください。
はじめに
M5StickC PLUSでアナログ入力をサクッと終えるつもりでしたが、ESP32の意外な一面を知って時間がかかってしまいました。記事の中で説明しますが、M5StickC PLUSの内蔵ADコンバータを使うときは、あまり精度は期待しないほうが良さそうです。精度が必要なときは、素直にADコンバータのモジュールを使用することをおすすめします。
▼ Raspberry Piの記事になりますが、外付けADコンバータの使い方はこれらの記事をご覧ください。
G36/G25を使ってアナログ電圧を読み取る
それではStickC PLUSのG36/G25を使って、アナログ電圧を読み取ってみましょう。ただし、G36/G25を使うときには注意しなければならないことがあります。
G36/G25を使うときの注意事項
G36/G25は同じポートを利用してます。そのため、片方のピンを利用しているとき、もう片方のピンはフローティング入力(プルアップもプルダウンもしない状態)にする必要があります。
setup()
{
M5.begin();
pinMode(36, INPUT);
gpio_pulldown_dis(GPIO_NUM_25); // Disable pull-down on GPIO.
gpio_pullup_dis(GPIO_NUM_25); // Disable pull-up on GPIO.
}
▲このような感じで、GPIO36番をADコンバータの入力として使う場合、GPIO25番をフローティング状態に設定します。
また、M5StickC PLUSのGPIOのアナログ入力電圧は3.3Vまでなので注意してください。
配線
入力電圧は、ふたつの1kΩの抵抗で3.3Vを分圧し、真ん中の端子をG36/G25へ入力しました。なので、理想でいけば1.65Vが入力されるはずです。
プログラム
次のようなプログラムを書いて、ADコンバータによるアナログ電圧を測定してみました。
#include <M5StickCPlus.h>
const float Vref = 3.3;
const int ADC_PIN = GPIO_NUM_36;
void setup() {
M5.begin();
M5.Axp.ScreenBreath(9);
M5.Lcd.setRotation(3);
M5.Lcd.fillScreen(WHITE);
M5.Lcd.setTextColor(BLACK);
M5.Lcd.setTextSize(6);
pinMode(ADC_PIN, INPUT);
gpio_pulldown_dis(GPIO_NUM_25); // Disable pull-down on GPIO.
gpio_pullup_dis(GPIO_NUM_25); // Disable pull-up on GPIO.
Serial.begin(115200);
}
void loop() {
M5.Lcd.fillScreen(WHITE);
int val = analogRead(ADC_PIN); // read the input pin
float volt = Vref * float(val) / 4095.0; // 12bit
Serial.println(volt);
M5.Lcd.setCursor(20, 40);
M5.Lcd.printf("%.2fV", volt);
delay(500);
}
写真は1kΩの抵抗で分圧したときの値です。なぜか理論値の1.65Vよりだいぶズレてしまいました。 抵抗の値をもっと高くして10kΩで分圧してみました。すると、さらに電圧値がズレた結果になってしまいました。さらに100kΩでは、0Vが表示され測定不可能になってしまいました。 ADコンバータの入力インピーダンスは高いものとばかり思っていましたが、随分と低いみたいです。つまり、アナログ入力にはある程度の電流を流す必要があるのかなと。 逆に抵抗をもっと小さくして、220Ωでやってみました。しかし結果はそれほど変わらず。アナログ入力のピンを変えてG26でも試してみました。少しはマシになったものの理論値とはかなりズレてます。謎です。
その後いろいろ調べてみるとESP32の内蔵ADコンバータは精度が悪いようで、皆さん同じ悩みを抱えているみたいです。 ESP-32でアナログ入力を使うときの注意点-Qiita
▼こちらのスイッチサインセンスさんの記事の「ADC(アナログ入力)」の項目が非常に参考になりました。 esp32_tips–スイッチサイエンス
ESP32には逐次比較型(SAR)ADCモジュールが使われているようです。分解能は9~12bitで、デフォルトは12bit。また、ADCの入力には減衰器を設定可能とのこと。
ここまでは良かったのですが、次の一文を読んで驚きました。
えっ!3.6V!?
これではどうやって測定しても理論通りの電圧にならない訳です。
実はESP32のADCは、もともと0〜1Vしか測れないようです。そこで減衰器を設定して、もっと幅広い電圧を測定できるようになりました。デフォルトでは、-11dBに減衰(-11dBは約1/3.6倍)させられているため、0〜3.6Vの値が測定されるというわけです。
▼ 精度に関する問題はこちらのスレッドで議論されてました。 [Answered]What are the ADC input ranges?-ESP32 Forum
この中から、減衰量による測定結果の違いに関するグラフがありましたので、ちょっとお借りします。 ご覧の通り、-11dBでは比例関係になっていません。だいぶ精度は悪そうです。 一方で、0dBや、-6dBでは測定できる最大電圧は低くなるものの、キレイな比例になっていてデフォルトよりは精度が良さそうです。 たとえば、-6dBで設定した場合は、先ほどのプログラムを次のように修正すれば良いでしょう(計算あってますかね?)。
void setup() {
...
analogSetAttenuation(ADC_6db); // ←追記
...
}
void loop() {
...
float volt = Vref * float(val) / log(5) / 4095.0; // ←修正
...
}
ただし、これ以上精度を突き詰めてもむだ骨を折るだけですので、今回はこの辺で終わります。