M5StickC(ESP32)をAlexaに対応させよう

一つ前の記事で赤外線リモコンを作ったので

M5StickCで赤外線リモコンを作ろう - Qiita
https://qiita.com/coppercele/items/ed91646944ca28ff0c07

EspAlexaを使ってAlexaから操作できるようにします
M5StickCと書いてありますがESP32 Dev kit,M5Stack,Atom liteなどでも大丈夫です

ちなみにESP32をAlexa対応させるライブラリは
・esp8266-alexa-wemo-emulator
・fauxmoESP
・EspAlexa

といろいろあるのですが、全てを渡り歩いた結果EspAlexaに落ち着きました

ライブラリマネージャから EspAlexaをインストールします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/d640c913-2fe0-b4db-58d8-469ae2eedc91.png)

EspAlexaのサンプルからFullyFeaturedを開きます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/7cc3a626-e049-47b3-7476-ef9253c9ef3d.png)

あとはサンプルを改造していくのですが最低限のポイントだけ挙げます

赤外線送信についてはこちらを参照してください

M5StickCで赤外線リモコンを作ろう - Qiita
https://qiita.com/coppercele/items/ed91646944ca28ff0c07

```cpp
// コールバック関数のプロトタイプ
void terebiChanged(EspalexaDevice* dev);
void danbouChanged(EspalexaDevice* dev);
void reibouChanged(EspalexaDevice* dev);

void setup()
{
  // デバイスを登録していく
  // 第1引数はAlexa上での表示名(後で変更できる)
  // 第2引数はコールバック関数の名前
  espalexa.addDevice("TEREBI", terebiChanged, EspalexaDeviceType::onoff);    // オンオフのみ
  espalexa.addDevice("DANBOU", danbouChanged, EspalexaDeviceType::dimmable); // 調光できるデバイスエアコンの場合は℃
  espalexa.addDevice("REIBOU", reibouChanged, EspalexaDeviceType::dimmable); 

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

// コールバック関数
void terebiChanged(EspalexaDevice *d)
{
  // LED点灯
  digitalWrite(GPIO_NUM_10, LOW);
  // テレビに送信
  irsend.sendNEC(0x15748B7);
  delay(500);
  // LED消灯
  digitalWrite(GPIO_NUM_10, HIGH);
}

void danbouChanged(EspalexaDevice *d) {
  // 「アレクサ、暖房を??℃にして」としゃべると実行される
  if (d == nullptr)
    return;

  // degreeには「??℃にして」の??が入る
  uint8_t degrees = d->getDegrees(); //for heaters, HVAC, ...
  if (30 < degrees)
    return;
  digitalWrite(GPIO_NUM_10, LOW);

  if (d->getValue()){
    Serial.println("ON");
    ac.setPower(true);
  }
  else {
    Serial.println("OFF");
    ac.setPower(false);
  }
  // 風速：オート　暖房　26度
  ac.setFan(kMitsubishiHeavy152FanAuto);
  ac.setMode(kMitsubishiHeavyHeat);
  ac.setTemp(degree);
  ac.setSwingVertical(kMitsubishiHeavy152SwingVLow);
  ac.setSwingHorizontal(kMitsubishiHeavy152SwingVLow);
  ac.send();
  delay(500);
  digitalWrite(GPIO_NUM_10, HIGH);
}

```

自分が運用してるソースを置いておきます

<details><summary>ExpAlexa.ino</summary>
<div>

