制御工学の学び直しをしたので、じゃあ何か制御してみよう、とりあえず目標の実現が簡単そうに見える温度制御を!ってことで、恒温槽を作ってみたというお話です。ただし、”恒温槽”そのものを作る能力が私には無いためその構造物の作成はしておらず、DAISOで手に入る部材の”切った張った”でなんとか構成しています。よって、本稿は、温度制御するソフトウエアや基板の話が主になります。

1.外観と操作イメージ
外観はこんな感じです。中身が見えた方が”手作り感”がって良いなと思ったので半透明にしてあります。操作インタフェースはほんの少しの表示部とボタンがあれば事足りるので、ブラウザにしました。

2.目的や仕様
”作ること自体”や”制御工学の理解を深める”が目的です。仕様は次のとおりです。3番目の仕様は「なんていいかげんな設計仕様か!」とお叱りを受けそうですが良いんです。作ってみる、完成させてみるのを優先します。

  1. 熱源としては、ペルチェとヒーシンク及びFAN使う
  2. 冷却は行わない
  3. 温度制御性能は問わない(目標温度までの到達時間とか、許容するオーバーシュート量とか制御誤差等)
  4. 電源は商用AC電源と用い、DC電源に変換して動作する
  5. Wi-Fiを通じてブラウザの画面から恒温槽内の温度モニタリングや温度制御指示が行える
  6. 温度制御方法としてPIDとON/OFF制御の選択が行え、PIDそれぞれのゲイン定数を変更できる
  7. 温度制御後に制御パラメータや温度の推移データを保存できる

3.システム構成
システム構成を示すと大きくは次の4つになります。

  1. 温度制御部(基板と、制御プログラム及びフロントエンドプログラム)
  2. 操作部(PCまたはスマートフォンのブラウザ)
  3. ペルチェユニット(ペルチェとヒートシンクとFAN)
  4. 恒温槽本体(恒温槽と制御室から成る構造物)
  5. 電源部(ACアダプタ及び電源ケーブルと端子板)

3.1 温度制御部(基板と、制御プログラム及びフロントエンドプログラム)
本稿のメインの話です。(基板については後述します。)温度制御を行う基板にはEPS32マイコン搭載の基板(ESP-WROOM-32搭載のWayinTop ESP32開発ボード)を用いました。Wi-Fiモジュールが搭載されててPWM制御可能なピンがあり、安価で入手性が良ければどんな基板でも良いです。ESP32のソフトウェアはArduinoのIDEを用いて開発及びESP32のメモリへのプログラム書き込みを行います。温度制御を行うプログラムはC言語で記述し、ユーザーインタフェースを担うフロントエンドプログラムはHTML/CSS及びJavaScriptで記述しました。それらすべてのプログラムをEPS32のメモリに書き込んで動作させます。
HTML/CSS及びJavaScriptもESP32のメモリに書き込む方法は、以下のサイトを参考にしました。大変助かりました。ありがとうございます。
参考:いなばテクノ・エボリューション株式会社 「ESP32でWebサーバーを立てよう」
ArduinoのIDEを用いて開発する拡張子がinoのc/c+ファイルと同じ場所に「data」フォルダを作成し、この中にHTML/CSS及びJavaScriptファイルを配置します。inoファイルと「HTML/CSS及びJavaScriptファイル」は別々にEPS32のメモリに書き込みます。

C:.
|   ESP32WROOM_WebserverTmpCtrl.ino  ・・制御プログラム(温調、サーバープログラム)
|
\---data
        index.html  ・・ フロントエンドプログラム
        script.js  ・・ フロントエンドプログラム
        style.css  ・・ フロントエンドプログラム

