昨日試した扇風機遊びを少し進め、温度制御を付けてみたい。
おあつらえ向きに、最初に買った「Seeedstudio SIDEKICK BASIC KIT」には、サーミスタが入っている。これで温度を検知して、温度ごとに扇風機の回転を制御するわけだ。値の変更などはETHERNET SHIELD 2をつなぎ、ウェブインターフェイスで行うと面白いだろう。
以前にも試したが、しかし、このキット付属のサーミスタは情報が少なくて困る。そこで、ちゃんと計算してそれなりの温度測定をしてみたい。以前は測りたい温度近傍の特性値をテーブルから拾って2次式で近似しただけであった。
スペックシートによれば、-55℃で10583.3Ω、+125℃で1.277Ωになる、とある。常温付近では0℃で190.07Ω、40℃で24.87Ωだ。もしサーミスタ直付けでArduinoから5V給電すれば、-55℃のときは0.47mAだが、0℃で26.3mA、40℃で201mAと、Arduinoに流せる電流の20mAを大きく逸脱してしまう。
40度付近でも大丈夫なように、抵抗を付加しなければならない。40℃の時に流したい電流を5mAと仮定すれば、

……というような計算で975Ωくらいつなげておけばよいということになる。
しかし、手持ちの抵抗は、キットに入っていた330Ω・1kΩ・10kΩの3種類しかないから、これで賄うしかない。
一番大きい抵抗は10kΩだから、これで計算しなおすと、40℃の時で
5V / (24.87Ω + 10kΩ) = 0.5mA
2番目は1kΩ、同じく40℃の時で
5V / (24.87Ω + 1kΩ) = 4.88mA
同様に3番目の330Ωだと
5V / (24.87Ω + 330Ω) = 14mA
…ということになる。間をとって、1kΩで回路を作ってみよう。
次に、サーミスタは抵抗から温度を知る。一方、Arduinoは電圧からデジタル値を知る。したがって、電圧からまず抵抗値を知らなければその先の温度測定に進めない。
先の電流調整用の1kΩ抵抗をR0として含め、現在のサーミスタの抵抗値を知るには、一般に式は次のようになろう。

これをソースコードに表せば、次のようになる。
const float v0 = 5.0, r0 = 1000.0; // Arduino +5Vと電流調整抵抗1kΩ const int resolution = 1024; // アナログ入力の分解能 int srcVal = 0; float vt = 0.0, rt = 0.0; srcVal = analogRead(THERMISTOR); vt = srcVal * (v0 / (resolution - 1)); rt = (v0 * r0 - vt * r0) / vt;
次に、知った抵抗値から温度を知る計算である。
サーミスタの抵抗は対数特性を持っているので、関数は対数モデルになる。また、サーミスタの特性を表す重要な値は「B値」と呼ばれるものと、Ta(温度)、R0(抵抗)の3つで、それらはスペックシートに書いてある。
TaとR0については、「温度がTaのとき、抵抗はR0になる」というものだ。私の手元のサーミスタは、スペックシートによると、
Ta = 25℃
R0 = 50kΩ
……とある。
B値は「抵抗の対数と、温度の比」である。これもスペックシートに関係式が書いてある。
これらのスペックシートの値と、知った抵抗値から温度を求める式は、次のようになる。

