M5Stick-C＋IR UnitとEspalexaでエアコンの電源をON/OFFさせるAlexaデバイスを作る

# はじめに

エアコンに限らず、家電は電源を落とすときに音声で操作できると便利なんだよなといつも思っている。
テレビは実現できている。電気も実現できている。残りはエアコンだけ。でもスマートリモコンを買うほどではなかったので、家にあった `M5Stick-C` でリモコンを作成することにした。

# やりたいこと

エアコンのON/OFFを音声で操作したい。
だけど電子工作とかは得意じゃないので、パーツを組み合わせてちょこっとプログラムを書く感じで、手軽にお安くリモコンを作りたい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/37a7f555-0bea-6da3-a798-e3c02ea5824f.png)

# 必要なモノ

- 1. Amazon
    - Echoデバイス
    - Alexaアプリ
- 2. M5Stack
    - M5Stick-C
    - IR Unit
- 3. IDE
    - Arduino IDE
    - IRremoteESP8266ライブラリ
    - Espalexaライブラリ

# 使用したスケッチ例

|No|スケッチ例|用途|備考|
|:--|:--|:--|:--|
|1.|IRremoteESP8266: IRrecvDumpV2|赤外線受信|一時的に使用するだけ|
|2.|IRremoteESP8266: IRsendDemo|赤外線送信| |
|3.|Espalexa: EspalexaBasic|Alexa連携| |

`IRrecvDumpV2` は赤外線信号をゲットするためだけに使用する。`IRrecvDumpV3`でもできると思うけど、ずっと`V2`を使ってるので今回もそのまま`V2`を使用した。

Alexaと連携するリモコンは`IRsendDemo`と`EspalexaBasic`を組合せて作る。


# IRremoteESP8266 とは

IRremoteESP8266は、IRremoteという赤外線のライブラリをESP8266またはESP32に特化させたもの。これを使うと簡単に赤外線信号を送受信することができる。

# Espalexa とは

Espalexaは`Philips hue`のエミュレーター。正確には、SSDPプロトコルとPhilips hue APIの一部をエミュレートしているとのこと。なので、Alexaで操作可能なデバイスとして検出ができて、そして制御ができる。

Alexaから見たら`M5Stick-C`ではなく`Philips hue`に見えるということ。こんな感じで、`セットアップするデバイスを選択` で 右上の`Philips Hue`のアイコンをクリックしていくと、エアコンのリモコンがライトとして検出される。

<img width="30%" border="1" alt="アレクサアプリでデバイスを検索する" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/f06c24ab-978d-68ac-7419-f98d3ce145e0.png">　<img width="30%" border="1" alt="Philips hueのデバイスを検索する" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/64bd2204-b1c3-b532-1ae9-03b40de9c097.png">　<img width="30%" border="1" alt="エアコンがライトとして検出される" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/fd938d8d-19e8-e54a-3ba0-4414c1fdb5de.png">


ライトのON/OFFと、エアコンの電源ON/OFFをマッピングさせることでエアコンを制御する仕組み。


# 作成の流れ

- 1. エアコンの赤外線信号を取得する
- 2. スケッチの骨格を作成する
    - Espalexa: EspalexaBasic を開く
    - Wi-Fiの設定
    - コールバック関数の作成
- 3. スケッチに赤外線送信部分を追加する
    - 赤外線送信部分の追加
- 4. Alexaに認識させる
    - アレクサアプリでデバイスとして認識させる
- 5. 定型アクションを作成する
    - 電源ON
    - 電源OFF

# 1. エアコンの赤外線信号を取得する

エアコンの赤外線信号を取得する。
まずは `スケッチ例` から `IRrecvDumpV2`を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/63dd383c-224d-df8f-a855-1cccb5229c37.png)


以下の２ヵ所を変更して書き込めば `IR Unit` で赤外線が受信できるようになる。

|No|変更前|変更後|
|:--|:--|:--|
|1.|#include \<Arduino.h\>|#include \<M5StickC.h\>|
|2.|const uint16_t kRecvPin = 14;|const uint16_t kRecvPin = 33;|

書き込めたら `IR Unit` に向かってリモコンのボタンをポチポチ押すと、シリアルモニタに以下のような情報が表示れさる。