3.1.1 制御プログラム
制御プログラム(C言語)は、ペルチェへの電圧印加(PWM制御)、温度制御、ブラウザとのやりとりを行います。Arduinoプログラミングと同様に、変数やクラスの宣言文の後にsetup()関数があり、続いてloop()関数という主要構成で必要な個別の関数を定義しています。
制御プログラムは、次の機能を持ちます。今回はFANの回転数著性は行いませんでした。(FANを常時一定速度で回します)

  • 温度の読み取り ・・サーミスタ抵抗を使った回路からADCを経て電圧を読み取り温度に変換します。
  • 温度制御 ・・現在温度と目標温度を比較して、PID制御またはON/OFF制御を行うための制御量、ここではペルチェを駆動する信号であるPWM信号のデューティ比を計算します。(ON/OFF制御の場合は最大値か最小値かの判断)
  • サーバープログラム ・・ブラウザからのリクエストに応じてデータを返したり、命令や制御パラメータを受け取ります。受け取った制御パラメータは温度制御部へ渡します。

3.1.1.1 ADCの読み取り(サーミスタ回路からの電圧)
ADCの値の読み取りに関してはEPS32のメーカから提供のライブラリがあるのでArduinoのIDEからインストールしてインクルードして使用します。インクリードするヘッダーファイルとsettup()内での設定例は以下のとおりです。
ヘッダーファイルのインクリード
 #include “driver/adc.h”
 #include “esp_adc_cal.h”
setup()内で行う初期化の例
 adc_gpio_init(ADC_UNIT_1, ADC_CHANNEL_5);   // ADC1_CH5を初期化 (=GPIO33)
 adc1_config_width(ADC_WIDTH_BIT_12);      // ADC1の解像度を12bit(0~4095)に設定
 adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);  // ADC1の減衰を11dBに設定
 esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adcChar);
電圧値を読み取る関数の使い方の例
 esp_adc_cal_get_voltage(ADC_CHANNEL_5, &adcChar, &voltageESP); //ADC1_CH5 (=GPIO33)の電圧値リード
ADCの値の読み取りについては、以下のサイトを参考にしました。大変助かりました。ありがとうございます。
参考:ESP32のADCについて
上記サイトの作者が言われているようにESP32のメーカーの公式サイトを当たるのが正しいアプローチです。
なお、ESP32のメーカーの公式サイトでも示されていますが、ADCから読み取った値はかなりノイジーです。マルチサンプリングや後述するコンデンサーの追加が必要です。今回の環境では温度換算で希に1℃くらいずれた突発値が読み取れていたため、”数回読んで並べ替えて中央値を読む”という処理を入れ、その上で安定しているであろうデータ群を使って平均を求めました。突発値は減りましたがそれでも値が暴れていたので、公式マニュアルを読んでコンデンサ追加することにしました。(これは回路的に最初からケアしておかないといけませんね。)まだ、突発値が残ってます。

3.1.1.2 温度制御
温度制御部では、ペルチェに電流を与えるドライバー回路への制御信号を作ります。ソフトウェアは加温制御したいときにその程度によって制御信号を操作します。EPS32のIOにはPWM制御可能なピンがいくつか割り当てられているので、そこへから操作量に応じたPWM信号を出力します。つまり、操作量を大きくしたい(ガンガン暖めたい)ときはPWMのデューティ比を高くし、操作量を小さい(暖めるペースをゆっくりに)ときはPWMのデューティ比を小さきます。全力のときはデューティ比は100%で、操作量を0にしたいときはデューティ比を0%にします。ここではPWMの分解能を256としたので、256がデューティ比100%(ネットで見つかる複数の記事によれば、255ではないらしい。)で、0がデューティ比0%となります。

温度制御方法をON/OFF制御した場合の操作量は、256か0の2値となります。一方、温度制御法にPID制御を選んだ場合の操作量は、目標値と現在温度との差、及び過去から現在までの制御結果によって、0から256までの値とを取ります。この操作量をPWM制御用に割り当てたピンに与えることで接続先のドライバー基板を経由してペルチェに伝わる電流を制御できます。

PID制御に必要なP、I、そしてDの項のゲイン定数はブラウザから与えられる値を使って設定し、PID制御のための操作量を演算します。演算周期は50ms程度としました。これはloop()関数内(最後)のdelay()関数で指定してますが、正確にはloop()内コードの実行時間を計測してそれを反映してやる必要がありますが今回は横着して飛ばしています。