```cpp:ExpAlexa.ino

#include <WiFi.h>
#include <Espalexa.h>
#include <ESP32Servo.h>

const char *ssid = "";
const char *password = "";

Servo servo; // create four servo objects 
int servoPin = 12;

// Published values for SG90 servos; adjust if needed
int minUs = 500;
int maxUs = 2400;

int pos = 0; // position in degrees

// prototypes
bool connectWifi();

//callback functions
//new callback type, contains device pointer
void terebiChanged(EspalexaDevice *dev);

bool wifiConnected = false;

Espalexa espalexa;

void setup()
{
    Serial.begin(115200);

    pinMode(GPIO_NUM_10, OUTPUT);
    digitalWrite(GPIO_NUM_10, HIGH);
    servo.setPeriodHertz(50); // Standard 50hz servo
    servo.attach(servoPin, minUs, maxUs);
    servo.write(93);
    delay(300);
    servo.detach();

    // Initialise wifi connection
    wifiConnected = connectWifi();

    if (!wifiConnected)
    {
        while (1)
        {
            Serial.println("Cannot connect to WiFi. Please check data and reset the ESP.");
            delay(2500);
        }
    }

    // Define your devices here.
    espalexa.addDevice("NIKAI", terebiChanged); //non-dimmable device

    espalexa.begin();
}

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

//our callback functions
void terebiChanged(EspalexaDevice *d)
{
    if (d == nullptr)
        return; //this is good practice, but not required
if (d->getValue()){

    Serial.println("ON");

    if (!servo.attached()) {
      servo.attach(servoPin, minUs, maxUs);
    }
    servo.write(93);
    delay(300);
    servo.write(120);
    delay(300);
    servo.write(65);
    delay(300);
    servo.write(93);
    delay(300);
//    IKKAI
//    servo.write(93);
//    delay(300);
//    servo.write(115);
//    delay(300);
//    servo.write(93);
//    delay(300);
  }
  else {
    Serial.println("OFF");
    if (!servo.attached()) {
      servo.attach(servoPin, minUs, maxUs);
    }
    servo.write(93);
    delay(300);
    servo.write(120);
    delay(300);
    servo.write(65);
    delay(300);
    servo.write(93);
    delay(300);
//      servo.write(93);
//      delay(300);
//      servo.write(75);
//      delay(300);
//      servo.write(93);
  }
  servo.detach();

}

// connect to wifi â€“ returns true if successful or false if not
bool connectWifi()
{
    bool 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;
}

```

</div>
</details>

M5StickCにスケッチを書きこんだらAlexaデバイスとして登録します

スマホアプリだけで動作しないので実機を用意してください
Alexa搭載タブレットなら大丈夫かもしれません

Amazon.co.jp： Echo Flex (エコーフレックス) プラグイン式スマートスピーカー with Alexa: Kindleストア
https://www.amazon.co.jp/dp/B07PGX7GC9/


Amazon.co.jp： Echo Dot (エコードット)第3世代 - スマートスピーカー with Alexa、チャコール: Kindleストア
https://www.amazon.co.jp/dp/B07PFFMQ64/

スマホアプリからデバイスタブを表示して右上の＋マーク

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/89bc0b63-2628-0152-f6cb-d1e57d6d091f.png)

一番下のその他

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/3ff919bd-7b1a-e63f-0097-f077dacb2a58.png)

デバイスを検出ボタンをタップ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/4293103f-6009-68ca-1d02-c89eb7400299.png)

しばらく待つとデバイスが検出され登録されます
デバイス名の検出は日本語対応されてない(みたい)なのでローマ字から名前を編集します
めんどくさいのでローマ字のままにしようとしましたが認識してくれませんでした

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/b926ebf5-4448-ea2b-4a19-37b69b570bee.png)

この状態になれば画面上の電源ボタンを押すか「アレクサ、テレビをつけて(ONにして)」とアレクサに言えばデバイス名に紐づいたコールバック関数が実行されます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298776/6d163c1f-39c9-0d53-247b-b5c2de607252.png)

エアコンの場合は「アレクサ、暖房を26度にして」などと言えばgetDegrees()で指定の温度が取得できます

##サーボでシーリングライトを操作してみた##

赤外線リモコンと同様にサーボを動かすと壁のスイッチを押すことができます

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/Espalexa?src=hash&amp;ref_src=twsrc%5Etfw">#Espalexa</a> はいいぞ<a href="https://twitter.com/hashtag/ESP32?src=hash&amp;ref_src=twsrc%5Etfw">#ESP32</a> <a href="https://t.co/ozWspDGH46">pic.twitter.com/ozWspDGH46</a></p>&mdash; もけ@ムギ㌠ (@coppercele) <a href="https://twitter.com/coppercele/status/1257288386286505990?ref_src=twsrc%5Etfw">May 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

```cpp
#include <ESP32Servo.h>

void nikaiChanged(EspalexaDevice *d) {

    if (!servo.attached()) {
      servo.attach(servoPin, minUs, maxUs);
    }
    servo.write(93);
    delay(300);
    servo.write(120);
    delay(300);
    servo.write(65);
    delay(300);
    servo.write(93);
    delay(300);
}

```

このように物理的にスイッチを押すこともできるのであらゆるものをAlexaで操作しましょう！