```
14:20:10.550 -> IRrecvDump is now running and waiting for IR input on Pin 33
14:20:25.463 -> if in 
14:20:25.463 -> 
14:20:25.463 -> Timestamp : 000201.408
14:20:25.463 -> Library   : v2.7.11
14:20:25.463 -> 
14:20:25.463 -> Protocol  : FUJITSU_AC
14:20:25.463 -> Code      : 0x1463001010FE0930C101A414002038FE (128 Bits)
14:20:25.463 -> Mesg Desc.: Model: 5 (ARRY4), Power: On, Mode: 1 (Cool), Temp: 28C, Fan: 4 (Quiet), Clean: Off, Filter: On, Swing: 2 (Swing(H)), Command: N/A
14:20:25.463 -> uint16_t rawData[259] = {3304, 1584,  466, 358,  440, 378,  464, 1166,  442, 378,  442, 1194,  442, 376,  440, 378,  442, 376,  442, 1190,  440, 1192,  444, 388,  468, 350,  442, 378,  468, 1168,  466, 1172,  442, 380,  440, 388,  440, 380,  464, 356,  442, 380,  466, 356,  442, 380,  440, 378,  442, 374,  442, 376,  440, 376,  442, 382,  466, 352,  442, 1192,  442, 376,  440, 382,  440, 380,  442, 380,  440, 380,  442, 378,  440, 382,  440, 1194,  442, 376,  442, 376,  440, 380,  440, 380,  440, 1220,  440, 1194,  466, 1168,  442, 1190,  464, 1168,  466, 1172,  440, 1198,  442, 1188,  442, 380,  440, 380,  442, 1190,  442, 380,  440, 378,  442, 380,  442, 380,  466, 350,  442, 382,  470, 376,  440, 380,  438, 1192,  440, 1194,  442, 378,  440, 386,  464, 1168,  442, 382,  440, 380,  440, 382,  442, 384,  436, 378,  440, 1192,  440, 1192,  440, 1198,  440, 382,  442, 378,  440, 382,  440, 382,  440, 384,  440, 378,  438, 378,  440, 380,  440, 380,  440, 1192,  442, 376,  440, 380,  440, 1190,  440, 378,  468, 1200,  440, 380,  440, 378,  442, 1194,  464, 354,  464, 1168,  440, 382,  438, 382,  440, 380,  440, 384,  464, 354,  440, 378,  440, 382,  442, 378,  440, 378,  440, 382,  440, 378,  464, 354,  440, 380,  440, 378,  440, 378,  460, 380,  440, 1200,  440, 380,  440, 380,  438, 380,  464, 356,  440, 384,  442, 1192,  442, 1192,  440, 1198,  440, 380,  440, 380,  440, 382,  440, 1194,  440, 1192,  442, 1192,  440, 1196,  440, 1196,  440, 1192,  440, 1192,  440};  // FUJITSU_AC
14:20:25.603 -> uint8_t state[16] = {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0xC1, 0x01, 0xA4, 0x14, 0x00, 0x20, 0x38, 0xFE};
14:20:25.603 -> 
14:20:25.603 -> 
14:20:27.191 -> if in 
14:20:27.191 -> 
14:20:27.191 -> Timestamp : 000203.157
14:20:27.191 -> Library   : v2.7.11
14:20:27.191 -> 
14:20:27.191 -> Protocol  : FUJITSU_AC
14:20:27.191 -> Code      : 0x1463001010FE0930C104A414002038FB (128 Bits)
14:20:27.239 -> Mesg Desc.: Model: 5 (ARRY4), Power: On, Mode: 4 (Heat), Temp: 28C, Fan: 4 (Quiet), Clean: Off, Filter: On, Swing: 2 (Swing(H)), Command: N/A
14:20:27.239 -> uint16_t rawData[259] = {3326, 1562,  440, 380,  440, 376,  440, 1196,  442, 382,  440, 1192,  440, 378,  440, 380,  440, 382,  438, 1194,  440, 1194,  440, 386,  438, 378,  464, 356,  438, 1192,  440, 1196,  440, 382,  440, 380,  440, 380,  442, 380,  440, 382,  442, 382,  440, 378,  440, 378,  440, 376,  442, 376,  440, 378,  438, 380,  440, 378,  440, 1196,  464, 354,  440, 382,  440, 380,  440, 378,  440, 380,  440, 378,  440, 376,  440, 1190,  442, 376,  464, 354,  440, 376,  440, 386,  440, 1208,  442, 1190,  440, 1192,  442, 1192,  442, 1192,  440, 1194,  440, 1194,  440, 1192,  464, 356,  440, 378,  440, 1196,  440, 380,  438, 380,  440, 382,  440, 384,  440, 382,  442, 378,  492, 352,  440, 376,  466, 1168,  440, 1196,  440, 378,  462, 356,  440, 1192,  442, 384,  438, 382,  440, 384,  438, 382,  440, 380,  438, 1192,  440, 1194,  440, 384,  438, 382,  440, 1196,  440, 380,  466, 352,  440, 382,  442, 378,  440, 376,  440, 376,  442, 378,  440, 1194,  440, 380,  440, 386,  440, 1196,  464, 358,  490, 1180,  440, 380,  440, 380,  438, 1194,  440, 378,  464, 1170,  440, 382,  440, 384,  440, 382,  440, 376,  466, 356,  464, 356,  440, 382,  440, 380,  438, 380,  438, 378,  438, 378,  440, 382,  438, 382,  440, 384,  440, 384,  440, 380,  440, 1196,  440, 380,  440, 376,  442, 378,  438, 378,  440, 378,  438, 1198,  440, 1196,  440, 1194,  440, 378,  440, 386,  440, 1194,  440, 1194,  440, 384,  440, 1198,  440, 1194,  442, 1192,  440, 1194,  440, 1194,  464};  // FUJITSU_AC
14:20:27.333 -> uint8_t state[16] = {0x14, 0x63, 0x00, 0x10, 0x10, 0xFE, 0x09, 0x30, 0xC1, 0x04, 0xA4, 0x14, 0x00, 0x20, 0x38, 0xFB};
```