3.1.1.3 サーバープログラム
ブラウザからのリクエストに応じてデータを返したり、命令や制御パラメータを受け取ります。受け取った制御パラメータは温度制御部へ渡す処理です。こちらはHTML/CSS及びJavaScriptもESP32のメモリに書き込む方法を参考にした、「ESP32でWebサーバーを立てよう」にで説明されているように、「ESPAsyncWebServer」とそれが依存している「AsyncTCP」をインストールします。

3.1.1.3.1 WiFiのサーバー/Access Pointとしての動作するために
インクリードするヘッダーファイルとsettup()内での設定例は以下のとおりです。
ヘッダーファイルのインクリード
 #include <WiFI.h> // これは「ESPAsyncWebServer」のヘッダーファイルではない
 #include “ESPAsyncWebServer.h”
setup()内で行う初期化の例
 // WiFi-APの設定設定
 WiFi.softAP(ssid, pass); // SSIDとパスの設定
 delay(100); // このdelayを入れないと失敗する場合がある
 WiFi.softAPConfig(ip, ip, subnet);     // IPアドレス、ゲートウェイ、サブネットマスクの設定
 IPAddress myIP = WiFi.softAPIP(); // WiFi.softAPIP()でWiFi起動

 // 各種情報を表示
 Serial.print(“SSID: “);
 Serial.println(ssid);
 Serial.print(“AP IP address: “);
 Serial.println(myIP);

 // GETリクエストに対するハンドラーを登録
 // rootにアクセスされた時のレスポンス
 server.on(“/”, HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, “/index.html”);
 });
 // style.cssにアクセスされた時のレスポンス
 server.on(“/style.css”, HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, “/style.css”, “text/css”);
 });
 // script.jsにアクセスされた時のレスポンス
 server.on(“/script.js”, HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(SPIFFS, “/script.js”);
 });


ここまでで、WiFiのサーバー/Access Pointとしての準備が整いました。クライアントであるブラウザからこのSSIDに対してアクセスがあるとHTMLファイルをブラウザへ返し、更に付属するスタイルシートやJavaScriptのファイルを返すとができます。

3.1.1.3.2 ブラウザとの通信
ここまででブラウザとの通信が確立しました。しかしこれでは何もできません。ここから独自に命令やデータを受け取ったり、ブラウザへ返すデータの送信ができるよう実装します。

基板への命令は、ブラウザからは非同期通信のAjaxを用いてPOSTメソッドを使って命令やデータを送られてきます。その際に指定されるURLに応じた(この例ではURLに”/post_ctrl”が指定されています)関数を定義します。この例では、dataに命令やデータが格納されます。この例ではdataにはブラウザからPOSTされた文字列が入っています。)関数の形は以下のように記述し、受け取った命令やデータに対する必要な処理は、”//ここに処理を記述する”のところに記載します。
//デバイスへの命令に対する処理
server.on(
 ”/post_ctrl”,
 HTTP_POST,
  (AsyncWebServerRequest * request){},
 NULL,
  (AsyncWebServerRequest * request, uint8_t *data, size_t len, size_t index, size_t total) {
 // ここに処理を記述する
});

反対に、ブラウザの求めに応じて基板からデータを送るための関数は以下のように記述います。
この例では、POSTメソッドをデータ送信のリクエストが飛んでくるので、その求めに応じてデータを返します。

// デバイスへのデータ取得リクエストに対する処理
server.on(
“/post_sensor”,
HTTP_POST,
[](AsyncWebServerRequest * request){
// ここに処理を記述する。(”result”にデータを詰め込む)
request->send(200, “application/json”, result);
});

制御プログラムのコードとフロントエンドプログラムのコード一式はこちらです。


3.1.2 フロントエンドプログラム
さて、続いて、ブラウザ側で実行されるプログラムです。サーバー側である基板にこれらのファイルはあるわけですが、ブラウザからサーバーへアクセスすることでブラウザ側にダウンロードされてブラウザ上で実行されまる。 HTML/CSS及びJavaScriptで構成しました。
現在の温度は1秒ごとに更新表示されますが、これは非同期通信のAjaxというものを用いてJSONデータ形式でサーバーからデータを受け取り、温度データ部分を抜き取ってブラウザ画面上に表示しています。ついでに、PID制御の演算過程を把握すべくP、I、Dの項の計算値も1秒ごとに受け取っています。
一方、サーバーへ温度制御開始や温度制御方法及びPID制御のための各種ゲインを送信するのは、温度制御開始ボタンが押されたときのみAjaxを用いて送信しています。この命令もJSONデータ形式で送っています。


