昨日試した扇風機遊び を少し進め、温度制御を付けてみたい。
おあつらえ向きに、最初に買った「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化する。昨日買っておいた「クリアランス確保用ピンソケット」が役に立つ。
組み付けるとこんな感じである。回路はネットにつながない場合と同じでよい。
こんなインターフェイスで動かす。
設定した温度になると扇風機が回る。
VIDEO
スケッチはこのようになる。
//
// 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]);
}
}