上段が`冷房` ボタンを押した時の情報で、下段が`停止`ボタンを押した時の情報。
`uint16_t rawData` の部分を使用する。抜き出したのが以下。この２行を使用する。

```
// 電源ON
uint16_t rawData[259] = {3304, 1584,  466, 358,  440, 378,  464, 1166,  442, 378,  442, 1194,  442, 376,  440, 378,  442, 376,  442, 1190,  440, 1192,  444, 388,  468, 350,  442, 378,  468, 1168,  466, 1172,  442, 380,  440, 388,  440, 380,  464, 356,  442, 380,  466, 356,  442, 380,  440, 378,  442, 374,  442, 376,  440, 376,  442, 382,  466, 352,  442, 1192,  442, 376,  440, 382,  440, 380,  442, 380,  440, 380,  442, 378,  440, 382,  440, 1194,  442, 376,  442, 376,  440, 380,  440, 380,  440, 1220,  440, 1194,  466, 1168,  442, 1190,  464, 1168,  466, 1172,  440, 1198,  442, 1188,  442, 380,  440, 380,  442, 1190,  442, 380,  440, 378,  442, 380,  442, 380,  466, 350,  442, 382,  470, 376,  440, 380,  438, 1192,  440, 1194,  442, 378,  440, 386,  464, 1168,  442, 382,  440, 380,  440, 382,  442, 384,  436, 378,  440, 1192,  440, 1192,  440, 1198,  440, 382,  442, 378,  440, 382,  440, 382,  440, 384,  440, 378,  438, 378,  440, 380,  440, 380,  440, 1192,  442, 376,  440, 380,  440, 1190,  440, 378,  468, 1200,  440, 380,  440, 378,  442, 1194,  464, 354,  464, 1168,  440, 382,  438, 382,  440, 380,  440, 384,  464, 354,  440, 378,  440, 382,  442, 378,  440, 378,  440, 382,  440, 378,  464, 354,  440, 380,  440, 378,  440, 378,  460, 380,  440, 1200,  440, 380,  440, 380,  438, 380,  464, 356,  440, 384,  442, 1192,  442, 1192,  440, 1198,  440, 380,  440, 380,  440, 382,  440, 1194,  440, 1192,  442, 1192,  440, 1196,  440, 1196,  440, 1192,  440, 1192,  440};  // FUJITSU_AC

// 電源OFF
uint16_t rawData[259] = {3326, 1562,  440, 380,  440, 376,  440, 1196,  442, 382,  440, 1192,  440, 378,  440, 380,  440, 382,  438, 1194,  440, 1194,  440, 386,  438, 378,  464, 356,  438, 1192,  440, 1196,  440, 382,  440, 380,  440, 380,  442, 380,  440, 382,  442, 382,  440, 378,  440, 378,  440, 376,  442, 376,  440, 378,  438, 380,  440, 378,  440, 1196,  464, 354,  440, 382,  440, 380,  440, 378,  440, 380,  440, 378,  440, 376,  440, 1190,  442, 376,  464, 354,  440, 376,  440, 386,  440, 1208,  442, 1190,  440, 1192,  442, 1192,  442, 1192,  440, 1194,  440, 1194,  440, 1192,  464, 356,  440, 378,  440, 1196,  440, 380,  438, 380,  440, 382,  440, 384,  440, 382,  442, 378,  492, 352,  440, 376,  466, 1168,  440, 1196,  440, 378,  462, 356,  440, 1192,  442, 384,  438, 382,  440, 384,  438, 382,  440, 380,  438, 1192,  440, 1194,  440, 384,  438, 382,  440, 1196,  440, 380,  466, 352,  440, 382,  442, 378,  440, 376,  440, 376,  442, 378,  440, 1194,  440, 380,  440, 386,  440, 1196,  464, 358,  490, 1180,  440, 380,  440, 380,  438, 1194,  440, 378,  464, 1170,  440, 382,  440, 384,  440, 382,  440, 376,  466, 356,  464, 356,  440, 382,  440, 380,  438, 380,  438, 378,  438, 378,  440, 382,  438, 382,  440, 384,  440, 384,  440, 380,  440, 1196,  440, 380,  440, 376,  442, 378,  438, 378,  440, 378,  438, 1198,  440, 1196,  440, 1194,  440, 378,  440, 386,  440, 1194,  440, 1194,  440, 384,  440, 1198,  440, 1194,  442, 1192,  440, 1194,  440, 1194,  464};  // FUJITSU_AC
```