3.1.3 基板
主にESP32マイコン開発ボードと(ペルチェの)ドライバー基板、及び12v→5vのDCDCコンバーターからなります。他には抵抗とかLEDとか端子とかサーミスタ抵抗や配線も必要です。FANもPWM制御するにあたり、ESP32の3.3v系とFANが受け付ける5v系にレベルシフトさせる回路を追加しました。

ドライバー基板はTIのDRV8871が搭載された基板です。DRV8871は、DCモータを制御するためのICで入力信号をPWM制御することでモーターの回転数を調整できます。これをペルチェ素子へ印可する電圧のドライバーとして使用しました。正転・反転できますが今回は加温しかしないので一方向の制御のみで使っています。電流制限抵抗により出力電流の上限を決定できます。

主要部品リストを掲載します。

3.2 操作部(PCまたはスマートフォンのブラウザ)
これは説明不要ですね。サーバー(ESP32マイコン基板)への接続方法だけ記しておきます。サーバー(ESP32マイコン基板)の電源と投入後、PCまたはスマートフォンのWiFiネットワークの設定を開き、接続先の候補のうち、サーバープログラムで「SSID」指定した名前のサーバーに接続します。

3.3 恒温槽本体(恒温槽と制御室から成る構造物)
本記事がソフトウェアよりで、かつ、作者が機構設計のど素人なので仕方ないのですが、本来ならこれが本稿の主役な気がします。これが熱の移動主体であり、恒温したい槽とそうでない槽とを分け隔てる境界でもあります。ペルチェ素子が移動させた熱を暖めたい側の槽へ熱を届け、暖めたくない槽には熱を届けないように設計する必要があるということです。2つの槽は写真をご覧のとおり2つのケースの重ね合わせで実現しています。今回のはいい加減な設計ですが、まっとうな温調を求めるならこれらの断熱性確保は必須となります。

恒温槽本体

ペルチェユニット

ヒートシンクはFAN付きのものでパソコンのCPUボードの装着するものです。パソコンショップで手に入れます。図のようにペルチェ素子をヒートシンクとヒートシンクバックプレートで挟みます。その際に図のように2つの層を分け隔てるための板を挟み込みます。図では珪藻土の板と書いていますがこれは自宅に転がっていた板を使用したのであって、素材はそれなりに強度があって断熱性が良ければ何でも良いでしょう。

3.3 電源部(ACアダプタ及び電源ケーブルと端子板)
安定化電源にしろACアダプタにしろ出力できる電流が大きくなればなるほど高額になります。ペルチェ素子の流せる電流が小さいと加温や冷却(ここでは冷却しない)の能力が小さくなりますが、今回は勉強用の試作なので12A-3Aのもので良しとしました。FANや基板の消費電流が1A以下で消費電流の合計を4Aで見積もり、電源は5Aまで流すことのできるものを選びました。これなら2000円未満で手になります。ACアダプタの出力端子から赤黒の配線を取り出す便利な変換配線を経由して、端子板につなぎます。

4. 実験結果

5. 問題点
26℃くらいからスタートして目標温度の38℃まで12℃上昇させるのに800秒ちょっとかかっています。14分くらいですね。加熱パワーや槽の断熱性能のせいか目標温度へ到達するまでの時間が長く、ON/OFF制御でもオーバーシュートがほぼ無いですね。そしてON/OFF制御ではヒステリシスを0.15℃としましたがもう少し小さくできそうです。そうすると、この恒温槽ではPID制御しなくても良好な温調ができそうです。あぁ、せっかく、PID制御を実装したのに残念です。

6.感想
こんな小さい空間を狙った温度に保つのに、ずいぶんと難しいことがたくさんあるものだと・知らんことがいっぱいあるなと大変勉強になりました。(改良の余地だらけで終わってないけど)