初めてのArduino ⑧ UNOボードで温度を測る(その3 白金測温抵抗体 MAX31865)

 白金測温抵抗体は悪環境でも測定でき、測定温度範囲は-200~+660°Cと低温側が得意です。抵抗値は0℃の時に10Ω、100Ω、1000Ω値の製品があるようです。

 温度によって抵抗値が変化するのを測るため、白金測温体から測定装置までの導線の抵抗値の影響を受けます。そのため、3線式もしくは4線式の接続方法が用いられます。

 ここでは、抵抗値をA-D変換するMAX31865を利用します。抵抗値と温度の非線形関係は補正しないので、自分で計算します。

使用環境

  • Windows10 Home 21H2
  • Arduino IDE 2.0 RC(2.0.0-rc6)
  • Arduino UNO R3

MAX31865の主な特徴

  • 抵抗値 PT100(100Ω)〜PT1000(1kΩ)
  • 接続  2、3、4線式のいずれにも対応
  • インターフェース SPI(ChipSelect、SCK、MISO、MOSI。Mode1/3)
  • 基準抵抗は430Ω

 ブレークアウト・ボードとPT100のセンサはアマゾンで入手しました。ボードはAdafruit製とよく似ています。

  HiLetgo PT100 MAX31865 Arduino用RTD温度熱電対センサーアンプモジュール

 センサは3線式をアマゾンで購入しました。

  uxcell PT100 RTD温度センサープローブ 3線ケーブル熱電対 ステンレス鋼 200cm(6.56ft)、2Bグレード

<参考>

  Adafruit MAX31865 RTD PT100 or PT1000 Amplifier

接続

  • ねじ留めをする2ピンのターミナルを連結して、ボードに挿し込んではんだ付けする
  • 8ピンのピンヘッダをはんだ付けする
  • シルク印刷「2/3Wire」のショート部分をはんだを盛ってショートする
  • シルク印刷「2 4 3」 ショート部分のはんだ吸い取り線ではんだを取り去る。2-4間がプリント版の銅配線でショートされているので、ナイフで削り取る。4と3をはんだを盛ってショートする
  • F+とRTD+にPT100の赤色のケーブルをそれぞれ接続する
  • F-にPT100の赤色でないケーブルを接続する
  • ピンヘッダVinをArduino UNOの5Vにつなぐ、
  • GND同士をつなぐ
  • ピンヘッダCLKをArduino UNOのSCK(13)につなぐ(LEDマトリクス表示器のSCKにもつながっている)
  • ピンヘッダSDOをArduino UNOのMISO(12)につなぐ
  • ピンヘッダSDIをArduino UNOのMOSI(11)につなぐ(LEDマトリクス表示器のDinにもつながっている)
  • ピンヘッダCSをArduino UNOの(9)につなぐ

最近の表記
  • MISO;マスター・イン・スレーブ・アウト->CIPOコントローラー・イン・ペリフェラル・アウト
  • MOSI;マスター・アウト・スレーブ・イン->COPIコントローラー・アウト・ペリフェラル・イン
  • SS;スレーブ・セレクト<-CS

MAX31865のレジスタ

  データシート

Configuration;アドレス 読み出し時0x00、書き込み時0x80

D7 D6 D5 D4 D3 D2 D1 D0
VBIAS
1 = ON
0 = OFF
Conversion
mode
1 = Auto
0 = Normally off
1-shot
1 = 1-shot
(auto-clear)
3-wire
1 = 3-wire RTD
0 = 2-wire or
4-wire
Fault Detection
Cycle Control
Fault Status
Clear
1 = Clear
(auto-clear)
50/60Hz filter
select
1 = 50Hz
0 = 60Hz

RTDの読み出し上位1バイト;アドレス 読み出し時0x01

RTDの読み出し下位1バイト;アドレス 読み出し時0x02

レジスタ RTDの読み出し上位1バイト RTDの読み出し下位1バイト
ビット D7 D6 D5 D4 D3 D2 D1 D0 D7 D6 D5 D4 D3 D1 D0 D0
データ MSB                           LSB Fault

 アラート関係は省略。

スケッチ

 SPIライブラリを使います。settingsしていないと最近はデフォルトでは動きません。モードは1です。多くのデバイスでは0が使われます。チップ・セレクトSSは9番ピンを使います。LEDマトリクス表示器が10番を使っています。

#include <SPI.h> 
#define SS 9

SPISettings settings(100000,MSBFIRST,SPI_MODE1);
 
 Configurationの書き込みアドレスは0x80です。書き込む内容は0b11010011 ですが、関西の人は0b11010010です。AC100Vの誘導ノイズを軽減します。
 マニュアルにはrefResistor を400Ωと書かれていますが、基板上には430Ωがありました。
 抵抗値は、RTD_Resistance_MSBの上位バイトから2バイト読み出します。
 
