ESP32でstdなRust開発入門 その4 ADC

概要

前回はデジタル入出力でしたが、今回はアナログ入力のADCを調べてみます。

公式サンプル

https://github.com/esp-rs/esp-idf-hal/blob/master/examples/adc.rs

上記がサンプルコードとなります。

use esp_idf_hal::adc::config::Config;
use esp_idf_hal::adc::AdcChannelDriver;
use esp_idf_hal::adc::AdcDriver;
use esp_idf_hal::adc::Atten11dB;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::peripherals::Peripherals;

use esp_idf_hal::gpio::Gpio32;

fn main() -> anyhow::Result<()> {
    let peripherals = Peripherals::take().unwrap();

    let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true))?;

    let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio32, Atten11dB<_>> =
        AdcChannelDriver::new(peripherals.pins.gpio32)?;

    loop {
        println!("ADC value: {}", adc.read(&mut adc_pin).unwrap());
        FreeRtos::delay_ms(100);
    }
}

ESP32向けにちょっと手を入れたのが上記になります。

ADCユニットの指定

ADCユニットチャンネルESP32ESP32-S2ESP32-S3ESP32-C3ESP32-H2
ADC10361100
ADC11372211
ADC12383322
ADC13394433
ADC14325544
ADC153366
ADC163477
ADC173588
ADC1899
ADC191010
ADC204111155
ADC2101212
ADC2221313
ADC23151414
ADC24131515
ADC25121616
ADC26141717
ADC27271818
ADC28251919
ADC29262020

ESP32シリーズではADCユニットが2つあり、ADC2は無線利用時には使えなくなる制約があったりします。チャンネルは内部的には利用していますが、通常は意識する必要ありません。

ArduinoではGPIOだけ指定すればADCのユニットも自動的に決まりますが、RustのライブラリではGPIOとADCユニットを両方指定する必要があります。ちょっと面倒ですね。。。

    let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true))?;

上記でADCユニットを取得しています。

GPIOの指定

    let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio32, Atten11dB<_>> =
        AdcChannelDriver::new(peripherals.pins.gpio32)?;

上記で指定しています。

ちなみにGPIO32はADC1の4チャンネルですが、間違ってADC2を指定するとADC2の4チャンネルであるGPIO13に連動した値が取れましたが、取得できた値がGNDで3Vに近い値、3V3で0に近いひっくり返った値になっていました。

あまりイケてない設計ですね。

電圧取得

println!("ADC value: {}", adc.read(&mut adc_pin).unwrap());

こちらは普通に取得できますね。

ADC value: 634
ADC value: 610
ADC value: 142
ADC value: 142
ADC value: 386
ADC value: 533
ADC value: 587
ADC value: 551
ADC value: 3165
ADC value: 3165
ADC value: 3165

この値はmVの電圧値ですね。

Arduinoでいうと上記にあるanalogReadMilliVolts()関数相当になります。つまりこの端末だとGNDに接続しても142mVまでしか下がらず、3V3に接続しても3165mVまでとなります。

ソースコード見た限り、このライブラリでは生のADCデータを取得することはできないようです。ESP-IDFの関数を直接呼ばないとだめそうですね。

生ADCを取得してみる

use esp_idf_hal::adc::config::Config;
use esp_idf_hal::adc::AdcChannelDriver;
use esp_idf_hal::adc::AdcDriver;
use esp_idf_hal::adc::Atten11dB;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_sys::adc1_get_raw;

use esp_idf_hal::gpio::Gpio32;

fn main() -> anyhow::Result<()> {
    let peripherals = Peripherals::take().unwrap();

    let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true))?;

    let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio32, Atten11dB<_>> =
        AdcChannelDriver::new(peripherals.pins.gpio32)?;

    loop {
        print!("ADC value: {}", adc.read(&mut adc_pin).unwrap());
        println!(" ({})", unsafe { adc1_get_raw(4) });
        FreeRtos::delay_ms(100);
    }
}

adc1_get_raw()を直接呼び出すことで生データを取得しています。アッテネーターとかの設定が本来必要ですが、正規の呼び出しをしているので設定済みになっているはずです。

ADC value: 142 (0)
ADC value: 142 (0)
ADC value: 380 (279)
ADC value: 528 (460)
ADC value: 637 (553)
ADC value: 628 (575)
ADC value: 3165 (4095)
ADC value: 3165 (4095)
ADC value: 3165 (4095)

ちゃんとADCの最小値0と最大値4095が取得できていました。とはいえ、ESP-IDFの関数を直接呼び出すのはあまり好ましく無いはずです。

まとめ

ちょっと使いにくいADCですね。ここでArduino互換の関数とかを作ってしまうと面倒だと思いますので、全部作りきってクレートとして公開するか、標準関数を使うしかないと思います。。。

コメント