PT100センサとArduino Unoで作る高精度温度管理サンプル輸送コンテナ
必要な部品と材料
- PT100温度センサ × 1
- Arduino UNO × 1
- リチウムイオンバッテリー(10400 mAh) × 1
- ポリスチレン箱(サンプル/薬品配送用のリサイクル品) × 1
- 銅ブロック(約80×50×10mm、ヒートシンクおよびペルチェ取り付け用に加工、ヒートパイプ穴付き) × 1
- 調整可能昇圧電源モジュール(ULS) × 1
- ヒートパイプクーラー × 1
- デジタルポテンショメータ(MCP41100) × 1
- マジックジェル(サンプル間に金属棒を入れ熱伝導向上) × 1
- DCコンバータ × 1
- ペルチェモジュール × 1
- コンデンサ 3.3μF × 1
- ヒートパイプ × 1
- ACアダプタ用電源 × 1
- DCスプリッタ × 1
- Adafruit LCDシールド × 1
- ヒーターマット(サンプル加熱用) × 1
- ターミナルブロック × 1
- プラスチックケース(内部用) × 1
- パネルマウントDCジャック × 1
- 4極ダブルスロー スイッチ × 1
- 単極ダブルスロー スイッチ × 1
- プラスチックケース(外部電子部品用) × 1
- ターミナルブロックコネクタ 3極 × 1
- ターミナルブロックコネクタ 5極 × 1
- フックアンドループ DualLockテープ × 1
- Adafruit Proto-screwshield × 1
- Adafruit リチウムイオンUSBバッテリーチャージャー × 1
- Adafruit PT100温度センサアンプ(MAX31865) × 1
必要な工具と機器
- はんだごて(汎用)
- Dremel 4000
- バイス
- ドリル
- ケーブルストリッパー、ペンチ、ニッパー
- 手動フライス盤(銅部品加工用)
使用するアプリとオンラインサービス
- GitHub(sleemanj氏のMCP41100ライブラリ、Adafruit各モジュールライブラリ)
- Arduino IDE
プロジェクト概要
Mr ThermoParcel:サンプル輸送用温度管理コンテナ
一般的な郵便サービスで温度に敏感なサンプルを安全に輸送できるコンテナを開発することを目的としています。試作機「Mr ThermoParcel」は4〜37℃の範囲で動作し、ACアダプタ、内蔵バッテリ、またはシガーソケットやノートPC充電器などの12V電源で駆動可能です。
プロジェクトの発端
共同研究者と温度に敏感な生物サンプル(血液など)をやり取りする際、適切な温度管理がされないとサンプルが劣化するという実体験から生まれました。凍結輸送は高コストかつ凍結/解凍でサンプルが損傷し、大学の宅配ロッカーでの保管遅延もリスクとなります。
設計コンセプト
一般的なポリスチレン箱(250×250×250mm、壁厚45mm)を外装とし、内部にプラスチックケース(125×70×40mm)を配置。ここに2本の50mL遠心管を二次容器として収め、一次サンプル(2mLエッペンドルフチューブ6本)を吸収ティッシュで仕切って固定します。冷却はペルチェ素子、加熱はヒーターマットで行い、PT100センサでサンプル温度を監視、ArduinoがPID制御で冷却/加熱強度を調整します。

実装詳細
コンテナハードウェア設計
内部ボックスには電気絶縁性の固化ジェルを充填し、サンプルと銅板、温度センサの熱的接触を確保。ジェルは加熱/冷却の均一化にも寄与します。

冷却・加熱システム
ペルチェ素子(定格3.9A、7.6V)の冷却面を内部銅板に、放熱面をヒートパイプ3本で外部銅ブロック+CPUクーラーに接続。ヒーターマットは内部ケース内でセンサとサンプルに接触させます。



電子回路と配線
冷却/加熱制御
ペルチェ駆動にはPTN78020W降圧スイッチングレギュレータを使用。入力7-36V、出力2.5-12.6V。出力電圧は2つの調整ピン間の抵抗値で決まり、Arduino制御のMCP41100デジタルポテンショメータ(100kΩ)で可変抵抗として機能させます。ヒーターマットも同様に電圧制御で発熱量を調整。
電源系統
外部12V電源時はPTN78020Wへ直接給電。内蔵バッテリ(3.7V 10400mAh)使用時はXL6019昇圧コンバータで10-12Vへ昇圧してからPTN78020Wへ入力。Arduinoは外部12Vまたは昇圧後の電圧をジャックから受け取ります。
Arduino配線
デジタルポテンショメータはsleemanjライブラリの可変抵抗モードで接続。PT100アンプ(MAX31865)とLCDシールドはAdafruit公式ドキュメント通りに配線。
ソフトウェア
各Adafruitモジュールは専用ライブラリ、デジタルポテンショメータはsleemanjライブラリで制御。核心はPID閉ループ温度制御で、目標温度はLCDシールドのボタンで設定。偏差からPID値を算出しデジタルポテンショメータへ出力。物理トグルスイッチで冷却/加熱モードを切替、コード側でもモードフラグで符号を反転。実験ではP項のみで±0.5℃以内に収束したためI/D項は省略(熱容量が大きく温度変化が緩やかなため)。
性能評価
- AC駆動時:室温21-23℃から4℃まで約1時間。8-10℃到達は約20分。ヒーターマットで37℃到達は約10分。全域で設定値±0.5℃以内を維持。
- バッテリ駆動時:最低約10℃(1.5-2時間)。加熱37℃は40-60分。バッテリの最大放電電流(7A@3.7V)と昇圧損失がボトルネック。ソフトウェアで電流制限を実装。より高放電率のバッテリや3直列(11.1V)リチウムイオンなら昇圧不要で解決可能。