# 2. スケッチの骨格を作成する

`EspalexaBasic` をベースにして修正していく。

#### Espalexa: EspalexaBasic を開く

`スケッチ例` から `EspalexaBasic` を開く。開いたら`名前をつけて保存する`で別ファイルとして保存しておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/1fefe2ca-a760-ca36-a3ef-44874b82eac9.png)

上記の`スケッチ例`に表示されない場合は`ライブラリマネージャ`で`Espalexa`をインストールする。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/5437cc80-ddde-f5f2-60c7-caedb0e63106.png)


#### Wi-Fiの設定

ssid と password に 自身のSSIDとパスワードを設定する。

```
// Change this!!
const char* ssid = "...";
const char* password = "wifipassword";
```

#### コールバック関数の作成

今回は以下の設定で作成する。

|No|項目|設定値|備考|
|:--|:--|:--|:--|
|1.|関数名|airconPowerOnOff| |
|2.|デバイス表示名|FUJITSU AC|うちのエアコンが富士通製なので|
|3.|デバイスタイプ|EspalexaDeviceType::onoff| |

サンプルスケッチには予め `firstLightChanged`　`secondLightChanged`　`thirdLightChanged` の３つのコールバック関数が記載されているが、この３つは削除して `airconPowerOnOff` 関数を記述していく。基本的には以下の３カ所を変更していく。

関数を定義する。

```
// Callback Functions
void airconPowerOnOff(EspalexaDevice* dev);
```

関数を登録する。今回は電源のオン／オフがやりたいので`EspalexaDeviceType::onoff`を設定した。Espalexaがサポートしているデバイスタイプは、[Espalexa（GitHub）](https://github.com/Aircoookie/Espalexa)を参照。

```
// Define your devices here. 
espalexa.addDevice("FUJITSU AC", airconPowerOnOff, EspalexaDeviceType::onoff);
```

関数の本体を作成する。

```
// our callback functions
void airconPowerOnOff(EspalexaDevice *d) {
    if (d == nullptr) return;
    Serial.println(d->getValue());
    if (d->getValue()) {
        Serial.println("Power On");
    } else {
        Serial.println("Power Off");
    }
}
```

ちなみに、`d->getValue()`で取得できる値の意味は以下だけど、今回は、電源ON/OFFしかしないので `255` か `0` のどちらかになる。

|No|値|状態|
|:--|:--|:--|
|1.|0|OFF|
|2.|1 - 254|Dimmed|
|3.|255|ON|


# 3. スケッチに赤外線送信部分を追加する

`スケッチ例` から `IRsendDemo` を開き、これを参考にして必要な部分を追加していく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/3ed69edf-af6b-1db3-f16a-638d0025b1e2.png)



#### 赤外線送信部分の追加