これを、先の抵抗値を知る部分と併せて、Arduinoで使える関数にすれば、このようになろうか。
float tempMesure(){ const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0; // MF11-503Kスペックシート記載 const float K = 273.15; // 熱力学温度の定数 const float v0 = 5.0, r0 = 1000.0; // Arduino +5Vと電流調整抵抗1kΩ const int resolution = 1024; // アナログ入力の分解能 int srcVal = 0; float vt = 0.0, rt = 0.0; srcVal = analogRead(THERMISTOR); vt = srcVal * (v0 / (resolution - 1)); rt = (v0 * r0 - vt * r0) / vt; return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K); }
これを用いて、少し真面目っぽい温度測定スケッチを書けば、次のようになる。
// // thermistor2temp.ino // サーミスタでわりと真面目に温度を測る。 // 27.7.5(日) // 佐藤俊夫 // const int THERMISTOR = 1; void setup() { Serial.begin(9600); } void loop() { float t = 0.0; t = tempMesure(); Serial.print("Temp = "); Serial.print(t); Serial.println("C"); delay(500); } float tempMesure(){ const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0; // MF11-503Kスペックシート記載 const float K = 273.15; // 熱力学温度の定数 const float v0 = 5.0, r0 = 1000.0; // Arduino +5Vと電流調整抵抗1kΩ const int resolution = 1024; // アナログ入力の分解能 int srcVal = 0; float vt = 0.0, rt = 0.0; srcVal = analogRead(THERMISTOR); vt = srcVal * (v0 / (resolution - 1)); rt = (v0 * r0 - vt * r0) / vt; return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K); }
次に、温度に応じて扇風機の回転数が変わるようにしよう。先日作った「ソリッドステート・リレーモジュール」を遺憾なく使用する。

このモジュールの内部には、秋月電子の「ソリッド・ステート・リレー(SSR)キット 25A(20A)タイプ」が組み付けてある。
27℃で1/fゆらぎエフェクト、28℃で弱風、29℃で強風、とでもしてみようか。1/fゆらぎエフェクトには、先週書いた関数をそのままコピペする。

// // thermistor2windFan.ino // サーミスタで温度を測り、扇風機を制御する。 // 27.7.5(日) // 佐藤俊夫 // const int THERMISTOR = 1, WINDFAN = 9; void setup() { Serial.begin(9600); pinMode(WINDFAN, OUTPUT); } void loop() { const int half = 128, full = 255; const float lowTemp = 27.0, midTemp = 28.0, highTemp = 29.0; float t = 0.0, f = 0.0; t = tempMesure(); Serial.print("temp="); Serial.println(t); if(t > highTemp){ analogWrite(WINDFAN, full); }else if(t > midTemp){ analogWrite(WINDFAN, half); }else if(t > lowTemp){ f = f1Fluctuation(); analogWrite(WINDFAN, f); }else{ analogWrite(WINDFAN, 0); } delay(20); } float tempMesure(){ const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0; // MF11-503Kスペックシート記載 const float K = 273.15; // 熱力学温度の定数 const float v0 = 5.0, r0 = 1000.0; // Arduino +5Vと電流調整抵抗1kΩ const int resolution = 1024; // アナログ入力の分解能 int srcVal = 0; float vt = 0.0, rt = 0.0; srcVal = analogRead(THERMISTOR); vt = srcVal * (v0 / (resolution - 1)); rt = (v0 * r0 - vt * r0) / vt; return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K); } int f1Fluctuation(){ static float x = 0.1; if(x < 0.5){ x = x + 2 * x * x; } else { x = x - 2 * (1.0 - x) * (1.0 - x); } if(x < 0.05 || x > 0.995){ x = random(10, 90) / 100.0; } return((int)(x * 255)); }
回路図など、描くも愚かというか、こんな簡単なものである。

さて次に、ETHERNET SHIELD 2をつなぎ、この扇風機をWeb化する。昨日買っておいた「クリアランス確保用ピンソケット」が役に立つ。

組み付けるとこんな感じである。回路はネットにつながない場合と同じでよい。

こんなインターフェイスで動かす。

設定した温度になると扇風機が回る。

スケッチはこのようになる。
// // web2thermistor_windFan.ino // サーミスタで温度を測り、扇風機を制御する。 // 27.7.5(日) // 佐藤俊夫 // #include <SPI.h> #include <Ethernet2.h> // byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 }; IPAddress ip(192, 168, 1, 129); EthernetServer SERVER(80); EthernetClient CLIENT; const int THERMISTOR = 1, WINDFAN = 9; // void setup() { Ethernet.begin(mac, ip); SERVER.begin(); Serial.begin(9600); pinMode(WINDFAN, OUTPUT); } void loop() { const int half = 128, full = 255; static float lowTemp = 27.0, midTemp = 28.0, highTemp = 29.0; float t = 0.0, f = 0.0; // Webサーバの動作 char c; String rstr = ""; CLIENT = SERVER.available(); if (CLIENT) { while (CLIENT.connected()) { if (CLIENT.available()) { c = CLIENT.read(); rstr += c; if(rstr.endsWith("\r\n")){ break; } } } if(rstr.indexOf("low=") >= 0){ lowTemp = rstr.substring(rstr.indexOf("low=") + 4, rstr.indexOf("&green=")).toInt(); midTemp = rstr.substring(rstr.indexOf("mid=") + 4, rstr.indexOf("&blue=")).toInt(); highTemp = rstr.substring(rstr.indexOf("high=") + 5, rstr.indexOf("&end")).toInt(); } rstr = ""; sendform(lowTemp, midTemp, highTemp); delay(1); // close the connection: CLIENT.stop(); } // 温度測定 t = tempMesure(); Serial.print("temp="); Serial.println(t); if(t > highTemp){ analogWrite(WINDFAN, full); Serial.println("high"); }else if(t > midTemp){ analogWrite(WINDFAN, half); }else if(t > lowTemp){ f = f1Fluctuation(); analogWrite(WINDFAN, f); }else{ analogWrite(WINDFAN, 0); } delay(20); } float tempMesure(){ // Seeedstudio SIDEKICK BASIC KIT付属のサーミスタ「MF11-503K」で // 温度を測る。 const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0; // MF11-503Kスペックシート記載 const float K = 273.15; // 熱力学温度の定数 const float v0 = 5.0, r0 = 1000.0; // Arduino +5Vと電流調整抵抗1kΩ const int resolution = 1024; // アナログ入力の分解能 int srcVal = 0; float vt = 0.0, rt = 0.0; srcVal = analogRead(THERMISTOR); vt = srcVal * (v0 / (resolution - 1)); rt = (v0 * r0 - vt * r0) / vt; return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K); } int f1Fluctuation(){ // 間欠カオス法により0~255の間で1/fゆらぎを生成して返す。 static float x = 0.1; if(x < 0.5){ x = x + 2 * x * x; } else { x = x - 2 * (1.0 - x) * (1.0 - x); } if(x < 0.05 || x > 0.995){ x = random(10, 90) / 100.0; } return((int)(x * 255)); } void sendform(float lowTemp, float midTemp, float highTemp){ // フォームを送る。 char* formFirstHalf[] = { "<html>", " <head>", " <meta charset=\"utf-8\">", " </head>", " <body bgcolor='#ddddff'>", " <center>", " <h1>Arduino ネット扇風機</h1>", " <form method='GET'>", " <table>", " <tr>", " <th>ゆらぎ送風温度</th>", " <th>弱風温度</th>", " <th>強風温度</th>", " </tr>" }; // 14 num. char* formSecondHalf[] = { " </table>", " <input type='hidden' name='end'>", " <input type='submit' value='セット'>", " </form>", " </center>", " </body>", "</html>" }; // 7 num. int i = 0; CLIENT.println("HTTP/1.1 200 OK"); CLIENT.println("Content-Type: text/html"); CLIENT.println("Connection: close"); CLIENT.println(); CLIENT.println("<!DOCTYPE HTML>"); for(i = 0; i < 14; i++){ CLIENT.println(formFirstHalf[i]); } CLIENT.println(" <tr>"); CLIENT.println(" <td>"); CLIENT.println(" <input type='text' name='low' size='6em' value ="); CLIENT.print(lowTemp); CLIENT.println(">"); CLIENT.println(" </td>"); CLIENT.println(" <td>"); CLIENT.println(" <input type='text' name='mid' size='6em' value ="); CLIENT.print(midTemp); CLIENT.println(">"); CLIENT.println(" </td>"); CLIENT.println(" <td>"); CLIENT.println(" <input type='text' name='high' size='6em' value ="); CLIENT.print(highTemp); CLIENT.println(">"); CLIENT.println(" </td>"); CLIENT.println(" </tr>"); for(i = 0; i < 7; i++){ CLIENT.println(formSecondHalf[i]); } }
「ウェブ扇風機」への2件のフィードバック