isakon が 2020年08月21日10時41分13秒 に編集
筆者を明記した
本文の変更
elchika運営です。
elchika運営のisakonです。
M5StickCを買ったので、スマホ操作できる家電リモコンを作ってみました。 ## 使用したもの - [M5StickC](https://www.switch-science.com/catalog/5517/) - [M5Stack用赤外線送受信ユニット](https://www.switch-science.com/catalog/5699/) 全てスイッチサイエンス様の通販で揃います。 ## M5StickCとは ![M5StickC](https://camo.elchika.com/fce578cfcadefd0c3d163b19514424cf2fb8082f/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f36666436623331632d333763652d343735362d396463342d636632633664653039643166/) M5StickCは小型の[M5Stack](https://m5stack.com/)です。 ESP32ベースでWi-FiとBluetoothを内蔵しているので、気軽にIoT開発を始めることができます。 80×160のLCDが最初からついていて、工場出荷時の状態のM5StickCを起動すると6軸IMUが計測した温度やピッチ・ロール・ヨーなどが表示されるようになっていました。 開発言語としてはArduino言語のほか、[Micro Python](http://micropython.org/)や、ビジュアルプログラミングができる[UIFlow](https://m5stack.github.io/UIFlow_doc/ja/)が使えます。 ## 環境構築 Arduino IDEを使います。 M5Stack公式のドキュメントに[M5StickC クイックスタート](https://docs.m5stack.com/#/ja/quick_start/m5stickc/m5stickc_quick_start_with_arduino_Windows)があるので、M5StickCにスケッチを書き込めるようにしておきましょう。 余談ですが、「ファイル → スケッチ例 → M5StickC → Games → FlappyBird」のスケッチを書き込むとM5StickC上でFlappyBirdが遊べるので、動作確認やEEPROMの読み書きのテストにオススメです:bird: 本記事では扱いませんが、EEPROMを使うことでデータを永続化できるため、赤外線信号を学習して送信するリモコンを作ることができます。 ## スマホ操作できる家電リモコンを作る M5StickCを使ってテレビなどの家電に赤外線信号を送信して操作できる家電リモコンを作ります。 また、スマホと連携して対象の家電を操作できるようにします。 以下のように製作を進めていきます。 - 代替したい家電リモコンの赤外線信号を解析する - 取得した赤外線信号をM5StickCで送信してみる - Bluetoothで操作できるようにする ### IRユニットで赤外線信号を解析する M5StickCにIRユニットを接続して赤外線信号を受信・解析できるようにしましょう。IRはinfrared(赤外線)の略称です。 Groveケーブルを抜き差しするときは若干固めなので壊さないように気をつけてください。 ![M5StickCにIRユニットを接続](https://camo.elchika.com/493aec4ca0377cb45bcd82f070b1715e01fae98e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f65626166356538372d376632652d343233612d623133652d613636373931646465363061/) 赤外線信号の解析というと仰々しい感じがしますが、[IRremoteESP8266](https://github.com/crankyoldgit/IRremoteESP8266)のexamplesの[IRrecvDumpV2](https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/IRrecvDumpV2/IRrecvDumpV2.ino)のスケッチを書き込むと簡単に受信した信号を表示することができます。 IDEのライブラリマネージャの検索欄に「IRremoteESP8266」を入力してインストールし、「ファイル → スケッチ例 → IRremoteESP8266→ IRrecvDumpV2」からスケッチを表示します。 受信に使用するPIN番号が異なるので、37行を次のように書き換えてからスケッチをM5StickCに書き込みます。 ```arduino const uint16_t kRecvPin = 33; ``` 書き込み終えたらシリアルモニタを表示し、右下のボーレートを`115200bps`に合わせます。 IRユニットに向かってリモコンのボタンを押して赤外線信号を飛ばしてみましょう。 正しく接続されていれば次のような出力がされます。 ```:HisenseのTVリモコンの電源ボタンを押したときの出力 IRrecvDumpV2 is now running and waiting for IR input on Pin 33 Timestamp : 000044.428 Library : v2.7.0 Protocol : NEC Code : 0xFDB04F (32 Bits) uint16_t rawData[67] = {9008, 4420, 614, 528, 588, 526, 588, 526, 590, 528, 588, 526, 590, 526, 588, 526, 588, 526, 590, 1642, 590, 1642, 588, 1642, 588, 1642, 590, 1644, 588, 1642, 590, 526, 590, 1644, 588, 1644, 588, 526, 588, 1644, 588, 1642, 588, 528, 588, 528, 588, 526, 590, 526, 588, 526, 588, 1642, 590, 528, 588, 526, 588, 1642, 588, 1642, 590, 1642, 590, 1642, 590}; // NEC FDB04F uint32_t address = 0xBF00; uint32_t command = 0xD; uint64_t data = 0xFDB04F; ``` `Protocol : NEC`と表示されていますが、これは赤外線リモコンの通信フォーマットとなっています。 プロトコルはこの後の赤外線信号の送信に関わってきます。 `Code :0xFDB04F (32 Bits)`は受信した信号と固定長フレームのサイズになっています。 `rawData`は信号そのもののデータなので、これをそのまま記録して送信すれば同一の操作ができます。 構造としてはONの時間とOFFの時間の繰り返しでできています。 ちなみに、NECフォーマットは $T = 562(\mu s)$ として、ONを $16T = 8992(\mu s)$、OFFを$8T = 4496(\mu s)$ 間送信することでフレームの開始を判別できるようにしています。 ここではとりあえず、電源ボタン・音量+-・1〜12のチャンネル変更ボタンの信号を記録しておきました。 IRユニットは赤外線信号を受信するために接続したので、これ以上受信する必要が無ければもう外してしまっても大丈夫です。 ### M5StickCを赤外線リモコンにする 前項で記録したCodeを使ってM5StickCを赤外線リモコンにします:rotating_light: M5StickCにはIRトランスミッタが組み込まれているので、赤外線信号を送信するスケッチを書き込めば赤外線LEDの発光に変換されます。 IRremoteESP8266の`IRsend::sendNEC`を呼べば、`IRsend::sendGeneric`にNECフォーマットの規格に準拠したパラメータが渡されて赤外線信号を送信できます。 `IRsend::sendDaikin`や`sendSony`なども用意されているので、通信規格に合ったものを選びましょう。 M5StickCのM5ボタンを押したら家電に赤外線信号を送信できるようにします。 次のスケッチを書き込みます。 ```arduino:M5ボタンで赤外線信号を送信する #include <M5StickC.h> #include <IRremoteESP8266.h> #include <IRsend.h> // M5StickCのIRは9ピン const uint16_t kIrLed = 9; IRsend irsend(kIrLed); void setup() { irsend.begin(); M5.begin(); M5.Lcd.setRotation(1); M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.println("IR Test"); M5.Lcd.println("Press M5 button"); } void loop() { M5.update(); // M5ボタン(BtnA)が押されたとき if (M5.BtnA.wasPressed()) { // IRsend::sendNEC(uint64_t data, uint16_t nbits, uint16_t repeat) // ここで赤外線信号を送信する irsend.sendNEC(0xFDB04F, 32); delay(100); } } ``` 家電の赤外線の受光部分にM5StickCを向けながらM5ボタンを押すと反応するはずです。 受信側の性能にもよりますが、1mくらいの距離が限界なように感じました。 今回はテレビの電源の信号だったので、M5ボタンを押すたびに電源のON/OFFが切り替わりました。 ### Blynkと連携してスマホから操作する 先述した通り、M5StickCでは短い距離しか赤外線を飛ばせません。 そこで、家電のそばにM5StickCを置いたまま、BLE(Bluetooth Low Energy)を使ってスマホから操作できるようにします。 簡単にIoTを実現できる[Blynk](https://www.blynk.cc/)というサービスがあるので、これを端末にインストールして使います。 - [App Store](https://apps.apple.com/jp/app/blynk-iot-for-arduino-esp32/id808760481) - [Google Play](https://play.google.com/store/apps/details?id=cc.blynk&hl=ja) 起動するとログインを求められるので、アカウントを作成してログインしましょう。 無料枠だと2000ポイント分のWidgetを設置することができます。ボタン1個あたり200ポイントなので、無料枠ならボタンを10個置けるといった具合です。 「+ New Project」のボタンを押して新規プロジェクトを作成しましょう。 M5StickCでBLEで通信する場合は下図のようにします。Project Nameには好きな名前をつけましょう:thinking: ![Blynkプロジェクトを作成する](https://camo.elchika.com/870c965e10da0d25ad23cbfbfbc610aaff084fcf/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f64616336313132612d633665612d343133612d623530622d393261343764333133396134/) プロジェクトが作成されると登録したメールアドレスにAuth Tokenが送られてきます。 このAuth TokenをM5StickCに書き込んでBlynkアプリと連携します。 初めに表示される画面には点々しか表示されていませんが、画面をタップするとWidget Boxが表示されます。 BLEを扱うには「BLE (beta)」のWidgetを置く必要があるので置いておきましょう。 ![Widget Box](https://camo.elchika.com/cd668ac615ccba5ae02dc44658acda03656680e2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f39356131316462382d313138382d343166322d386162642d323262323831346536326162/) ポイントの制限はありますが、Widgetを好きなように置いていってコントローラーを作ることができます。 TVリモコンのようにチャンネルのボタンを置いていくと無料枠を超えてしまうので、セレクトボックスのWidgetとボタンを組み合わせて、選択した項目に対応する信号を送信できるようにしました。 ![作成したコントローラー](https://camo.elchika.com/1ee81a8d96fd7aa2a9aa5430a2205550d708eeb5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f66396132373534652d663533612d346365322d616439612d356431313861666331623330/) 各種Widgetの値を受け取るには置いたWidgetをタップして開かれたSettingsのOUTPUTにバーチャルピンを指定する必要があります。 セレクトボックスは下図のように設定しておきました。これによって、V0にMENU ITEMSの左側にある`[index]`の数値が渡されるようになります。 ![セレクトボックス(Menu)の設定](https://camo.elchika.com/6de8318148ece16ee34c666f1653daf5e5a00bed/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f66383531333635372d633038392d343034622d396135372d313732643130353563633064/) 準備ができたので、Blynkで操作できるようにするスケッチをM5StickCに書き込みましょう:pen: ```arduino:M5StickCをBlynkから操作する #include <M5StickC.h> #include <BlynkSimpleEsp32_BLE.h> #include <IRremoteESP8266.h> #include <IRsend.h> #define BLYNK_PRINT Serial #define BLYNK_USE_DIRECT_CONNECT #define DEVICE_NAME "M5StickC Remocon" const char auth[] = "プロジェクト作成時に送られたメールに書いてあるAuth Tokenをここに貼り付ける"; const uint16_t kIrLed = 9; IRsend irsend(kIrLed); struct Remote { char name[9]; uint64_t command; }; // 使用するCode(IRユニットで受信した赤外線信号)を定義しておく Remote menu[] = { { "POWER", 0xFDB04F }, { "1" , 0xFD807F }, { "2" , 0xFD40BF }, { "3" , 0xFDC03F }, { "4" , 0xFD20DF }, { "5" , 0xFDA05F }, { "6" , 0xFD609F }, { "7" , 0xFDE01F }, { "8" , 0xFD10EF }, { "9" , 0xFD906F }, { "10" , 0xFD00FF }, { "11" , 0xFDD02F }, { "12" , 0xFD38C7 }, }; Remote volumePlus = { "VOL+", 0xFD22DD }; Remote volumeMinus = { "VOL-", 0xFDC23D }; // 選択されたコマンドを保持する Remote selection = menu[0]; void setup() { irsend.begin(); Blynk.setDeviceName(DEVICE_NAME); Blynk.begin(auth); M5.begin(); M5.Lcd.setRotation(1); M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.setCursor(0, 0, 2); M5.Lcd.println(DEVICE_NAME); M5.Lcd.setTextSize(2); show(selection.name); } void loop() { M5.update(); Blynk.run(); // M5ボタンを押したときに保持しているコマンドを送信する if (M5.BtnA.isPressed()) { irsend.sendNEC(selection.command, 32); delay(100); } } void show(char *name) { M5.Lcd.fillRect(0, 20, 160, 40, TFT_BLACK); M5.Lcd.setCursor(0, 20, 2); M5.Lcd.print(name); } void sendAndShow(struct Remote *remote) { irsend.sendNEC(remote->command, 32); show(remote->name); } // セレクトボックスで選択したアイテムに対応するコマンドを保持して名前をLCDに表示 BLYNK_WRITE(V0) { int index = param.asInt() - 1; selection = menu[index]; show(selection.name); } // 保持したコマンドを送信する BLYNK_WRITE(V1) { if (param.asInt() == 1) { irsend.sendNEC(selection.command, 32); } } // 音量-のコマンドを送信する BLYNK_WRITE(V2) { if (param.asInt() == 1) { sendAndShow(&volumeMinus); } } // 音量+のコマンドを送信する BLYNK_WRITE(V3) { if (param.asInt() == 1) { sendAndShow(&volumePlus); } } ``` 書き込んだ後、Blynkの「BLEのWidgetをタップ → Settings → Connect BLE Device」で表示されるBLE端末の一覧にM5StickC Remoconのようなデバイス名が追加されるので、選択してOKを押して接続します。 接続されるとBLEのWidgetのBluetoothアイコンが水色になります。 上のスケッチだと、セレクトボックスのアイテムを選択すると対応したコマンドの名前がM5StickCのLCDに表示されます。 送信ボタンを押すと、赤外線信号をM5StickCから送信することができます。 家電の赤外線受光部分の前にM5StickCを置き、距離を置いてBlynkのボタンを押すと、あたかもスマホから家電を操作しているかのようになります。 ![Blynkとスケッチを書き込んだM5StickC](https://camo.elchika.com/c436b66f5a5129753ce2f216c6b3bca2939c59e3/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f30303030303030302d303030302d303030302d303030302d3030303030303030303030302f66633563616436382d663261382d343765322d616363652d313433303235643266633965/) M5StickCに内蔵されているものではなく、別途高出力な赤外線LEDを用意すれば、部屋の家電を全てスマホからコントロールできるようになるかもしれません。 リモコンを探さなくてもよくなりますね:relaxed: ## おまけ:elchikaロゴをLCDに表示する 冒頭でM5StickCでelchikaロゴを表示している画像を載せたのでその解説を載せておきます。 M5StickCのLCDに画像を表示したい場合、画像をC言語の配列表現に変換したXBitMap(XBM)を`M5.Lcd.drawXBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t width, int16_t height, uint16_t color)`に渡せば表示できます。 ImageMagickなどで変換するほか、https://www.online-utility.org/image/convert/to/XBM のようなWebサービスを使ってXBMに変換した画像を利用することができます。 以下のスケッチは、M5ボタンを押すたびにロゴの色も変わるようにしています。 ```arduino:M5StickCにelchikaロゴを表示する #include <M5StickC.h> #include <pgmspace.h> #define logoWidth 32 #define logoHeight 32 #define logoX 18 #define logoY 24 PROGMEM const unsigned char logo[] = { 0x00, 0xC0, 0x1F, 0x00, 0x00, 0x80, 0x7F, 0x00, 0x00, 0x00, 0xF9, 0x01, 0x00, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x80, 0x0F, 0x1E, 0x00, 0x00, 0x7F, 0x1C, 0x70, 0x00, 0xFC, 0x38, 0xF8, 0x00, 0xE0, 0x71, 0xF8, 0x01, 0xC2, 0x63, 0xFC, 0x01, 0x8F, 0xE7, 0xF8, 0x03, 0x1E, 0x07, 0xF8, 0x0F, 0x38, 0x02, 0x10, 0x3F, 0x31, 0x00, 0x00, 0xFC, 0x07, 0x00, 0x00, 0xF0, 0x06, 0x00, 0x00, 0x60, 0x0C, 0x00, 0x00, 0x70, 0x1E, 0x00, 0xF8, 0xFC, 0x7F, 0x26, 0xFC, 0xBF, 0xFB, 0x7F, 0x0E, 0x8F, 0xE3, 0x7B, 0x06, 0x83, 0xC3, 0x61, 0x06, 0x83, 0x81, 0x61, 0x06, 0x83, 0x83, 0x61, 0x8E, 0x83, 0x83, 0x61, 0xFC, 0x81, 0x83, 0x7F, 0xF8, 0xC0, 0x07, 0x7F, 0x00, 0xC0, 0x07, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xE0, 0x0F, 0x00, 0x00, 0xC0, 0x07, 0x00, 0x00, 0x80, 0x03, 0x00, }; uint16_t color = TFT_GREEN; void setup() { M5.begin(); M5.Lcd.setRotation(1); M5.Lcd.fillScreen(TFT_BLACK); M5.Lcd.drawXBitmap(logoX, logoY, logo, logoWidth, logoHeight, color); M5.Lcd.setCursor(60, 34); M5.Lcd.setTextSize(2); M5.Lcd.setTextColor(TFT_WHITE); M5.Lcd.println("elchika"); } void loop() { M5.update(); if (M5.BtnA.isPressed()) { switch (color) { case TFT_RED: color = TFT_GREEN; break; case TFT_GREEN: color = TFT_BLUE; break; case TFT_BLUE: color = TFT_RED; break; } M5.Lcd.drawXBitmap(logoX, logoY, logo, logoWidth, logoHeight, color); delay(200); } } ``` ## 参考資料 - [赤外線リモコンの通信フォーマット - Electronic Lives Manufacturing](http://elm-chan.org/docs/ir_format.html) - [M5StickCで赤外線リモコンを使う - Lang-ship](https://lang-ship.com/blog/?p=886)