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も選択に入れてみてください。

はじめに

M5StickC PLUSでアナログ入力をサクッと終えるつもりでしたが、ESP32の意外な一面を知って時間がかかってしまいました。記事の中で説明しますが、M5StickC PLUSの内蔵ADコンバータを使うときは、あまり精度は期待しないほうが良さそうです。精度が必要なときは、素直にADコンバータのモジュールを使用することをおすすめします。

▼ Raspberry Piの記事になりますが、外付けADコンバータの使い方はこれらの記事をご覧ください。

G36/G25を使ってアナログ電圧を読み取る

それではStickC PLUSのG36/G25を使って、アナログ電圧を読み取ってみましょう。ただし、G36/G25を使うときには注意しなければならないことがあります。

G36/G25を使うときの注意事項

G36/G25は同じポートを利用してます。そのため、片方のピンを利用しているとき、もう片方のピンはフローティング入力(プルアップもプルダウンもしない状態)にする必要があります。

cpp
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番をフローティング状態に設定します。

m5-docs

また、M5StickC PLUSのGPIOのアナログ入力電圧は3.3Vまでなので注意してください。

配線

配線図
配線図
入力電圧は、ふたつの1kΩの抵抗で3.3Vを分圧し、真ん中の端子をG36/G25へ入力しました。なので、理想でいけば1.65Vが入力されるはずです。

プログラム

次のようなプログラムを書いて、ADコンバータによるアナログ電圧を測定してみました。

cpp
#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コンバータは精度が悪いようで、皆さん同じ悩みを抱えているみたいです。 ESP-32でアナログ入力を使うときの注意点-Qiita

▼こちらのスイッチサインセンスさんの記事の「ADC(アナログ入力)」の項目が非常に参考になりました。 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の値が測定されるというわけです。

▼ 精度に関する問題はこちらのスレッドで議論されてました。 [Answered]What are the ADC input ranges?-ESP32 Forum

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

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

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

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

関連記事

最後までご覧いただきありがとうございます!

▼ 記事に関するご質問やお仕事のご相談は以下よりお願いいたします。
お問い合わせフォーム

人気のArduino互換機
Arduinoで人気の周辺パーツ
あると便利な道具
Arduinoのオススメ参考書

▼ Arduino初心者向きの内容です。ほかのArduino書籍と比べて図や説明がとてもていねいで読みやすいです。Arduinoで一通りのセンサーが扱えるようになります。

▼ 外国人が書いた本を翻訳したものです。この手の書籍は、目からうろこな発見をすることが多いです。

▼ Arduinoの入門書を既に読んでいる方で、次のステップを目指したい人向きの本です。C言語のプログラミングの内容が中心です。ESP32だけでなく、ふつうのArduinoにも役立つ内容でした。

関連記事