今後の展望
CPUクーラーの可動部と堅牢性の問題で現状は発送不可。ヒートシンクをパッシブ化し、95kPa耐圧の二次容器を採用すれば航空輸送含め標準宅配要件を満たせる見込み。温度ログの記録やGSMモジュールによるSMS通知、GPSトラッキング追加も検討中。
Arduinoスケッチ(PID_LCD_controller_v03)
#include <Adafruit_MAX31865.h>
#include <MCP41_Simple.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>
#include <Wire.h>
// デジタルポテンショメータ
MCP41_Simple digitalPotentiometer;
const uint8_t digitalPotentiometer_CS = 10;
// PT100温度センサ(ソフトウェアSPI: CS, DI, DO, CLK)
Adafruit_MAX31865 PT100amplifier = Adafruit_MAX31865(2, 3, 4, 5);
#define RREF 430.0
#define RNOMINAL 100.0
// LCDシールド
Adafruit_RGBLCDShield LCD_shield = Adafruit_RGBLCDShield();
int powerMode = 1; // 1:バッテリ, -1:AC
int operationMode = 1; // 1:冷却, -1:加熱
float PT100ratio;
uint8_t buttonsPressed = 0;
float kp = 500.0;
int ki = 5;
int kd = 3.9;
float PID_p = 0.0;
float Tmeasured = -1.0;
float Tsetpoint = 22.0;
float PID_error = 5;
float PID_value = 0;
static char TsetpointString[3];
static char TmeasuredString[4];
void print_Tsetpoint(int T) {
LCD_shield.setCursor(10, 0);
dtostrf(T, 3, 0, TsetpointString);
LCD_shield.print(TsetpointString);
}
void print_Tmeasured(float T) {
LCD_shield.setCursor(8, 1);
dtostrf(T, 5, 1, TmeasuredString);
LCD_shield.print(TmeasuredString);
}
void print_powerMode() {
LCD_shield.setCursor(15, 0);
LCD_shield.print(powerMode == 1 ? "B" : "M");
}
void print_operationMode() {
LCD_shield.setCursor(15, 1);
LCD_shield.print(operationMode == 1 ? "C" : "H");
}
void setup() {
Serial.begin(115200);
Serial.println("Mr ThermoParcel, starting operation...");
PT100amplifier.begin(MAX31865_4WIRE);
digitalPotentiometer.begin(digitalPotentiometer_CS);
digitalPotentiometer.setWiper(200);
LCD_shield.begin(16, 2);
LCD_shield.print("Tsetpoint: C");
LCD_shield.setCursor(0, 1);
LCD_shield.print("Tsample: C");
}
void loop() {
// 温度読み取り
uint16_t rtd = PT100amplifier.readRTD();
PT100ratio = rtd / 32768.0;
Tmeasured = PT100amplifier.temperature(RNOMINAL, RREF);
Serial.print("Setpoint Temperature = "); Serial.println(Tsetpoint);
Serial.print("Temperature = "); Serial.println(Tmeasured);
print_Tsetpoint(Tsetpoint);
print_Tmeasured(Tmeasured);
print_powerMode();
print_operationMode();
// PID計算
PID_error = Tmeasured - Tsetpoint;
PID_p = operationMode * kp * PID_error;
PID_value = (int)PID_p;
Serial.print("PID_p = "); Serial.println(PID_p);
Serial.print("powerMode = "); Serial.println(powerMode);
Serial.print("operationMode = "); Serial.println(operationMode);
Serial.print("PID_error = "); Serial.println(PID_error);
Serial.print("PID_value = "); Serial.println(PID_value);
// バッテリモードでは出力制限(過負荷防止)
if (powerMode == 1) {
PID_value = constrain(PID_value, 0, 120);
} else {
PID_value = constrain(PID_value, 0, 255);
}
Serial.print("Adjusted PID_value = "); Serial.println(PID_value);
// デジタルポテンショメータ設定(ワイパー位置は逆転)
digitalPotentiometer.setWiper(255 - PID_value);
// ボタン操作で目標温度/モード変更
delay(1000);
buttonsPressed = LCD_shield.readButtons();
if (buttonsPressed & BUTTON_SELECT) {
LCD_shield.setCursor(14, 0);
LCD_shield.blink();
delay(1000);
buttonsPressed = 0;
while (!(buttonsPressed & BUTTON_SELECT)) {
buttonsPressed = LCD_shield.readButtons();
if (buttonsPressed & BUTTON_UP) Tsetpoint += 1;
if (buttonsPressed & BUTTON_DOWN) Tsetpoint -= 1;
if (buttonsPressed & BUTTON_RIGHT) { powerMode *= -1; print_powerMode(); }
if (buttonsPressed & BUTTON_LEFT) { operationMode *= -1; print_operationMode(); }
print_Tsetpoint(Tsetpoint);
LCD_shield.setCursor(14, 0);
delay(500);
}
LCD_shield.noBlink();
buttonsPressed = 0;
}
Serial.println();
}
回路図
メイン回路図:

配線実装図(コミュニティ要望で公開、実装用のラフな図):

ポテンショメータ周りの配線:

製造プロセス