今回は、`M5Stick-C` の内臓IRではなく、`IR Unit` のIRを使用するため以下のようにpin番号を変更する。
※`M5Stick-C` 以外のものを使用する場合は、pin番号が32とは限らないので注意。

```
const uint16_t kIrLed = 32;
IRsend irsend(kIrLed);
uint16_t rawData_on[259]  = {3336, 1556,  470, 354,  470, 376,  448, 1188,  448, 370,  448, 1184,  448, 348,  466, 374,  446, 348,  470, 1160,  472, 1162,  470, 388,  446, 352,  470, 372,  448, 1162,  468, 1166,  472, 372,  448, 346,  470, 372,  448, 372,  448, 348,  446, 400,  448, 372,  444, 374,  448, 346,  470, 370,  448, 346,  472, 354,  472, 350,  472, 1184,  448, 348,  472, 346,  472, 374,  450, 346,  472, 372,  448, 370,  448, 374,  448, 1186,  448, 346,  474, 350,  472, 348,  470, 348,  472, 1220,  446, 1164,  474, 1162,  472, 1162,  474, 1158,  472, 1162,  474, 1158,  472, 1164,  474, 348,  470, 350,  472, 1162,  472, 374,  448, 344,  474, 368,  450, 368,  450, 370,  450, 350,  500, 350,  474, 346,  474, 1158,  474, 1160,  474, 344,  474, 344,  472, 1160,  474, 348,  474, 348,  476, 350,  474, 350,  474, 348,  476, 1158,  474, 1158,  472, 1160,  474, 350,  474, 344,  472, 354,  472, 342,  476, 342,  474, 348,  474, 368,  450, 342,  476, 344,  474, 1156,  476, 348,  474, 346,  474, 1160,  476, 346,  502, 1166,  476, 346,  474, 1158,  474, 1162,  474, 342,  474, 1160,  474, 344,  474, 348,  502, 350,  474, 346,  474, 346,  476, 344,  476, 346,  476, 344,  476, 350,  474, 346,  474, 344,  474, 372,  450, 344,  476, 346,  474, 1162,  474, 1160,  474, 344,  476, 346,  476, 344,  476, 344,  474, 346,  474, 348,  474, 1162,  472, 1160,  474, 1162,  474, 350,  476, 348,  474, 352,  474, 348,  474, 1158,  474, 346,  474, 348,  474, 346,  476, 346,  474, 346,  474};  // FUJITSU_AC
uint16_t rawData_off[115] = {3264, 1624,  438, 380,  404, 422,  404, 1230,  434, 382,  428, 1206,  404, 418,  404, 422,  402, 420,  402, 1230,  404, 1230,  404, 418,  402, 416,  402, 416,  404, 1228,  406, 1230,  428, 396,  404, 416,  402, 416,  404, 422,  402, 418,  404, 416,  402, 418,  402, 418,  402, 416,  402, 420,  402, 414,  404, 414,  402, 414,  404, 1230,  404, 418,  404, 418,  428, 426,  402, 420,  402, 414,  404, 416,  402, 416,  434, 1196,  402, 418,  434, 384,  402, 418,  426, 392,  402, 1228,  404, 414,  402, 418,  404, 418,  402, 416,  402, 418,  402, 418,  402, 1230,  402, 420,  434, 1196,  404, 1230,  404, 1228,  404, 1230,  404, 1230,  436, 1198,  404};  // FUJITSU_AC
```

作成済みの関数 `void airconPowerOnOff(EspalexaDevice *d)` に赤外線送信部分を追加する。

```
    // 電源ON
    irsend.sendRaw(rawData_on, 259, 38);
    // 電源OFF
    irsend.sendRaw(rawData_off, 115, 38);
```

※ソースコードは下記に掲載。

# 4. Alexaに認識させる

作成したデバイス `FUJITSU AC` を Alexaに認識させる。
    
#### アレクサアプリでデバイスとして認識させる

<img width="30%" border="1" alt="デバイスの追加" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/38a13d20-fc67-ba44-848f-3253207f4787.png">　<img width="30%" border="1" alt="その他" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/3ede116a-db25-c911-2fd6-9084ad463d5e.png">　<img width="30%" border="1" alt="デバイスの検出" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/45e32c93-b03d-7fe4-c832-eeed850937a0.png">

<img width="30%" border="1" alt="デバイス検出中" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/987bfa09-3d0a-df4b-006a-75e9d668aec9.png">　<img width="30%" border="1" alt="検出・接続済み" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/a55a2cd7-9153-fb80-e568-54288a96e442.png">