#define Configuration_REGISTER_W  0x80
#define Configuration 0b11010011 // 3wire autoconvertion 50Hzfilter
#define setConfig 0x00
const float TDnominal = 100.0;
const float refResistor = 430.0;
#define RTD_Resistance_MSB  0x01
 
 setup() 内です。チップ・セレクト信号はハードでは行ってくれないので、通信を始めるときにLOWにし、おわるとHIGHに戻します
 ここでは、Configurationレジスタに設定内容を書き込んでいます。
 
  SPI.begin();
    SPI.beginTransaction(settings);
        digitalWrite(SS, LOW);
            SPI.transfer(Configuration_REGISTER_W);
            SPI.transfer(Configuration); 
        digitalWrite(SS, HIGH);
    SPI.endTransaction();
 
 抵抗値を読み取って温度を返すread_tempdata() 関数です。抵抗値の入った上位バイト0x01を指定し、ダミーの0x55を2回送って、その代わりに上位バイトhighByte と下位バイトlowByte を読み込みます。
 
double read_tempdata() {
    SPI.beginTransaction(settings);
        digitalWrite(SS, LOW);
            SPI.transfer(RTD_Resistance_MSB); // 
            uint16_t highByte = SPI.transfer(0x55); // dummy
            uint8_t  lowByte = SPI.transfer(0x55);  // dummy
        digitalWrite(SS, HIGH);
    SPI.endTransaction();
 
 
 読み取った2バイトのデータを15ビットのデータに直します。LSBはFaultなので、左に1シフトして消します。データシートに書かれている計算式で抵抗値rを求めます。rから温度Tを求める説明は下記で行います。
 
    uint16_t dataT = ((highByte << 8) | lowByte) >> 1;
    double r = dataT * refResistor / 32768.0;
    double T = 8.0 * 0.0000001 * r * r * r + 0.0007 * r * r + 2.3893 *r - 247.01;
 
 
温度Tを求める
 データシートにCallendar-Van Dusenの式が書かれています。
 
  R(T) = R0(1 + aT + bT^2 + c(T - 100)T^3)
 
 求めたいのはTです。この方程式を解けないので、まずエクセルでグラフを書きます。使用したい温度範囲を-20~110℃にしました。2行目にその温度を記入します。10℃おきにしました。
 -20の1行目(A1)に上記の式を記入します。B1に式をコピーします。
 0(C1)の1行目に上記の式を記入します。cは0なので、記入なしです。N1まで式をコピーします。
 

 2行の数値の入っているところを選択し、メニューの挿入から散布図を選びます。

 線の上にマウスを持っていき、右クリックで近似曲線の書式設定を呼び出します。多項式にチェックし、3次を選択します。グラフに数式を表示するをチェックします。

  y= -3E07x^3 + 0.0009x^2 + 2.3692x -246.2

  x軸=rで、y軸=Tの温度です。プログラムにすると、次のようになります。

    double T = 3.0 * 0.0000001 * r * r * r + 0.0009 * r * r + 2.3692 * r - 246.2;

 0~100℃の時は、

    double T = 8.0 * 0.0000001 * r * r * r + 0.0007 * r * r + 2.3893 *r - 247.01;

です。両方を実行してみました。抵抗値、0~100℃、-20~110℃の順です。

 JISのJIS C 1604-1997の表によると、19.0℃は107.40Ω、20.0℃は107.79Ωなので、この間が比例しているとすると、107.53Ωは、19.33℃と予測できます。したがって、-20~110℃の3次の多項式のほうが正しい値に近いかもしれません。必要だったら、5次や7次を試してみてください。

スケッチ全体

 メインです。

#include  <SPI.h> 
#define SS 9

SPISettings settings(100000,MSBFIRST,SPI_MODE1);

#define Configuration_REGISTER_W  0x80
#define Configuration 0b11010011 // 3wire autoconvertion 50Hzfilter
#define setConfig 0x00
const float TDnominal = 100.0;
const float refResistor = 430.0;
#define RTD_Resistance_MSB  0x01

#include <LEDMatrixDriver.hpp>
#define LEDMATRIX_CS_PIN   10
#define LEDMATRIX_SEGMENTS 4
#define LEDMATRIX_WIDTH  LEDMATRIX_SEGMENTS * 8
int16_t sensorValue;
float analogSensorData;

// This is the font definition. You can use http://gurgleapps.com/tools/matrix to create your own font or sprites.
// If you like the font feel free to use it. I created it myself and donate it to the public domain.
     byte font[14][8] = { {0,0,0,0,0,0,0,0}, // SPACE
                     {0x60,0x60,0x1c,0x12,0x20,0x20,0x12,0x0c}, // `C
                     {0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60}, // DOT
                     {0,0,0,0,0,0,0,0},
                     {0x30,0x48,0x48,0x48,0x48,0x48,0x30,0x00}, // 0
                     {0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00}, // 1
                     {0x30,0x48,0x08,0x08,0x30,0x40,0x78,0x00}, // 2
                     {0x30,0x48,0x08,0x30,0x08,0x08,0x70,0x00}, // 3
                     {0x10,0x10,0x30,0x50,0x78,0x10,0x10,0x00}, // 4
                     {0x70,0x40,0x40,0x30,0x08,0x08,0x70,0x00}, // 5
                     {0x30,0x40,0x40,0x70,0x48,0x48,0x30,0x00}, // 6
                     {0x78,0x48,0x10,0x20,0x20,0x20,0x20,0x00}, // 7
                     {0x30,0x48,0x48,0x30,0x48,0x48,0x30,0x00}, // 8
                     {0x30,0x48,0x48,0x38,0x08,0x08,0x30,0x00}, // 9
                  }; 

LEDMatrixDriver lmd(LEDMATRIX_SEGMENTS, LEDMATRIX_CS_PIN);

void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.println("\nMAX31865 test");
  pinMode(SS, OUTPUT);
  SPI.begin();
    SPI.beginTransaction(settings);
        digitalWrite(SS, LOW);
            SPI.transfer(Configuration_REGISTER_W);
            SPI.transfer(Configuration); 
        digitalWrite(SS, HIGH);
    SPI.endTransaction();

  // init the display
  lmd.setEnabled(true);
  lmd.setIntensity(2);   // 0 = low, 10 = high
}

