Project 9.1 ADコンバーター/Read the Voltage of Potentiometer

前回はシリアル通信を使用して、ESP32-S3でデータを受け取る処理を動かしてみました。

あわせて読みたい
Project 8.2 シリアル通信受信編/Serial Read and Write 前回はシリアル通信でESP32-S3からデータを受信してみました。 https://kaneshige.org/?p=314 今回はESP32-S3でデータを受け取る処理を動かしてみましょう。 早速コード...

今回はESP32-S3のADC機能を使って、ポテンショメータの電圧値を読み取り、シリアルモニタに出力します。
マイコンでセンサーから値を読み取る時は、ADコンバーターを使用して電圧値を読み取ることが多いので、ぜひとも習得してもらいたい単元です。

目次

ADC(ADコンバーター)とは

ADCとは、電圧などのアナログ信号をデジタルまたはバイナリ形式(1と0で表現される形式)に変換する電子集積回路です。ESP32-S3に搭載されているADCの分解能は12ビットで、2^12=4096の細かさにアナログ値を分解できることを意味します。この数値は(3.3V電源の場合)0V~3.3Vの電圧範囲が4096等分されることを示しています。これによりアナログ値の幅がADC値と対応するようになります。ADCのビット数が多いほど、アナログ値が細かく分割され、変換結果の精度が高くなります。

この節では、ESP32-S3のADCにおけるアナログ値とデジタル値の対応関係について説明します。

1. アナログ値とデジタル値の範囲

  • アナログ入力範囲:0V ~ 3.3V
  • デジタル出力範囲:0 ~ 4095

2. アナログ値とデジタル値の変換

ESP32-S3のADCは、12ビットの分解能を持つため、アナログ入力範囲を4096個の等間隔なレベルに分割します。各レベルは、0 ~ 4095のデジタル値に対応します。

以下は、アナログ値とデジタル値の変換式です。

ADC 値 = アナログ電圧 / 3.3 * 4095

この式に基づいて、以下の対応関係が成り立ちます。

  • アナログ値 0(0V)はデジタル値 0 に対応します。
  • アナログ値 1~4095(0V + (3.3V / 4096) = 約0.000805V~3.3V)はデジタル値 1 に対応します。

3. 例

例として、アナログ値 1.65V をデジタル値に変換してみましょう。

ADC 値 = 1.65V / 3.3 * 4095 ≈ 2046

よって、アナログ値 1.65V はデジタル値 2046 に対応します。

ESP32-S3のADCについて

ESP32-S3は、12ビット逐次近似型のデジタルアナログコンバータ(ADC)を2基搭載しており、合計20本のピンを使ってアナログ信号を測定することができます。GPIOピン番号とアナログピン定義は、以下の表の通りです。

 Pin number in Arduino GPIO numberADC channel
A0GPIO 1ADC1_CH0
A1GPIO 2ADC1_CH1
A2GPIO 3ADC1_CH2
A3GPIO 4ADC1_CH3
A4GPIO 5ADC1_CH4
A5GPIO 6ADC1_CH5
A6GPIO 7ADC1_CH6
A7GPIO 8ADC1_CH7
A8GPIO 9ADC1_CH8
A9GPIO 10ADC1_CH9
A10GPIO 11ADC2_CH0
A11GPIO 12ADC2_CH1
A12GPIO 13ADC2_CH2
A13GPIO 14ADC2_CH3
A14GPIO 15ADC2_CH4
A15GPIO 16ADC2_CH5
A16GPIO 17ADC2_CH6
A17GPIO 18ADC2_CH7
A18GPIO 19ADC2_CH8
A19GPIO 20ADC2_CH9

ESP32-S3では、コード中のアナログピン番号をGPIO番号と置き換えて使用することができます。例えばGPIO1をA0と記述できます。

ポテンションメーター(可変抵抗器)とは

ポテンショメータは、3つの端子を持つ可変抵抗です。これまでに使用してきたような抵抗は固定値ですが、ポテンショメータは抵抗値を調整することができます。ポテンショメータは、抵抗体(ワイヤやカーボン素子)と可動接触子(ブラシ)で構成されています。 ブラシが抵抗体に沿って移動すると、ポテンショメータの出力端子(3)の抵抗値(もしくは、回路内部の電圧)が変化します。下図は、線形スライドポテンショメータと、その回路図記号を示したものです。