# 5. 定型アクションを作成する

電源ONと電源OFFの２つの定型アクションを作成する。
 
#### 電源ON

|項目|設定値|備考|
|:--|:--|:--|
|定型アクション名|富士通エアコン電源オン|※任意|
|実行条件|Alexaのおしゃべり<br>└アレクサ、エアコンをつけて|※任意|
|アクション|スマートホーム<br>└照明<br>　└FUJITSU AC<br>　　└電源<br>　　　└<font color="OrangeRed"><b>オン</b></font>||

<img width="30%" border="1" alt="定型アクション設定" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/c82bec6c-ec8d-e02f-9136-787b18777e96.png">　<img width="30%" border="1" alt="定型アクション 電源オン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/367ea562-088b-31d8-8540-c86e5c6a4765.png">


#### 電源OFF

|項目|設定値|備考|
|:--|:--|:--|
|定型アクション名|富士通エアコン電源オフ|※任意|
|実行条件|Alexaのおしゃべり<br>└アレクサ、エアコンを消して|※任意|
|アクション|スマートホーム<br>└照明<br>　└FUJITSU AC<br>　　└電源<br>　　　└<font color="OrangeRed"><b>オフ</b></font>||

<img width="30%" border="1" alt="定型アクション設定" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/2bccf4aa-bb96-cfd6-c3cb-641bc97320cc.png">　<img width="30%" border="1" alt="定型アクション 電源オン" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/749e248c-b77b-1f73-1895-6d8e2aa0f233.png">

完成。

<img width="40%" border="1" alt="完成" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426739/ab9fd227-b34d-5b0b-62ed-57e4cbf86bf3.png">

動作確認

