サイトロゴ

M5StickC PLUSの内蔵ADコンバータで電圧測定

著者画像
Toshihiko Arai

この記事では、M5StickC PLUSの内蔵ADコンバータを使って、電圧測定する方法を解説します。 M5StickC PLUSは、Arduino互換機であるESP32 PICOをベースに、バッテリーやボタンをパッケージしたM5Stackシリーズ製品のひとつです。本記事の内容はESP32の資料を参考にしました。また、M5StickC PLUSの使い方は をご覧ください。

はじめに

M5StickC PLUSでアナログ入力をサクッと終えるつもりでしたが、ESP32の意外な一面を知って時間がかかってしまいました。記事の中で説明しますが、M5StickC PLUSの内蔵ADコンバータを使うときは、あまり精度は期待しないほうが良さそうです。精度が必要なときは、素直に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番をフローティング状態に設定します。

https://docs.m5stack.com/en/core/m5stickc_plus

また、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コンバータは精度が悪いようで、皆さん同じ悩みを抱えているみたいです。 https://qiita.com/klme_u6/items/e29e4b57fd149488d1c3

▼こちらのスイッチサインセンスさんの記事の「ADC(アナログ入力)」の項目が非常に参考になりました。 https://trac.switch-science.com/wiki/esp32_tips

ESP32には逐次比較型(SAR)ADCモジュールが使われているようです。分解能は9~12bitで、デフォルトは12bit。また、ADCの入力には減衰器を設定可能とのこと。

ここまでは良かったのですが、次の一文を読んで驚きました。

**デフォルトでは11dBの減衰が設定され、12bitでの__最大値は3.6V__を示してます。**

えっ!3.6V!?

これではどうやって測定しても理論通りの電圧にならない訳です。

実はESP32のADCは、もともと0〜1Vしか測れないようです。そこで減衰器を設定して、もっと幅広い電圧を測定できるようになりました。デフォルトでは、-11dBに減衰(-11dBは約1/3.6倍)させられているため、0〜3.6Vの値が測定されるというわけです。

▼ 精度に関する問題はこちらのスレッドで議論されてました。 https://esp32.com/viewtopic.php?f=12&t=1045

この中から、減衰量による測定結果の違いに関するグラフがありましたので、ちょっとお借りします。 ご覧の通り、-11dBでは比例関係になっていません。だいぶ精度は悪そうです。 一方で、0dBや、-6dBでは測定できる最大電圧は低くなるものの、キレイな比例になっていてデフォルトよりは精度が良さそうです。 たとえば、-6dBで設定した場合は、先ほどのプログラムを次のように修正すれば良いでしょう(計算あってますかね?)。

void setup() {
...
    analogSetAttenuation(ADC_6db); // ←追記
...
}
void loop() {
...
    float volt = Vref * float(val) / log(5) / 4095.0; // ←修正
...
}

ただし、これ以上精度を突き詰めてもむだ骨を折るだけですので、今回はこの辺で終わります。

関連記事