【キルミーベイベー】あぎりさんの部屋にある装置をつくってみた

はじめに

キルミーベイベー第6話にて…

やすな「からくりっぽいのとかは無いんですか?」
あぎりさん「ありますよ~」
「この置物を動かすと~
置物(ヤッベー)
「クーラーがつきます♪」

あぎりさんの部屋にあった置物のイメージ

何この装置…欲しい…
ということで今回はこれを作っていこうと思います。

基本構想

  • エアコンは赤外線を使ってつける
  • 赤外線送信機と置物は分けて作る
  • 赤外線送信機と置物はWiFi通信でつなぐ
  • 置物を動かしたらWiFiで送信機に指令を送る→送信機がエアコンをつける

赤外線送信機と置物は一体で作ることもできますが(そっちのほうが安いし簡単)、どうしても赤外線がエアコンに届くように配置を工夫する必要がありますし、置物の正面に立つと使えなかったりするのでここはWiFiを使っています。
それにLEDむき出しだとなんか凄さが半減して嫌じゃないですか(笑)

作りたい装置のイメージ

必要なもの

  • 適当な置物
  • 回転センサ(10kΩ可変抵抗器、10kでなくても可)
  • 10kΩ抵抗器(アナログ入力が最大1Vの場合のみ必要。可変抵抗の2倍)
  • ESP8266モジュール(NodeMCU ESP8266 Board)×2
  • 赤外線LED
  • トランジスタ(2CS1815)
  • 200Ω抵抗器(あったほうがいいけどなくても可)

まず置物側ですが、置物本体に回転によって抵抗値が変わる可変抵抗器を使いたいと思います。可変抵抗器は別名ボリュームともいい、昔のテレビやラジオのダイヤルやアンプの音量調節など、ダイヤル状のものにはまず使われているであろう基本的な部品です。これを使って置物が一定以上回ったらWiFi信号を送るデバイスを作ろうと思います。
可変抵抗と普通の抵抗は10kΩと書きましたが別に1kΩでもいいです。

次に送信機側ですが、赤外線LEDの他に抵抗とトランジスタを使って赤外線が最大限届くようにします。

必要なものリスト(AMAZON)

使う部品リストを載せておきます。ただ、抵抗やトランジスタはAmazonだとバラ売りされていないので、お近くの電子部品の販売店に行かれる方が絶対良いです。東急ハンズとかでも売ってます。逆にESP8266モジュールはネットで買った方が安くておすすめです。

置物側の設計

置物を回転させたらWiFiを通じてリクエストを送信する装置を作ります。

必要なものは、ESP8266と回転によって抵抗が変化する可変抵抗、確認用のLEDです。

回転を検知する可変抵抗

可変抵抗とESP8266モジュールのつなぎ方

可変抵抗の仕組みは簡単です。両端に電圧をかけると、(抵抗が一様ならば)その間で電圧は一様に降下します。可変抵抗を回転させるとこの抵抗のどこを触るかが変えられるので、電圧をESP8266で読み取るだけで回転量が分かります。

可変抵抗の仕組み

アナログ入力の上限が1VのESP8266を使う場合

僕のESP8266モジュールはアナログ入力に0~3.3Vが使えるのですが、アナログ入力が0~1VのESP8266もあります。そういう場合は次のように20kΩの抵抗を入れることによって出力の範囲を変えることが出来ます。

出力の範囲を変える方法

組み立て

100均の木箱のフタを利用しました
見た目がスッキリするように
適当な置物をくっつけて完成!

Arduinoソースコード(置物側)

回転を検知→HTTP GETリクエストを送信します。

/* 
 *  あぎり「この置物を動かすと~
 *  クーラーが付きます♪
 *  (具体的な動作:可変抵抗の値に応じてHTTPリクエストを送る)
 */
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266HTTPClient.h>
#include <mDNSResolver.h>       //https://github.com/madpilot/mDNSResolver

#define WIFI_SSID "********"
#define WIFI_PWD "****"
#define TARGET_HOSTNAME "ir-sender.local"
String host_ip = "";


// 前回送ったコマンドを記憶
#define STATE_OFF 0
#define STATE_ON 1
int state = STATE_OFF;

// ONまたはOFFのしきい値
// 置物を回したときのセンサ値を見て、いい感じの値に設定
#define ON_THRESH 220
#define OFF_THRESH 240

// 赤外線LEDの+側
#define LED_PIN 14

// 名前解決してくれる(ir-sender.local → 192.168.0.22)
WiFiUDP udp;
mDNSResolver::Resolver resolver(udp);

String ip_to_string(IPAddress ip){
  return 
  String(ip[0]) + String(".") +
  String(ip[1]) + String(".") +
  String(ip[2]) + String(".") +
  String(ip[3]);
}

String getPageSource(String host) {
  HTTPClient http;
  
  http.setTimeout(500);
  http.begin(host);
  
  int httpCode = http.GET();
  
  String result = "";

  if (httpCode < 0) {
    result = http.errorToString(httpCode);
  } else if (http.getSize() < 0) {
    result =  "size is invalid";
  } else {
    result = http.getString();
  }

  http.end();
  return result;
}

void wake_wifi(){
  // 原因未解明!
  // WiFi接続が確認できるまで待つ。これがないとGETが失敗することがある。
  while(WiFi.status() != WL_CONNECTED){
    delay(100);
    Serial.print(".");
  }
}

String get_ip(char* host){
  IPAddress ipaddr = resolver.search(host);
  return ip_to_string(ipaddr);
}

void setup() {
  Serial.begin(115200);
  delay(200);
  
  WiFi.begin(WIFI_SSID, WIFI_PWD);
  
  wake_wifi();
  
  
  Serial.println("");
  Serial.println("Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());
  
  // DNSの名前解決は最初の一回だけ行う
  host_ip = get_ip(TARGET_HOSTNAME);

  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  
  int read_val = analogRead(A0);
  //Serial.print("analog read = ");
  //Serial.println(read_val);

  if(state == STATE_OFF && read_val < ON_THRESH){
    // OFF→ONになったとき、GETリクエストを送る
    digitalWrite(LED_PIN, HIGH);

    wake_wifi();  // WiFiが使えるようになるまで待つ必要あり?未解明
    String get_result = getPageSource(String("http://") + host_ip + String("/on"));
    //Serial.println(get_result);
    
    digitalWrite(LED_PIN, LOW);

    state = STATE_ON;
  }else if(state == STATE_ON && read_val > OFF_THRESH){
    // ON→OFFになったとき、GETリクエストを送る
    digitalWrite(LED_PIN, HIGH);

    wake_wifi();  // WiFiが使えるようになるまで待つ必要あり?未解明

    
    String get_result = getPageSource(String("http://") + host_ip + String("/off"));
    //Serial.println(get_result);

    digitalWrite(LED_PIN, LOW);

    state = STATE_OFF;
  }

  delay(10);
}

赤外線LED側の設計

WiFiリクエストを受け取って、エアコン用の赤外線を出す装置を作ります。必要なものはESP8266と、赤外線LED、電流増幅用のトランジスタと抵抗です。

ソースコード(赤外線LED側)

WiFiリクエストを受け取って何かをするのはWebServer.hを使います。まずは→ライブラリ管理→でESP8266WebServerと検索してライブラリをダウンロードします。

赤外線の送信は「」というライブラリを使いました。こちらはTOSHIBA製や日立製など主要なエアコン製品の赤外線信号を提供してくれているので、好きなコマンドを簡単に送ることが出来ます。

自分が持っているリモコンの信号を覚えさせてそれを再生する方法もありますが、全部のボタンを覚えさせるのが手間なのと、たまに送信ミスすることがあったのでこちらを使うことにしました。

/* IRremoteESP8266: IRsendDemo - demonstrates sending IR codes with IRsend.
 *
 * Version 1.1 January, 2019
 * Based on Ken Shirriff's IrsendDemo Version 0.1 July, 2009,
 * Copyright 2009 Ken Shirriff, http://arcfn.com
 *
 * An IR LED circuit *MUST* be connected to the ESP8266 on a pin
 * as specified by kIrLed below.
 *
 * TL;DR: The IR LED needs to be driven by a transistor for a good result.
 *
 * Suggested circuit:
 *     https://github.com/markszabo/IRremoteESP8266/wiki#ir-sending
 *
 * Common mistakes & tips:
 *   * Don't just connect the IR LED directly to the pin, it won't
 *     have enough current to drive the IR LED effectively.
 *   * Make sure you have the IR LED polarity correct.
 *     See: https://learn.sparkfun.com/tutorials/polarity/diode-and-led-polarity
 *   * Typical digital camera/phones can be used to see if the IR LED is flashed.
 *     Replace the IR LED with a normal LED if you don't have a digital camera
 *     when debugging.
 *   * Avoid using the following pins unless you really know what you are doing:
 *     * Pin 0/D3: Can interfere with the boot/program mode & support circuits.
 *     * Pin 1/TX/TXD0: Any serial transmissions from the ESP8266 will interfere.
 *     * Pin 3/RX/RXD0: Any serial transmissions to the ESP8266 will interfere.
 *   * ESP-01 modules are tricky. We suggest you use a module with more GPIOs
 *     for your first time. e.g. ESP-12 etc.
 */

#ifndef UNIT_TEST
#include <Arduino.h>
#endif
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <ir_Toshiba.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

#define WIFI_SSID "Buffalo-G-4310"
#define WIFI_PWD "buf50317"
#define DNS_NAME "ir-sender"

#define HTML_HEADER "<!doctype html>"\
  "<html><head><meta charset=\"UTF-8\"/>"\
  "<meta name=\"viewport\" content=\"width=device-width\"/>"\
  "</head><body>"
#define HTML_FOOTER "</body></html>"

ESP8266WebServer server(80);

const uint16_t kIrLed = 4;  // ESP8266 GPIO pin to use. Recommended: 4 (D2).
IRToshibaAC ac(kIrLed);  // Set the GPIO to be used for sending messages.

void printState() {
  // Display the settings.
  Serial.println("Toshiba A/C remote is in the following state:");
  Serial.printf("  %s\n", ac.toString().c_str());
  // Display the encoded IR sequence.
  unsigned char* ir_code = ac.getRaw();
  Serial.print("IR Code: 0x");
  for (uint8_t i = 0; i < kToshibaACStateLength; i++)
    Serial.printf("%02X", ir_code[i]);
  Serial.println();
}

void handle_root(){
  String str = HTML_HEADER
    "<h1>I'm IR sender!</h1>"
    "<h2>Usage</h2>"
    "<h3>/</h3>"
    "このページです。<br>"
    "<h3>/set?temp=22&fan=0&mode=a</h3>"
    "デフォルトのパラメータを設定します。<br>"
    "<h3>/state</h3>"
    "デフォルトのパラメータを表示します。<br>"
    "<h3>/on?temp=22&fan=0&mode=a または /on</h3>"
    "エアコンONを送信します。クエリをつけるとそれをデフォルトのパラメータに設定して起動します。<br>"
    "クエリをつけないと以前に設定したパラメータを使います。<br>"
    "<h3>/off</h3>"
    "エアコンOFFを送信します。<br>"
    HTML_FOOTER;

  server.send(200, "text/html", str);
}

void handle_set(){
  for (int i = 0; i < server.args(); i++) {

    if(server.argName(i) == "temp"){
      ac.setTemp(server.arg(i).toInt());
    }else if(server.argName(i) == "fan"){
      ac.setFan(server.arg(i).toInt());
    }else if(server.argName(i) == "mode"){
      if(server.arg(i) == "a") ac.setMode(kToshibaAcAuto);
      if(server.arg(i) == "h") ac.setMode(kToshibaAcHeat);
      if(server.arg(i) == "c") ac.setMode(kToshibaAcCool);
      if(server.arg(i) == "d") ac.setMode(kToshibaAcDry);
    }
  
  }
}

void handle_on(){
  if(server.args() > 0)
    handle_set();
  
  ac.on();
  ac.send();

  String str = HTML_HEADER + ac.toString() + HTML_FOOTER;
  server.send(200, "text/html", str);
}

void handle_off(){
  ac.off();
  ac.send();

  String str = HTML_HEADER + ac.toString() + HTML_FOOTER;
  server.send(200, "text/html", str);
}

void setup() {

  ac.begin();
  Serial.begin(115200);
  delay(200);
  
  WiFi.begin(WIFI_SSID, WIFI_PWD);
  
  // Wait until WiFi is connected
  Serial.println("");
  while(WiFi.status() != WL_CONNECTED){
    delay(1000);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.println("Connected!");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin(DNS_NAME)) {
    Serial.println("MDNS responder started");
  }

  // Setup WebServer Handlers
  server.on("/", handle_root);
  server.on("/on", handle_on);
  server.on("/off", handle_off);
  server.on("/set", handle_set);

  server.on("/state", [](){
    String str = HTML_HEADER + ac.toString() + HTML_FOOTER;
    server.send(200, "text/html", str);
  });

  server.begin();
}

void loop() {
  
  server.handleClient();
  MDNS.update();
}

回路図

よくあるLEDを光らせる回路と同じです。僕が買った赤外線LEDは3.3Vでは光が弱すぎてエアコンまで届かなかったので、5VをトランジスタでON/OFFするようにしています。

赤外線LEDをトランジスタでON/OFFするだけ
基盤もなしでそのまま配線しちゃいます

また、このままだと5VがLEDに直接かかっているので、電流が流れすぎてLEDを壊す可能性がありますが、たくさん電流を流したほうが赤外線がよく届くのであえて抵抗を入れていません。

今のところ問題なく動いていますし、LEDが光るのは一瞬なので大丈夫だとは思いますが、最初は抵抗をはさみつつテストする方が良いでしょう。

適当な箱に入れて目立たなくする

置物側を作るときに使った箱のもう半分を利用して適当に作ってみました。木の箱に入れるだけでちゃんとしてる感が出ますね!

LEDやトランジスタにジャンパワイヤを切ってハンダ付け。テープで巻いときゃなんとかなる。
木箱からのぞくLED…

完成!

置物を目立つところに、LEDの箱を目立たないところに置いておきましょう。置物を動かすと…クーラーが付きます♪

以上です。

コメントを残す

メールアドレスが公開されることはありません。