[![動作確認](http://img.youtube.com/vi/7YxF_Km2z1c/0.jpg)](http://www.youtube.com/watch?v=7YxF_Km2z1c)

本体が赤く点滅するのは、ただの飾り。
動いてる感を出したかっただけ。

# ソースコード

動いているのでいいんじゃないかと思ってる。

```
#include <Arduino.h>
#include <M5StickC.h>

// IR Remote
#include <IRremoteESP8266.h>
#include <IRsend.h>

// Espalexa
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
#include <Espalexa.h>

// Wi-Fi Configuration
boolean connectWifi();
boolean wifiConnected = false;
const char* ssid = "...";
const char* password = "wifipassword";

// Callback Functions
void airconPowerOnOff(EspalexaDevice* dev);

// IR  ( M5Stick-C 9 pin / Serial 32 pin )
const uint16_t IR_LED_PIN = 32;
IRsend irsend(IR_LED_PIN);

// LED ( M5Stick-C 10 pin )
const uint16_t LED_PIN = 10;

// Air conditioner power on
uint16_t rawData_on[259]  = {3336, 1556,  470, 354,  470, 376,  448, 1188,  448, 370,  448, 1184,  448, 348,  466, 374,  446, 348,  470, 1160,  472, 1162,  470, 388,  446, 352,  470, 372,  448, 1162,  468, 1166,  472, 372,  448, 346,  470, 372,  448, 372,  448, 348,  446, 400,  448, 372,  444, 374,  448, 346,  470, 370,  448, 346,  472, 354,  472, 350,  472, 1184,  448, 348,  472, 346,  472, 374,  450, 346,  472, 372,  448, 370,  448, 374,  448, 1186,  448, 346,  474, 350,  472, 348,  470, 348,  472, 1220,  446, 1164,  474, 1162,  472, 1162,  474, 1158,  472, 1162,  474, 1158,  472, 1164,  474, 348,  470, 350,  472, 1162,  472, 374,  448, 344,  474, 368,  450, 368,  450, 370,  450, 350,  500, 350,  474, 346,  474, 1158,  474, 1160,  474, 344,  474, 344,  472, 1160,  474, 348,  474, 348,  476, 350,  474, 350,  474, 348,  476, 1158,  474, 1158,  472, 1160,  474, 350,  474, 344,  472, 354,  472, 342,  476, 342,  474, 348,  474, 368,  450, 342,  476, 344,  474, 1156,  476, 348,  474, 346,  474, 1160,  476, 346,  502, 1166,  476, 346,  474, 1158,  474, 1162,  474, 342,  474, 1160,  474, 344,  474, 348,  502, 350,  474, 346,  474, 346,  476, 344,  476, 346,  476, 344,  476, 350,  474, 346,  474, 344,  474, 372,  450, 344,  476, 346,  474, 1162,  474, 1160,  474, 344,  476, 346,  476, 344,  476, 344,  474, 346,  474, 348,  474, 1162,  472, 1160,  474, 1162,  474, 350,  476, 348,  474, 352,  474, 348,  474, 1158,  474, 346,  474, 348,  474, 346,  476, 346,  474, 346,  474};  // FUJITSU_AC
// Air conditioner power off 
uint16_t rawData_off[115] = {3264, 1624,  438, 380,  404, 422,  404, 1230,  434, 382,  428, 1206,  404, 418,  404, 422,  402, 420,  402, 1230,  404, 1230,  404, 418,  402, 416,  402, 416,  404, 1228,  406, 1230,  428, 396,  404, 416,  402, 416,  404, 422,  402, 418,  404, 416,  402, 418,  402, 418,  402, 416,  402, 420,  402, 414,  404, 414,  402, 414,  404, 1230,  404, 418,  404, 418,  428, 426,  402, 420,  402, 414,  404, 416,  402, 416,  434, 1196,  402, 418,  434, 384,  402, 418,  426, 392,  402, 1228,  404, 414,  402, 418,  404, 418,  402, 416,  402, 418,  402, 418,  402, 1230,  402, 420,  434, 1196,  404, 1230,  404, 1228,  404, 1230,  404, 1230,  436, 1198,  404};  // FUJITSU_AC

Espalexa espalexa;

// ----------------------------------------------------------------------------
void setup() {
  irsend.begin();
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);

  // Initialise wifi connection　★サンプルのロジックをちっとだけ変えた。
  while (!wifiConnected) {
      wifiConnected = connectWifi();
      if(wifiConnected){
          Serial.println("Successful connection!");
          // Define the required devices. 
          espalexa.addDevice("FUJITSU AC", airconPowerOnOff, EspalexaDeviceType::onoff); 
          espalexa.begin();
      } else {
          Serial.println("Cannot connect to WiFi. Please check data and reset the ESP.");
          delay(2500);
      }
  }
}

void loop() {
   espalexa.loop();
   delay(1);
}

// Function to turn on / off the power of the air conditioner
void airconPowerOnOff(EspalexaDevice *d) {
    if (d == nullptr) return;
    Serial.println(d->getValue());
    if (d->getValue()) {
        Serial.println("Power On");
        irsend.sendRaw(rawData_on, 259, 38);
        blinkBuiltinLED();
    } else {
        Serial.println("Power Off");
        irsend.sendRaw(rawData_off, 115, 38);
        blinkBuiltinLED();
    }
}

// Blink the built-in LED
void blinkBuiltinLED() {
    digitalWrite(LED_PIN, LOW);
    delay(500);
    digitalWrite(LED_PIN, HIGH);
}

// connect to wifi – returns true if successful or false if not
boolean connectWifi(){
  boolean state = true;
  int i = 0;
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 20){
      state = false; break;
    }
    i++;
  }
  Serial.println("");
  if (state){
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println("Connection failed.");
  }
  return state;
}
```

# おわりに

実はこれを完成させるまでに半年ぐらいかかっている。何度やっても赤外線が反応してくれなくて諦めていた時期もあったんだけど、オチは `IR Unit` が故障していただけだったという...

`IR Unit` 新しく買ってきて、もう一度やりなおしたらめちゃくちゃ簡単にできた。
電源ON/OFFぐらいで良いのであればこれで十分だと思う。夏前に完成させることができてよかった。

# 参考

- [Alexaアプリ（Web版）](https://alexa.amazon.co.jp/spa/index.html?)
- [Arduino IDE](https://www.arduino.cc/en/software)
- [Espalexa](https://www.arduino.cc/reference/en/libraries/espalexa/)
- [Espalexa（GitHub）](https://github.com/Aircoookie/Espalexa)
- [IRremoteESP8266](https://www.arduino.cc/reference/en/libraries/irremoteesp8266/)
- [M5StickCをAlexa連携デバイス化する](https://qiita.com/norippy_i/items/d8456e3c84432e279754)
- [Espalexa by Christian Schwinne](https://platformio.org/lib/show/5525/Espalexa)
- [M5Displayクラスの使い方](https://lang-ship.com/reference/unofficial/M5StickC/Tips/M5Display/)