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

M5StickC PLUSの内蔵ADコンバータ
M5StickC PLUSの内蔵ADコンバータ

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

はじめに

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

また、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Ωの分圧による測定結果
1kΩの分圧による測定結果

写真は1kΩの抵抗で分圧したときの値です。なぜか理論値の1.65Vよりだいぶズレてしまいました。

抵抗の値をもっと高くして10kΩで分圧してみました。すると、さらに電圧値がズレた結果になってしまいました。さらに100kΩでは、0Vが表示され測定不可能になってしまいました。

ADコンバータの入力インピーダンスは高いものとばかり思っていましたが、随分と低いみたいです。つまり、アナログ入力にはある程度の電流を流す必要があるのかなと。

逆に抵抗をもっと小さくして、220Ωでやってみました。しかし結果はそれほど変わらず。アナログ入力のピンを変えてG26でも試してみました。少しはマシになったものの理論値とはかなりズレています。謎です。

その後いろいろ調べてみると、ESP32の内蔵ADコンバータは精度が悪いようで、皆さん悩まされているようですね。

▼こちらのスイッチサインセンスさんの記事の「ADC(アナログ入力)」の項目が非常に参考になりました。

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の値が測定されるというわけです。

▼精度に関する問題はこちらのスレッドで議論されているようです。

この中から、減衰量による測定結果の違いに関するグラフがありましたので、ちょっとお借りします。

減衰量による測定結果の違い
減衰量による測定結果の違い

ご覧の通り、-11dBでは比例関係になっていません。だいぶ精度は悪そうです。

一方で、0dBや、-6dBでは測定できる最大電圧は低くなるものの、キレイな比例になっていてデフォルトよりは精度が良さそうです。

たとえば、-6dBで設定した場合は、先ほどのプログラムを次のように修正すれば良いでしょう(計算あってますかね?)。

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

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

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
M5Stackのオススメ参考書
M5Stack製品
M5StickCで使えるHat
関連記事