void loop() {
  Serial.println("====");
  float temp = read_tempdata();
  Serial.println(temp,4);

  String sensorValueSTR = String(temp, DEC);
  char Buf[10]="11.890";
  sensorValueSTR.toCharArray(Buf, 10);
	drawString(Buf, 5, 0, 0);

	drawSprite( font[1], 24, 0, 8, 8 );  // `C
	lmd.display();
  delay(2000);  
}

double read_tempdata() {
    SPI.beginTransaction(settings);
        digitalWrite(SS, LOW);
            SPI.transfer(RTD_Resistance_MSB); // 
            uint16_t highByte = SPI.transfer(0x55); // dummy
            uint8_t  lowByte = SPI.transfer(0x55);  // dummy
        digitalWrite(SS, HIGH);
    SPI.endTransaction();
    Serial.print(highByte); Serial.print(" , ");Serial.println(lowByte);
    uint16_t dataT = ((highByte << 8) | lowByte) >> 1;
    double r = dataT * refResistor / 32768.0;
    double T = 8.0 * 0.0000001 * r * r * r + 0.0007 * r * r + 2.3893 *r - 247.01;
    double T2 = 3.0 * 0.0000001 * r * r * r + 0.0009 * r * r + 2.3692 * r - 246.2;
    Serial.print(r); Serial.print(" , ");Serial.print(T,4); Serial.print(" , ");Serial.println(T2,4);
  return  T2;
}

 タブfonts.inoです。


                  
// * This function draws a string of the given length to the given position.

void drawString(char* text, int len, int x, int y ){
  //Serial.println("===");
  int flag=0;
  for( int idx = 0; idx < len; idx ++ ){
    int c = text[idx] ;
    //Serial.println(c);
    // stop if char is outside visible area
    if( x + idx * 5  > LEDMATRIX_WIDTH )
      return;

    // only draw if char is visible
    if( 4 + x + idx * 4 > 0 )
      if (!flag) {
        drawSprite( font[c-44], x+1 + idx * 5, y, 5, 8 );
      } else  {
        drawSprite( font[c-44], x-1 + idx * 5, y, 5, 8 );
        flag = 1;
      }

      if (c==46) flag = 1;
  }
}

// * This draws a sprite to the given position using the width and height supplied (usually 8x8)

void drawSprite( byte* sprite, int x, int y, int width, int height ){
  // The mask is used to get the column bit from the sprite row
  byte mask = B10000000;
  for( int iy = 0; iy < height; iy++ ){
    for( int ix = 0; ix < width; ix++ ){
      lmd.setPixel(x + ix, y + iy, (bool)(sprite[iy] & mask ));

      // shift the mask by one pixel to the right
      mask = mask >> 1;
    }
    // reset column mask
    mask = B10000000;
  }
}


前へ

初めてのArduino ⑦ UNOボードで温度を測る(その2 TMP117)