温度測定

今回は、サーミスタを使用して温度を測定します。

使用パーツ

  • サーミスタ(103AT)
  • 抵抗器10kΩ
  • ブレッドボード

サーミスタとは

温度によって抵抗値が変動する素子です。その抵抗値と温度の関係は下のような近似式で表現できます。

thermistor_approximation_1

これをTについて解くとこうなります。

thermistor_approximation_2

下図はデータシートの一部です。値BはB定数と呼ばれ、サーミスタ素子ごとに決まった値があります。 Toは25℃です。 Rrefは回路ごとに任意に決めてよく、ここでは10kΩとします。

thermistor_datasheet

出典 http://akizukidenshi.com/download/ds/semitec/at-thms.pdf

あとはRつまりサーミスタの抵抗値がわかれば、温度Tを求めることができます。 ではどうやってRを測定するのでしょうか? 下図をご覧ください。

thermistor_circuit_resistance

この図の電圧値Vrefがわかればよいことを示しています。よく見ると、これもオームの法則ですね。

ESP32には(そして他の多くのワンチップマイコンにも)、ADC(アナログ・デジタル・コンバータ)が搭載されており、Vrefの値を測定できます。 ちなみにラズパイにはADCが搭載されていませんので、別途ADCチップを買ってきて回路を組む必要があります。

ブレッドボードに配線する

ESP32開発ボードと抵抗器、サーミスタをブレッドボードで接続しましょう。青い素子がサーミスタで、方向の決まりはありません。

breadboard_thermistor

回路図と見比べることで、「IO0」ピンを3.3Vに固定し、「IO4」ピンがVrefを計測するということを理解できると思います。

プログラムを書く

いままでのハンズ・オンと同様にテンプレートをcloneします。

cd $HOME/esp
git clone https://github.com/hasumikin/mrubyc-template-esp32.git measuring-temperature
cd measuring-temperature

main/main.c

#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"

#include "mrubyc.h"

#include "models/thermistor.h"
#include "loops/master.h"

#define DEFAULT_VREF    1100
#define NO_OF_SAMPLES   64
#define MEMORY_SIZE (1024*40)

static esp_adc_cal_characteristics_t *adc_chars;
static const adc_channel_t channel = ADC_CHANNEL_0; //GPIO4
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_2;

static uint8_t memory_pool[MEMORY_SIZE];

static void c_gpio_init_output(mrb_vm *vm, mrb_value *v, int argc) {
  int pin = GET_INT_ARG(1);
  console_printf("init pin %d\n", pin);
  gpio_set_direction(pin, GPIO_MODE_OUTPUT);
}

static void c_gpio_set_level(mrb_vm *vm, mrb_value *v, int argc){
  int pin = GET_INT_ARG(1);
  int level = GET_INT_ARG(2);
  gpio_set_level(pin, level);
}

static void c_init_adc(mrb_vm *vm, mrb_value *v, int argc){
  adc2_config_channel_atten((adc2_channel_t)channel, atten);
  adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
  esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
}

static void c_read_adc(mrb_vm *vm, mrb_value *v, int argc){
  uint32_t adc_reading = 0;
  for (int i = 0; i < NO_OF_SAMPLES; i++) {
    int raw;
    adc2_get_raw((adc2_channel_t)channel, ADC_WIDTH_BIT_12, &raw);
    adc_reading += raw;
  }
  adc_reading /= NO_OF_SAMPLES;
  uint32_t millivolts = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
  SET_INT_RETURN(millivolts);
}

void app_main(void) {
  mrbc_init(memory_pool, MEMORY_SIZE);

  mrbc_define_method(0, mrbc_class_object, "gpio_init_output", c_gpio_init_output);
  mrbc_define_method(0, mrbc_class_object, "gpio_set_level", c_gpio_set_level);
  mrbc_define_method(0, mrbc_class_object, "init_adc", c_init_adc);
  mrbc_define_method(0, mrbc_class_object, "read_adc", c_read_adc);

  mrbc_create_task( thermistor, 0 );
  mrbc_create_task( master, 0 );
  mrbc_run();
}

mrblib/loops/master.rb

thermistor = Thermistor.new

while true
  puts "temperature: #{thermistor.temperature}"
  sleep 1
end

mrblib/models/thermistor.rb

B = 3435
To = 25
V = 3300 # mV
Rref = 10_000 # Ohm

class Thermistor
  def initialize
    gpio_init_output(0)
    gpio_set_level(0, 1)
    init_adc
  end

  def temperature
    vref = read_adc
    r = (V - vref).to_f / (vref.to_f/ Rref)
    1.to_f / ( 1.to_f / B * Math.log(r / Rref) + 1.to_f / (To + 273) ) - 273
  end
end

Mathクラスを有効化

対数を計算するために、mruby/cのMathクラス(数学計算のためのライブラリ)を有効にしなければなりません。デフォルトではオフになっているためです。

components/mrubyc/mrubyc_src/vm_config.h を開き、この行を探し、

#define MRBC_USE_MATH 0

以下のように修正してください。0を1にするだけです。

#define MRBC_USE_MATH 1

ビルド、実行

もちろん make flash monitor で実行できます(menuconfig画面のシリアルポート設定もお忘れなく)。

うまくいけば、1秒ごとに温度が表示されるはずです。

capture_taking_temperature