Arduino + Ethernet シールドで自動カーテン

お手軽なマイコンボードArduinoを使って我が家のカーテンをiPhoneから開閉、タイマー動作できるようにしました。

以下詳細を。

もちべーしょん

  • 朝カーテンが自動で開いて太陽の光で目覚めたい!
  • ArduinoEthernetシールドを使ってPCを介さずにネット接続したい

の2点

参考サイト

今回主に参考にしたサイト

カーテン駆動のハード部についてはほぼ上記サイトのまま。

Arduino + Ethernetシールドの部分は上記サイトを参考に今回の用途に合わせて若干変更。

システム構成

ざくっと下図のような感じ。

カーテン駆動部(ギア+モータ+チェーン)+モータ制御部+ネット接続部+コントローラ
カーテンレールの上にチェーンをかけてこれをモータで駆動。モーターの駆動はモータドライバをArduinoで制御。Arduinoへの指令はEthernetシールドを使ってLAN経由でiPhoneまたはPCから。

ハードウェア

  • カーテン駆動部


構成はほぼ前述のサイトの通り。
モータの出力をギアボックスで低速高トルク化してラダーチェーンを引っ張ります。
ギアボックスはタミヤシングルギアボックスを使用。
安価だし変則比も大きくてお手軽。
ラダーチェーンとスプロケットここから購入。まあどこでも手に入るとは思います。
コスト的にはラダーチェーンはちょっと高いんですが確実な動力伝達を優先ということで。

  • モータ制御部


モータ制御用IC TA7257Pを使用。DC3VのACアダプタからの出力のON・OFFおよび正逆を制御。
詳しい使い方などは建築発明工作ゼミ2008: Arduino 小型DCモータ/TA7291Pを参考にすると分かり易いかと。

  • ネット接続部


これは市販のEthernetシールドをArduinoにのっけるだけ。

  • コントローラ部

コントローラといってもLAN経由で操作するだけなのでiPhone又はPCで。

ソフトウェア

さて、ここからがほぼ本題。まずどうやってiPhone or PCから制御するかですが、これはArduinoでWebサーバを構築してそこにGETするという方法をとりました。
なんだかもっと賢そうな方法がありそうですが思いつかなかったので力技で。

というわけで以下がサーバー部の記述の一部。
基本的にはクライアントからの接続があったらsever.println()でひたすらHTMLを返します。

//HTTPレスポンス(返信)
server.println("HTTP/1.1 200 OK");//リクエスト成功
server.println("Content-Type: text/html");//HTML文書形式
server.println();//空白行を入れる
//タイトルバー表示
server.println("<!DOCTYPE html><html><head>");        
server.println("<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">");
server.println("<link rel=\"apple-touch-icon\" href=\"http://taiiku-suwari.moo.jp/auto_curtain/curtain_pic/icon.png\">");
server.println("<script type=\"text/javascript\" src=\"http://XXXX.web.fc2.com/glossy.js\"></script>");
server.println("<title>Curtain Controller</title>");
server.println("</head><body style=\"font-size:30px;\">");
server.println("<a href=\"http://192.168.0.9/\">Curtain Controller!</a>");
・
・
・
・

で、iPhone上で見たのが下図。(一応スマートフォン用にwidthとか合わせてます。)

ここで"OPEN" "CLOSE" "STOP"ではそれぞれGETで open または close または stop の値を送信します。
例えばこんな感じ。

<a href=\"http://192.168.0.9/?state=close\">

ブラウザから送られたHTMLリクエストをシリアル通信ですべて確認すると以下の通り。

GET /?state=open HTTP/1.1
Host: 192.168.0.9
Connection: keep-alive
Referer: http://192.168.0.9/
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 Safari/534.13 ChromePlus/1.6.0.0
Accept-Encoding: gzip,deflate,sdch
Accept-Language: ja,en-US;q=0.8,en;q=0.6
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.3

この中で"?state="以下の部分だけ取り出したい訳ですがどうするか。
取った手法は一字一字チェックし"?"が来たら改行までStringに格納。
その中でopen等の文字の有無をチェックし判断。命令に合わせてモータドライバに指令を送るという方法。
うん、スマートじゃない。ま、動けばいっか。

 //GETのデータ取り出し
if(get_flag){
   if(c != '\n'){
      buf1 += c;    //?以降の文字列をラインフィードまで加える 
   }else{
      get_flag = false;
   }
}
if(c == '?' && buf1 == ""){ 
   get_flag = true;
}

//HTTPリクエストにラインフィード(改行)があり、
//現在空白行である場合
if (c == '\n' && current_line_is_blank) {
          
   //カーテンの制御
   if(buf1.indexOf("open") != -1){
       o_curtain();   //カーテン開ける
       count = 0;
    }else if(buf1.indexOf("close") != -1){
       c_curtain();   //カーテン閉める
       count = 0;
    }else if(buf1.indexOf("stop") != -1){
       s_curtain();   //ストップ
       count = 0;
    }
}


タイマー予約を行うときも同様です。formからGETで時間と分をtimer_hとtimer_mに格納して送信。
またHTMLリクエストを逐次チェックし"?"が見つかったら以下同様にしてタイマーの時間と分を変数に入力。ただし送られてくる変数が一桁だったり二桁だったりするのでこれまた力技で分解。

if(buf1.indexOf("timer_h") != -1){
   set_hour_1 =(int) buf1.charAt(buf1.indexOf("timer_h") + 8);
   set_hour_2 =(int) buf1.charAt(buf1.indexOf("timer_h") + 9);
   //set_hour_2は”&”か数値
   if(set_hour_2 != 38){  //&以外なら
      set_hour_1 -= 48;
      set_hour_2 -= 48;
      set_hour = 10*set_hour_1 + set_hour_2;
    }else{  //&なら
      set_hour_1 -=48;
      set_hour = set_hour_1; 
    }
    set_minute_1 =(int) buf1.charAt(buf1.indexOf("timer_m") + 8);
    set_minute_2 =(int) buf1.charAt(buf1.indexOf("timer_m") + 9);
    if(set_minute_2 != 38){  //&以外なら
      set_minute_1 -= 48;
      set_minute_2 -= 48;
      set_minute = 10*set_minute_1 + set_minute_2;
    }else{  //&なら
      set_minute_1 -=48;
      set_minute = set_minute_1; 
    }
}
  • タイマーのチェック

肝心のタイマー動作です。まずどうやって正しい時間を知らせるか。これは日に一回NTPサーバにアクセスするという方法をとりました。
そしてその後はその時間を基準としてDateライブラリで時間をチェック。
NTPサーバへのアクセス方法やDateライブラリの使い方はArduinoのライブラリ中のサンプルを参考にほぼそのままです。以下タイマー動作部分。単純。

//時間が一致したら
if(set_hour == hour()){
   //かつ分が一致したら
   if(set_minute == minute()){
      //カーテン開ける
      o_curtain();
      count = 0;
      //タイマーのオフ(hour()は0-23なので24にはならない)
      set_hour = 24;
      set_minute = 60;
   }
}

動作

何はともあれ完成したカーテンの動画を。
D
とまあこんな感じで動作はゆっくりだし音はうるさいしですが、目覚ましとしての機能は十分果たしてくれそうです。

今回初めてEthernetシールドを扱ったのですが、思ってた以上に簡単という印象でした。
また何かネットに繋がるガジェットを作るときは使ってみようかなあ。でもmbedも興味あるし…。んー、悩ましい。

しかし自分の備忘録とはいえ技術的には何も大した事ないんだよなあこれ。