ここから3回、温度計測方法について紹介します。温度を測るには、当然、温度計が必要ですが、これには様々な原理に基づいた様々な種類のものがあります。中には原理をあまり明確に示していない製品もあります。自身の用途にどれが適しているかを検討しないといけないわけですが、精度やレンジという問題のほかに、形状や大きさ、防水性なども勘案しなければなりません。ここから3種類の温度センサーを順次紹介します。最初は熱電対(thermocouple)です。
熱電対そのものについては別途検索して下さい。簡単に書くと、2種類の金属線が(互いに絶縁されて)1本に並んだ、2芯ケーブルです。異なる金属が接触すると、温度に応じた起電力が発生する(ゼーベック効果)という原理に基づいたものです。金属の種類によって、K型、T型、などいくつも種類があり、計測可能な温度範囲が異なります。この起電力は非常に小さいので、センサーとして用いるためには増幅してやる必要があります。土質試験では特段に高い温度を計測することは少ないと思います。最も一般的と思われるのがK型(クロメル―アルメル、-200~+1000℃:温度範囲はWikipediaによる)です。私は凍結問題を扱うので、むしろ低温側での計測が必要となりますが、T型(銅-コンスタンタン、-200~+300℃:同上)がよいと聞いています。ただ、使った印象としてはK型とそれほど変わらないと思いました。
熱電対はAmazonでもMonotaroでもどこでも買えますが、凍土仲間のT先生に教えてもらったチノーという会社の「被覆熱電対」という製品を100m巻きで買っています(100mで確か2~3万円、そんなに長く必要なければ他でもっと安く買えます)。なお、この会社のウェブサイトの仕様ではK型・T型の使用温度上限は100℃となっています。金属の性質上+1000℃まで測れても、シース(絶縁体)が溶けては使えないですからね。
熱電対は、先端をむき出して、2つの金属が触れるようによじってやればこれで完成です(右写真)。ほどけないように、また防水処理として、この後、エポキシ樹脂で先端を固めて保護します。この部分の温度が起電力(電圧)として線の反対側(左写真)で計測されます。なお、電圧は相対的なものなので、零点補償といって、氷水(0℃)を使って温度を較正するのが正確な使い方と言われていますが、市販のロガーでは、線をつなぐだけで零点補償をしてくれるものが多いです。詳しいことはよく知りませんが、おそらくロガー内部のサーミスタなどでロガー側の接点温度を計測し、それに基づいて補償するのだと思います。よくわからん?説明しているサイトがいろいろあるので検索してみてください。
熱電対をArduinoに直接接続しても電圧が小さすぎて読み取れないので、例によってモジュールを紹介します。熱電対アンプモジュールはいくつかあります。簡単に手に入るものとしては、AD8495、MAX6675、MAX31855、MAX31856などがあります。AD8495は増幅した電圧をアナログで出力するもので、K型熱電対に対して約5 mV/℃で出力するそうです。Arduino UNOを用いる場合、電圧計測は5V/10bit=5mVなので、およそ1℃単位でしか計れませんね(第4回参照)。ADCモジュールADS1115(第8回参照)を使えれば、4V/16bitなので0.01~0.02℃くらいの分解能で読めそうです。実は、持っているのですが、使ったことがありません。次に紹介するMAX31856を使うことが多いからです。
熱電対モジュールMAX31856
MAX31856はMAX6675やMAX31855の上位版チップのようですが、あるサイトによれば、性能は各段に違うようです。価格も違いますが、研究用なら間違いなくMAX31856のほうがよいとのこと(MAX31855も使ったことがないのでわかりませんが)。ここではMAX31856の使い方を紹介します。MAX31856を搭載したモジュールはやや高めで、1500~2000円くらいで売っていることが多いです。Adafruit製が「正規品」のようですが、同じ作りのものは例によってたくさん売っています。2極端子台がついてくることが多く、写真中右のようにはんだ付けすると便利です。対応している熱電対の型は、B, E, J, K, N, R, S, Tです。サンプルコードを見ると、ゲインも変えようと思えば変えられるように見えます。
MAX31856はSPIでマイコンと通信します(第5回参照)。図のように接続してください。
そして、以下のライブラリをダウンロードします。
https://github.com/adafruit/Adafruit_MAX31856
以下のスケッチを使うと、シリアルモニタに温度計測結果が現れます。なお、ここではK型熱電対を使っています。
#include <Adafruit_MAX31856.h>
Adafruit_MAX31856 max = Adafruit_MAX31856(8, 11, 12, 13);
void setup()
{
Serial.begin(9600);
Serial.println("MAX31856 thermocouple test");
max.begin();
max.setThermocoupleType(MAX31856_TCTYPE_K);
}
void loop()
{
Serial.print("Thermocouple Temp: "); Serial.println(max.readThermocoupleTemperature());
delay(1000);
}
SDカードシールドとの併用
ここまでは他のサイトにも書いてあると思いますが(何より、Adafruitのサイトそのものにわかりやすく書いてあります:英語が問題でなければ)、このMAX31856で計測した結果をSDカードに記録しようとしたところで、かなり苦戦しましたので、そのやり方について説明します。他のサイトにいろいろヘルプを求めましたが、見つかりませんでした。ついでに、多チャンネル化のために複数のMAX31856を使うことを考えます(ここでは例として2個)。問題は、他のSPIセンサーをつなぐとSDカードシールドとの通信ができなくなることでした。
まず、SPI通信は、第5回で説明したように、6本の線を使います(VccとGNDで2本、厳密には残りの4本が通信用)。I2C通信と異なり、チップそのものがアドレスを持っていないため、個々のチップのCS(Chip Select)をArduinoの異なるデジタルピンにつなぎ、それを通して個々のチップを区別して通信をします(たとえば、1つ目のMAX31856のCSはピン8に、2つ目はピン9に、とつなぎ、「これからピン8のやつと通信しますよ」「次はピン9」として区別するわけです)。I2C通信と異なり、アドレス競合の問題がありませんが、デジタルピンの数しか接続できないという欠点もあります(実際にはバス拡張などで対応できるはずです)。まあとにかく、デジタルピンが3つ余っていれば、SDカードシールドとMAX31856を2個、計3つを接続できるはずです。多くのサイトには、CSを違うピンにつないで区別しましょう、とまでしか書いてありません。
そこで、以下のように接続します。デジタルピンは8, 9, 10をCSに使います。MOSI・MISO・CLKは共通です。MAX31856モジュールの基盤にはSDO・SDIなどとありますが、それぞれMISO・MOSIと同じことです(Master-In, Slave-OutとMaster-Out, Slave-In:通信の方向を示します。SDOとSDIはSlave Data-OutとSlave Data-Inなのでしょう)。なお、昨今の人種差別問題の余波で、通信の世界でもmasterやslaveという呼び名はなくす運びにあるようです。ちなみに大きな寝室をマスターベッドルームと呼ぶのもタブーらしいです。マスターキーやマスタープラン、バーのマスターもダメになるのでしょうか。
ブレッドボード図だと煩雑でかえってわかりづらいかもしれません。ブレッドボードを省いて描くと、
ここで、電圧レギュレータなしのSDカードシールドを使っているので、SDカードは3.3V端子から印加しています。以下のスケッチを使います。ここで、最後のほうにあるSPI.end()という一文が重要です。これを入れないと、SDカードと通信とした次のループで、MAX31856と通信できなくなります。先ほどと同様に、CS(Chip Select)には8~10を使っています。
#include <SPI.h>
#include <SD.h>
#include <Adafruit_MAX31856.h>
const int TCSS1 = 8;
const int TCSS2 = 9;
const int chipSelect = 10;
Adafruit_MAX31856 max1 = Adafruit_MAX31856(TCSS1, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK
Adafruit_MAX31856 max2 = Adafruit_MAX31856(TCSS2, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK
void setup()
{
Serial.begin(9600);
delay(1000);
}
void loop()
{
/* Start MAX31856 and read temperatures */
max1.begin();
max1.setThermocoupleType(MAX31856_TCTYPE_K);
max2.begin();
max2.setThermocoupleType(MAX31856_TCTYPE_K);
float temp1=max1.readThermocoupleTemperature();
float temp2=max2.readThermocoupleTemperature();
/* Write the readings to SD card */
SD.begin(chipSelect);
File dataFile = SD.open("datalog.csv", FILE_WRITE);
if (dataFile)
{
dataFile.print(temp1);dataFile.print(", ");dataFile.println(temp2);
}
dataFile.close();
SPI.end();
delay(1000);
}
MAX31856を2つ使えたら、おそらく3つでも4つでも使えることでしょう。例えば三軸圧縮試験中、土供試体の上端・下端・側面など、いろいろな場所の温度が測れます(先端の防水処理だけは注意が必要。防水が甘いと、シースの隙間をつたって三軸セルの中から水が漏れてきます)。
実用例紹介
ここで、北海道大学の地盤物性学研究室に導入した自作ロガーを紹介します。この装置は、一定荷重の下、粘土を凍結融解し、圧縮量の累積を計測していくもので、同じ原理の装置で行った研究は論文として公開されています(Nishimura et al., 2020)。ここでは、粘土供試体の上端・下端の温度を熱電対で計測し、荷重・変位をひずみゲージ型センサーで計測し(第19回参照)、LCDに表示するとともにSDカードに記録しています。このブログでいえば、第5、7、18、19、20回を読んでもらえれば作れます。それらを読まなくても、以下の通りつないで(ライブラリを入れたうえで)スケッチを入れればできますが。東京測器社製のハンドヘルドデータロガーを使っていたのですが、表示更新が遅く見づらいのと、ロードセルの分解能が低く、上載圧にして4kPa単位でしか表示できなったため、自作したという経緯があります。私はArduino Pro MiniやNanoといった小型のモデルを好んでいますが、ここでは5V(LCDを明るくするために必要)と3.3Vの出力端子を両方持つUNOをあえて使っています。
ちょっとした配置上の工夫点としては、HX711とMAX31856を上下に重ね、ビスで少し浮かした状態で固定していることです(下の写真)。二階建てにして、小さなユニバーサル基板の面積を有効利用しています。上のほうのユニバーサル基板上は不思議な配置に見えるかもしれません。他の装置で使っていたものを再利用しているからです。
いくつかの注意点です。
・第7回で紹介したRTCで時間を記録しています。
・HX711の読みにはセンサー固有の較正係数を掛けています。
・SDカードには10秒に1回だけ書き込むようにしています。
#include <DS3232RTC.h>
#include <Time.h>
#include <TimeLib.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <Adafruit_MAX31856.h>
#include <LiquidCrystal_I2C.h>
#include <HX711.h>
const int TCSS1 = 8;
const int TCSS2 = 9;
const int chipSelect = 10;
const float CF1=0.00368;
const float CF2=0.00000091;
int rsec=0;
Adafruit_MAX31856 max1 = Adafruit_MAX31856(TCSS1, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK
Adafruit_MAX31856 max2 = Adafruit_MAX31856(TCSS2, 11, 12, 13);// Use software SPI: CS, DI, DO, CLK
LiquidCrystal_I2C lcd(0x3F,16,2);
HX711 channel1;
HX711 channel2;
void setup()
{
/* ----- Serial connection with PC ----- */
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
/* ----- Initialisation of LCD display ------ */
lcd.init();
lcd.backlight();
/* ----- Initialisation of load cell amplifier ----- */
channel1.begin(A0, A1); // HX711.DOUT - pin #A0, HX711.PD_SCK - pin #A1
channel2.begin(A2, A3); // HX711.DOUT - pin #A2, HX711.PD_SCK - pin #A3
/* ----- Initialisation of time ----- */
//setTime(22, 29, 00, 28, 6, 2010); // set the system time in Arduino [Hour, Min, Sec, Month, Day, Year]
//RTC.set(now()); // set the RTC time according to Arduino (first time only, to reset the RTC)
setSyncProvider(RTC.get); // get the time from the RTC and set it to Arduino
if(timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
delay(1000);
}
void loop()
{
max1.begin();
max1.setThermocoupleType(MAX31856_TCTYPE_K);
max2.begin();
max2.setThermocoupleType(MAX31856_TCTYPE_K);
float temp1=max1.readThermocoupleTemperature();
float temp2=max2.readThermocoupleTemperature();
float strain_s1=channel1.read_average(20)*CF1-43.8-95.6;
float strain_s2=channel2.read_average(20)*CF2;
digitalClockDisplay();
Serial.print(temp1);Serial.print(", ");Serial.print(temp2);Serial.print(", ");Serial.print(strain_s1);Serial.print(", ");Serial.println(strain_s2);
/* Write the readings to SD card */
if(int(second()/10)!=rsec)
{
rsec=int(second()/10);
SD.begin(chipSelect);
File dataFile = SD.open("datalog.csv", FILE_WRITE);
if (dataFile)
{
dataFile.print(year());dataFile.print("/");dataFile.print(month());dataFile.print("/");dataFile.print(day());dataFile.print(" ");
dataFile.print(hour());dataFile.print(":");dataFile.print(minute());dataFile.print(":");dataFile.print(second());dataFile.print(" ,");
dataFile.print(temp1);dataFile.print(", ");dataFile.print(temp2);dataFile.print(", ");dataFile.print(strain_s1);dataFile.print(", ");dataFile.println(strain_s2);
}
dataFile.close();
}
SPI.end();
/* Display to LCD */
lcd.clear();
lcd.setCursor(0, 0);lcd.print(temp1);
lcd.setCursor(7, 0);lcd.print(" ");
lcd.setCursor(8, 0);lcd.print(strain_s1);
lcd.setCursor(0, 1);lcd.print(temp2);
lcd.setCursor(7, 1);lcd.print(" ");
lcd.setCursor(8, 1);lcd.print(strain_s2);
//delay(1000);
}
/* Function to display time on PC via serial */
void digitalClockDisplay(void)
{
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(' ');
Serial.print(day());
Serial.print(' ');
Serial.print(month());
Serial.print(' ');
Serial.print(year());
Serial.println();
}
/* Function to adjust the digits of minute and second */
void printDigits(int digits)
{
Serial.print(':');
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
参考文献
Nishimura, S., Okajima, S., Joshi, B. R., Higo, Y. and Tokoro, T. (2020) Volumetric behaviour of clays under freeze-thaw cycles. Géotechnique, Under production. DOI: 10.1680/jgeot.20.P.047