ポテンショメータのピン1とピン2の間が抵抗体です。ピン3は可動接触子 (ブラシ) に接続されています。ブラシがピン1からピン2に向かって移動すると、ピン1とピン3の間の抵抗が直線的に抵抗値の最大値まで増加し、ピン2とピン3の間の抵抗が直線的に0まで減少します。回路において、抵抗体の両端は電源の正極と負極に接続されることが多いです。ブラシ(ピン3)をスライドさせると、電源電圧の範囲内で任意の電圧を得ることができます。

回転式ポテンショメータと線形ポテンショメータは、どちらも電圧を調整する可変抵抗器として機能しますが、唯一の違いは抵抗値の調整方法にあります。

回路図

結線図

/**********************************************************************
  Filename    : ADC_DAC
  Description : Basic usage of ADC and DAC for esp32.
  Auther      : www.freenove.com
  Modification: 2022/10/20
**********************************************************************/
#define PIN_ANALOG_IN  1
void setup() {
  Serial.begin(115200);
}

void loop() {
  int adcVal = analogRead(PIN_ANALOG_IN);
  double voltage = adcVal / 4095.0 * 3.3;
  Serial.printf("ADC Val: %d, \t Voltage: %.2fV\r\n", adcVal, voltage);
  delay(200);
}

多くの場合はADCで読み取った値にはゆらぎがあります。抵抗を動かさなくてもADCの値は取得するたびに誤差を含んで違う値になってきます。そのため、より正確な値を必要とするようなプロジェクトではトリムミーンという計算を行うことが多いです。

トリムミーンとは

統計学における平均値の一種で、データセットの上位と下位の一定割合を除外した残りのデータの平均値を計算します。これは、異常値や極端な値の影響を受けにくい平均値として知られています。単純な平均値だと、データの上位や下位に特異値を含んでいることがあるため、中央値に近い値の平均を取るのが実務的には有用です。

トリムミーンの計算方法は以下の通りです。

  1. データセットをソートします。
  2. データセットの上位と下位の指定された割合のデータを削除します。
  3. 残りのデータの平均値を計算します。

例えば、以下のデータセットについて、上位10%と下位10%のデータを削除してトリムミーンを計算してみましょう。

1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  1. データセットをソートすると、以下のようになります。
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  1. 上位10%と下位10%のデータを削除すると、以下のようになります。
2, 3, 4, 5, 6, 7, 8, 9
  1. 残りのデータの平均値を計算すると、5.5となります。

トリムミーンを使用するには以下のように修正してください

/**********************************************************************
  Filename    : ADC_DAC
  Description : Basic usage of ADC and DAC for esp32.
  Auther      : www.freenove.com
  Modification: 2022/10/20
**********************************************************************/
#define PIN_ANALOG_IN 1 // 定義を修正

double adcValues[1000]; // 変数名を修正
double voltages[1000]; // 変数名を修正

void setup() {
  Serial.begin(115200);
}

void loop() {
  for (int i = 0; i < 1000; i++) {
    adcValues[i] = analogRead(PIN_ANALOG_IN) * 1.0; // 変数名を修正
  }

  double trimmedMeanADC = TrimMean(adcValues, 0.1); // 変数名を修正
  double voltage = trimmedMeanADC / 4095.0 * 3.3; // 演算子を修正
 
  Serial.printf("ADC Val: %.2f, \t Voltage: %.2fV\r\n", trimmedMeanADC, voltage);
  delay(200);
}

// TrimMean関数
double TrimMean(double* data, double cutRatio) {
  // データのソート
  std::sort(data, data + 1000, [](int a, int b) { return a < b; }); // ラムダ式を使用

  // カットするデータ数
  int trimSize = int(cutRatio * 1000);

  // トリムミーンの合計値
  int trimmedMean = 0;

  // トリムミーンの合計値を計算
  for (int i = trimSize; i < 1000 - trimSize; i++) {
    trimmedMean += data[i];
  }

  // トリムミーンの平均値を計算
  trimmedMean /= (1000 - 2 * trimSize);

  return trimmedMean;
}

私はトリムミーンをよく使用します

次回はタッチセンサーを使ってみましょう

あわせて読みたい
Project 10.1 タッチセンサー/Read Touch Sensor 前回はADCを使用して電圧を読み取ってみました。 https://kaneshige.org/?p=329 今回はタッチセンサーの使い方を学習します。今回もプログラミング要素は少ないです。 ...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

金重総合研究所の主席研究員。
子供の頃から研究者を目指し、ライフワークとして日々様々な研究をしています。
経営・マネジメント・金融・DXあたりが本職です。
私を採用したい人、私と一緒に働きたい人、一緒に知識を肥やしていきたい人はぜひお声がけ下さい。

コメント

コメントする

目次