ウェブ扇風機

投稿日:

 昨日試した扇風機遊びを少し進め、温度制御を付けてみたい。

 おあつらえ向きに、最初に買った「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と仮定すれば、

IMG_3075

……というような計算で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として含め、現在のサーミスタの抵抗値を知るには、一般に式は次のようになろう。

IMG_3079

 これをソースコードに表せば、次のようになる。

  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値は「抵抗の対数と、温度の比」である。これもスペックシートに関係式が書いてある。

 これらのスペックシートの値と、知った抵抗値から温度を求める式は、次のようになる。

IMG_3080

 これを、先の抵抗値を知る部分と併せて、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);
}

 次に、温度に応じて扇風機の回転数が変わるようにしよう。先日作った「ソリッドステート・リレーモジュール」を遺憾なく使用する。

ソリッドステート・リレーモジュール
IMG_3081

 このモジュールの内部には、秋月電子の「ソリッド・ステート・リレー(SSR)キット 25A(20A)タイプ」が組み付けてある。

 27℃で1/fゆらぎエフェクト、28℃で弱風、29℃で強風、とでもしてみようか。1/fゆらぎエフェクトには、先週書いた関数をそのままコピペする。

IMG_3083

//
//  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));
}

 回路図など、描くも愚かというか、こんな簡単なものである。

IMG_3084

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

IMG_3067

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

IMG_3087

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

ネット扇風機インターフェイス

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

IMG_3088

 スケッチはこのようになる。

//
//  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]);
  }

}






投稿者: 佐藤俊夫

 50代後半の爺。技術者。元陸上自衛官。2等陸佐で定年退官。ITストラテジストテクニカルエンジニア(システム管理)基本情報技術者

「ウェブ扇風機」への2件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください