うごくものをつくる

個人的な学習ノート

工作物

iPhoneとESP32で作る1000円スマートリモコン

※本記事はelchika様のハードウェア作品投稿キャンペーンにも掲載した内容となります。記事はこちら

会社の同僚の間で、Amazon アレクサやGoogle Homeのようなスマートスピーカーを導入している人が増えている。音声認識での家電操作には憧れるけど我が家にスマートスピーカーは無いし、音声に反応してくれる機械すら最近買ったiPhoneしかない。

「Hey, Siri. 電気つけて」

訳:まず対象製品を買ってね

iPhoneだけではどうにもならず、HomeKitアクセサリなどの家電コントロールのための機械を別途用意する必要がある。例えばこれとか↓

Nature スマートリモコン Nature Remo mini 2 ネイチャーリモミニ2 Remo-2W2 Alexa/Google Home/Siri対応

こういうのを買えば声で家電操作なんてすぐにできるんだろうけど、ただ買って導入するだけというのもちょっと面白くないな。

よし、自分でつくろう。

※本記事の回路・プログラムの確実な動作保証は致しかねます。

概要

今回は以下のような作戦で作る。

① Wi-Fi対応マイコンをWebサーバーとして稼働させる。
② ①のサーバーにはあらかじめ家電用リモコンの赤外線発光パターンを覚えさせておく。
③ Siriの音声認識機能を使って、iPhoneが①のサーバーにアクセスする。
④ アクセスされたサーバー(マイコン)は赤外線LEDでリモコンの信号を発信する。
⑤ 家電が動く。(完成)

Webサーバーの設置(ハードウェア編)

Webサーバーと言っても大掛かりなものは必要ない。

使うのはESP32と呼ばれるマイコンモジュール ※画像は秋月電子通商より

https://akizukidenshi.com/catalog/g/gM-15675/

正式名称はESP32-WROOM-32で、だいたい500円しないくらいの値段で売っている。秋月電子通商では360円。※2021年1月5日時点

これを使えば良いんだけど、ちょうど我が家に手持ちであったのはちょっと型番の違うESP32-WROVER-Bという製品

https://akizukidenshi.com/catalog/g/gM-13689/

ESP32にはいくつか亜種があるけどみんなまとめてESP32と呼ばれてるっぽい。

WROVERの方はWROOMに比べて、値段はほんのちょっと高い(秋月では530円だった)。その分PSRAMってのが搭載されているんだけど今回の用途では使わないので、そのメリットはあまり活かせず正直もったいない。でもわざわざ今からWROOMの方を買うのも面倒なのでこちらを使う。チップの形状がちょっと違うくらいで、使い方は変わらない(はず)。

安くて小さいけど高性能だし見た目のメタル感もかっこいいよね。

裏面には各ピン名称が書かれてあるので、「あれ?電源はどこに繋ぐんだったっけ?」と思ったら裏を見れば良い。

側面の金色の部分が端面スルーホールになっているので、ここに配線をはんだ付けしていく。

↑Espressif社データシートを基に説明を記載した。

使うピンは赤矢印で示したものだけなので、チップにはんだ付けするのは11箇所のみ。電源関連、プログラム書き込み関連、状態お知らせ用LED, 家電操作のための赤外線LEDの4機能のみピックアップした。かなりピンは余るのでもったいない気はするけど、たかが500円なので気にせず使っていく。

まずは側面に線をはんだ付けして、位置決めのために基板に両面テープで貼ってみた。1.27mmピッチ端面スルーホールのはんだ付けは細かい作業だけど、手作業できないくらい難しいというわけでもない。配線はそのへんにあった余り物を使ったので、色使いにはルールもへったくれもない。

こうしてつけた配線を、さらに回路として組んでいく。最低限の機能しか実装しないので、拍子抜けするほどシンプルな回路になった。電源部に使った3.3Vレギュレータは手元にあったNJU7223F33にした。これはESP32の消費電力から考えるとちょっと非力なように思えるので、マイコンにさせる仕事次第では電源能力不足で強制リセットかかってまともに動かないかも。その時はその時でもっと良いレギュレータで組み直すとして、とりあえず今回は70mFというなかなか大きな静電容量を持つ電気2重層コンデンサを並列に入れることで、電流不足をうまいこと補ってくれることを祈る。(なんとなく買ったけどずっと眠らせてた70mFコンデンサをようやく使った)

赤外線はいろんな角度に飛ばしたいので、赤外線LEDは3つにしてまとめて設置したあと、上から焦電センサ用のフレネルレンズをキャップのようにかぶせる。

本来は人感センサの感知範囲を広げるためのものだけど、やってることは光の拡散なのでこういう使い方もできる。

これで回路は完成。

Webサーバーの設置(ファームウェア編)

ESP32マイコンはArduinoの開発環境でファームを組むことができる。おまけにライブラリやサンプルが非常に多いので、それらを継ぎ接ぎして集めて自分用に組めばプログラムは完成する。

そうして作ったのが下記のコードである。

#include <Arduino.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>

WebServer server(80); //ポートは80番を使用
const char *serverName = "espremo";

//IRremoteに関する設定
const uint16_t IR_pin = 4;//IO4ピンで赤外線LEDを使う。
IRsend irsend(IR_pin);

const char *ssid = "自宅の無線LANルーターのSSID";
const char *password = "自宅の無線LANのパスワード";
const char *FWver = "Ver. 1.0";

void living_on();
void living_off();
void rootConnect();
void info();
void error();

void setup()
{
  irsend.begin();
  Serial.begin(115200);
  //WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) //WiFiに接続完了していない間…
  {
    //何もせず接続待ち待機
  }
  Serial.println("WiFi connected!");
  Serial.print("My IP address is   ");
  Serial.println(WiFi.localIP());
  Serial.println(" ");
  
  //↓このあとのOTAに関する処理でサーバー名を勝手に変えられてしまうので、先に所望の名前でOTAの設定をしておく
  ArduinoOTA.setHostname(serverName);

  if (!MDNS.begin(serverName))
  {
    Serial.println("mDNS Failed");
    while (1);
  }
  MDNS.addService("http", "tcp", 80);//Webサーバーを開始
  Serial.print("My server Name is,");
  Serial.println(serverName);

  server.on("/", rootConnect);
  server.on("/livingon", living_on);
  server.on("/livingoff", living_off);
  server.on("/info", info);
  server.onNotFound(error);
  server.begin();

  //OTAに関する初期設定
  ArduinoOTA
      .onStart([]() {
        String type;
        if (ArduinoOTA.getCommand() == U_FLASH)
          type = "sketch";
        else // U_SPIFFS
          type = "filesystem"
        // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
        Serial.println("Start updating " + type);
      })
      .onEnd([]() {
        Serial.println("\nEnd");
      })
      .onProgress([](unsigned int progress, unsigned int total) {
        Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
      })
      .onError([](ota_error_t error) {
        Serial.printf("Error[%u]: ", error);
        if (error == OTA_AUTH_ERROR)
          Serial.println("Auth Failed");
        else if (error == OTA_BEGIN_ERROR)
          Serial.println("Begin Failed");
        else if (error == OTA_CONNECT_ERROR)
          Serial.println("Connect Failed");
        else if (error == OTA_RECEIVE_ERROR)
          Serial.println("Receive Failed");
        else if (error == OTA_END_ERROR)
          Serial.println("End Failed");
      });
  ArduinoOTA.begin();

  //ここから下にその他の初期設定
  pinMode(2, OUTPUT);
  for (int i =0;i<6;i++)//設定終了をLED点滅で知らせる
  {
    digitalWrite(2,HIGH);
    delay(50);
    digitalWrite(2,LOW);
    delay(50);
  }
  Serial.println("Setup finished");
}

void loop()
{
  server.handleClient(); //Webサーバーへのアクセスを確認する処理
  ArduinoOTA.handle();//OTAのための待受処理
  //その他の普通のloop処理があれば以下に書く
}

void living_on()//リビングの照明(アイリスオーヤマ、リモコン1)のオン信号
{
  server.send(200, "text/plain", "OK");//GETリクエストへの返答
  uint16_t rawCh1On[85] = {2250, 600, 5650, 750, 1700, 250, 1700, 250, 700, 300, 700, 250, 700, 300, 700, 300, 650, 350, 650, 300,
                           650, 350, 1600, 350, 1600, 400, 600, 350, 650, 350, 650, 350, 600, 400, 1600, 350, 600, 350, 1600, 400, 600, 350, 650, 350, 650, 350,
                           600, 400, 600, 350, 650, 350, 650, 350, 600, 400, 600, 350, 650, 350, 650, 350, 600, 400, 600, 350, 650, 350, 1600, 350, 650, 350,
                           1600, 350, 1600, 350, 1600, 400, 600, 350, 1600, 350, 1600, 350, 600}; // Protocol=UNKNOWN Data=0xF36E6C18
  irsend.sendRaw(rawCh1On, 85, 38);//(rawData, データ数, IR変調周波数)
}

void living_off()//リビングの照明(アイリスオーヤマ、リモコン1)のオフ信号
{
  server.send(200, "text/plain", "OK");//GETリクエストへの返答
  uint16_t rawCh1Off[85] = {2250, 650, 5600, 750, 1700, 250, 1700, 300, 650, 300, 700, 300, 700, 300, 650, 350,
                            650, 300, 700, 300, 650, 350, 1600, 350, 1650, 300, 650, 350, 650, 350, 650, 300, 700, 300, 1650, 300, 1650, 350, 650, 300,
                            650, 350, 650, 350, 650, 350, 650, 300, 650, 350, 650, 350, 650, 350, 650, 300, 650, 350, 650, 350, 650, 350, 650, 300,
                            700, 300, 650, 350, 650, 350, 1600, 350, 1600, 350, 1550, 400, 1600, 350, 600, 450, 1500, 400, 1600, 350, 600}; // Protocol=UNKNOWN Data=0xFB0B9DE5
  irsend.sendRaw(rawCh1Off, 85, 38);//(rawData, データ数, IR変調周波数)
}

void info()
{
  IPAddress myIP = WiFi.localIP();
  String myIPString = String(myIP[0]) + "." + String(myIP[1]) + "." + String(myIP[2]) + "." + String(myIP[3]);
  String infoHTML = "<!DOCTYPE html>";
  infoHTML += "<HTML><HEAD><meta charset ='UTF-8'><TITLE>スマートリモコン 情報</TITLE></HEAD>";
  infoHTML += "<BODY><p><B>IPアドレス</p></B>" + myIPString;
  infoHTML += "<BR><BR><p><B>FWバージョン情報</p></B>" + String(FWver);
  infoHTML += "</BODY></HTML>";
  server.send(200, "text/html", infoHTML);
}

void rootConnect()
{
  server.send(200, "text/plain", "ROOT connected");
  Serial.println("ROOT connected");
}

void error()
{
  server.send(404, "text/plain", "Error");
  Serial.println("Error");
}

コードを個別に解説していたらとんでもない文量になるので今回はやらない。ESP32を使った「Wi-Fi機能の使い方」「Webサーバー機能の使い方」「mDNS機能の使い方」「OTA(Over The Air)機能の使い方」「IRremoteライブラリの使い方」で検索したらいくらでも出てくるので、それらを組み合わせただけ。

これをさっきのESP32マイコンに書き込んであげればよい。書き込み方は、下記のようなUSB-シリアル変換モジュールを使って、モジュールのTx, Rx, DTRピンの3つをさっきの回路に繋ぐだけで良い。モジュールのRxをESP32のTxに、モジュールのTxをESP32のRxに、モジュールのDTRをESP32のENピンに繋ぐ。

https://akizukidenshi.com/catalog/g/gK-06693/

繋ぐと言っても、最初の書き込みの1回しかこれは使わないので、はんだ付けで回路に組み込むのではなくピンソケットにジャンパ線でも差して使おう。PC側の開発ソフトで書き込み実行して、コンパイル終了後コンソールに「connecting….._____」と表示され始めたら、マイコンをプログラム書き込みモードにするためにIO0に繋いだスイッチ(回路図ではSW2)を押しっぱなしにして、書き込みが始まるまで待つ。writing…と表示されて書き込みが始まったらスイッチは離して良い。

一回このプログラムを書き込んでしまえば、後でプログラム修正したものを書き込みたいときはOTAという機能によって、線を繋いだりしなくてもパソコンから直接Wi-Fi経由で無線で書き込みができる。そうなるとシリアル変換は不要なので、買うのが嫌だという人は最初の1回だけはこれを持っているお友達に借りればOK。(そういうわけなのでタイトルの金額にはこのシリアル変換モジュールの価格は含んでいない)

書き込み終了したら、電源をつなぐとESP32がWi-Fiに接続し、こちらが決めた名前のサーバーとして動作を始める。今回は上記コード内にある通り”espremo”という名前に設定した。いー えす ぴー・りも。名前がかわいい。このとき設定する名前は、LAN内に存在する他の端末に設定されている名称とかぶらないようにすること。

サーバーが問題なく稼働しているとき、同じルーターに接続している手持ちのパソコンやスマホ(もちろん他のマイコンからでも良いけど)から、http://サーバー名.localにアクセスすれば、あらかじめESP32に仕込んだ動作をさせることができる。今回はhttp://espremo.localだ。上のコードではアクセスしても”ROOT connected”ってメッセージを返すだけだから、ちゃんと接続できている確認にしかならないけど。

重要なのは、http://espremo.local/livingon および http://espremo.local/livingoffにアクセスしたとき。

このとき、予め覚えさせておいた赤外線発光パターンの通りに赤外線LEDを発光させる、という動作をマイコンに実行させるようにした。この発光パターンはリモコンのものと全く同じなので、リモコンを使わなくてもマイコンの方から家電の制御ができる。

ここまでできたらあとはもう少し。マイコンボードの方はもう完成なので、赤外線が家電に届きやすいような位置に置いとこう。

iPhoneのショートカット設定

iPhone側で使うのは「ショートカット」というアプリ。iOS13以降ならプリインストールのはずなので、App storeからわざわざダウンロードしなくても既に入ってるはず。

こちらで好きなように動作を組み合わせて設定しておくと、ボタンを押したりSiriから呼び出すだけでその動作を実行できる。

というわけで、いちいちブラウザを立ち上げてURLを「http://espremo~~~」と入力しなくても、音声でここにアクセスできるようにショートカットを組もう。

使うコマンドはたったの2つ。

「アクションを追加」→「Web」→「URL」を選び、URL部にさっきのマイコンサーバーURL「http://espremo.local/livingon」を設定する。

次に、「Web」→「URLの内容を取得」というアクションを追加する。

これで、iPhoneは設定したURLの中身が何なのかを取得するため、そのURLにアクセスする。当然サーバーであるマイコンからは何らかのメッセージ(内容)が返ってくるが、今回はそれはどうでもいい。URLにアクセスすることによってマイコンに所定の動作をさせることが目的なので、iPhoneショートカットの中身としてはこれで終わり。

最後にこのショートカットを「電気つけて」という名前で保存する。

すると、Siriに「電気つけて」と言うと、Homeキットではなくこちらのショートカットの方の呼び出しを行ってくれる(最初の1回はWebへのアクセス許可のための確認が出るかもしれないので、そのときはOKを選択)。

全く同様にして、照明をオフにするためのURLにアクセスするショートカットを「電気消して」という名前で保存する。

照明以外の家電に関しても、リモコンの赤外線信号を解析してマイコンプログラムに追加&それに対応したiPhoneショートカットの追加、という流れで(マイコンのメモリが許す限り)いくらでも機能を増やすことができる。現時点で使用中の我が家のマイコンには、とりあえず上記コードからさらに機能追加して、ファンヒーターの設定温度を上げる/下げる、ヒーター停止という3つの動作も付け足した。

もちろんこれらは単独で実行させるだけでなく、照明の消灯用URLとヒーターオフ用URLの両方にアクセスするショートカットを組んで、複数の家電を全部オフにするといった使い方もできる。

まとめ

以上で、「iPhoneに話しかける」→「Siriがショートカットを実行」→「指定したURLにアクセスが行われる」→「アクセスを受けたマイコンが赤外線LEDによる信号送信を実施」という流れが完成した。

これにより、音声で家電のコントロールが可能となる。

「Hey, Siri. 電気消して。」

こんなふうにね。

(iPhone上部でチカチカしているのは近接センサー用の赤外線LEDなので、このチカチカで家電操作しているわけではない)

材料は以下の通り

品目小計
マイコン:ESP32-WROVER-32530円
3.3Vレギュレータ:NJU7223F3350円
タクトスイッチ 2個20円
フレネルレンズ S901340円
LED (赤×1個, 赤外×3個)80円
XHコネクタ(電源ケーブル接続用)10円
電気2重層コンデンサ 50円
XHコネクタ(電源ケーブル接続用)10円
線材、抵抗、コンデンサ等(安すぎて価格算定できないのでざっくり)50円
ユニバーサル基板50円
部材と価格(金額は秋月電子等の電子部品通販サイトより。2021年1月5日時点)

合計金額は890円。マイコンにWROVERじゃなくてWROOMの方を使っていれば720円。材料費わずか千円未満なので、小学生のお小遣いくらいの資金でも作れるので良い子のみんなはぜひやってみよう。使う道具もはんだごてとUSBシリアル変換モジュールとパソコンくらい。あと開発用ソフト。(Arduino IDEやVScodeなど。どっちも無料)

あっ、でもiPhoneは必要なので持ってなかったらどうにかして用意してね。Siriとショートカットが使えれば同じことはできるので、必ずしもiPhoneじゃなくてもいい。iPadとかでも。

あと、赤外線リモコンの信号を解析するための回路を別途作っておかないと、そもそもESP32に覚えさせる発光パターンがわからない。そのへんの説明は全カットしちゃった。一つの記事にまとめるにはあまりにも要素が多くて説明不足感が否めないけど、割とネットで調べられる一般知識の範囲で作れるので真似したい人は色々ググるか自力で頑張って。赤外線信号の解析には赤外線受光モジュールとIRremoteライブラリを使いました。

次にスマートリモコンを作るときは回路に温度センサーを付け足して、Siriに室温を尋ねて答えさせたり、指定の時間が来たらマイコンが自動で照明オン/オフして目覚ましや睡眠を促す機能とかを増やそうかな。

おわり

参考文献

ESP32&Arduino 電子工作 プログラミング入門

↑ ESP32だけに特化の解説本じゃなくてマイコンプログラミングの基礎から始まって、最終的にはESP32のWi-FiやBLE関連機能の解説まで入っている。

これ一冊買っておけば初級~中級あたりをかなり広範囲カバーできると思う。(ただし開発環境についてはArduinoのみの記載)

返信する

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