初めての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番を使っています。
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;
}
// * 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;
}
}