M5StickCをAlexa連携デバイス化する

#はじめに
音声認識をインターフェースにするデバイス便利ですよね。自分も既製品を結構買っています。
でも既製品だけじゃ物足りなくて、自分でオリジナルのデバイス作りたいと思ったことないですか？

この動画を見てください。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">教えてもらったespalexaライブラリを使って、改めてM5StickCのAlexa連携デバイス化をやってみた。<br>前回のらON,OFFだけだったけど、今回は明るさの0~255が送れるし、RGBもそれぞれ送れるので、より細かな処理ができる！<a href="https://twitter.com/hashtag/alexa?src=hash&amp;ref_src=twsrc%5Etfw">#alexa</a> <a href="https://twitter.com/hashtag/M5StickC?src=hash&amp;ref_src=twsrc%5Etfw">#M5StickC</a> <a href="https://t.co/Nu886lsWf6">pic.twitter.com/Nu886lsWf6</a></p>&mdash; norippy@Hardware&amp;Alexa skillやってます (@Norio_Delux) <a href="https://twitter.com/Norio_Delux/status/1179009607198359552?ref_src=twsrc%5Etfw">October 1, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

どうせIFTTT連携してるんでしょ？と思いがちですが、IFTTT使ってません。
実際、従来のやり方は結構面倒で、IFTTT連携して、blynkを使ってwebhookを送って…と設定手順も含めて結構複雑でした。
これに対して、動画のやり方は、M5StickCをAlexa連携デバイスとしてAlexaアプリに登録して動かしています。

つまり、
**M5StickCがガチのAlexa連携デバイスになって動いているということです**

スクショ画像で示すようにAlexaアプリを使ってトリガーやアクションを決めれます。
<img width="200" alt="IMG_BE2FCA80233E-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/fa7a8f59-389e-e54f-50e6-8a1907e897e5.jpeg">

Alexa連携デバイスと認識されていると、Alexaアプリにある定型アクションを使えるようになるので、M5StickCを動かすための発話トリガーの変更も簡単にできたり、一緒にニュースを読み上げたり、音楽をかけたりと複数のアクションを行うことも可能です。
ユーザーがやることはこの定型アクション等で送られてくる情報に対してM5StickCをどう動かすのかコードを書くだけでOK。

開発自体もとても簡単で、EspalexaというライブラリをM5StickCに書き込んで、WiFiに接続するだけです。

[Espalexa github page](https://github.com/Aircoookie/Espalexa)

実際に使ってみてわかりましたが。Phillips社のHueのエミュレートをしているようです。

#開発してみる
興味がある方が遊べるように、使って分かった事も踏まえて、実際にM5StickCに書き込んで動かす手順を残しておきます

###(準備)開発環境
以下に開発環境と使ったデバイスを示します
-MacBook Pro (Early 2015)
-Arduino IDE v1.8.9
-M5StickC

###1.ライブラリの追加
M5StickCのライブラリ等は既に持っていらっしゃる方はインストール済みかと思うので割愛。
EspalexaはArduino IDEのライブラリマネージャーでインストールが可能になっています。
ライブラリマネージャーは
**[スケッチ] -> [ライブラリをインクルード] -> [ライブラリの管理]**
で表示できます。表示できたら`espalexa`と検索して、インストール。
もちろんgithubからcloneしてArduinoのlibrariesフォルダの下に置くでも問題ありません。
<img width="500" alt="スクリーンショット 2019-10-02 15.13.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/2dbc00d6-57a9-2c0b-784f-ebded41bb06d.png">

###2.サンプルライブラリを読み解く
インストールが完了すると、Espalexaのサンプルコードが見れるようになります。
**[ファイル]->[スケッチ例] ->[Espalexa]**
<img width="693" alt="スクリーンショット 2019-10-02 15.26.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/63b373f7-2c42-1e28-33f4-e0b0ad41f60b.png">
今回は`EspalexaColor`というサンプルスケッチを選びます。少しではありますが、説明を追記したので、どんな風に動いているか読んでみてください。

``` .cpp
/*
 * This is a basic example on how to use Espalexa with RGB color devices.
 */ 
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
//#define ESPALEXA_ASYNC //ここはコメントアウトすること。デバック用の設定だけど、なぜかコンパイルが通らない(謎)
#include <Espalexa.h>

// prototypes
boolean connectWifi();

//callback function prototype
void colorLightChanged(uint8_t brightness, uint32_t rgb);
//コールバックの定義。ここの引数の書き方でデバイスが自動的にRGBなのか、単色なのかが判断されている。
//例えばここで
//void brightnessChanged(uint8_t brightness);
//のように定義すると、単色のPhillips Hueとして振る舞います。

// Change this!! SSIDとPasswordは環境に合わせて要変更！
//Alexaアプリが入っているスマートフォンと同じローカルネットワークにすること
const char* ssid = "...";
const char* password = "wifipassword";

boolean wifiConnected = false;

Espalexa espalexa;

void setup()
{
  Serial.begin(115200);
  // Initialise wifi connection
  wifiConnected = connectWifi();
  
  if(wifiConnected){
    espalexa.addDevice("Color Light", colorLightChanged);//ここでデバイス追加を行っている。
    //"Color Light"はAlexaアプリで表示される名前。
    //後でAlexaアプリから変更可能です。日本語も可能。
    //最大10台まで追加可能。

    espalexa.begin();//全てのデバイスを追加完了後(設定完了後)にbeginを実行する
    
  } else
  {
    while (1) {
      Serial.println("Cannot connect to WiFi. Please check data and reset the ESP.");
      delay(2500);
    }
  }
}
 
void loop()
{
   espalexa.loop();//このループがないと動きません。
   delay(1);
}

//the color device callback function has two parameters
//ここで各デバイスのコールバックのコードを記述する。
void colorLightChanged(uint8_t brightness, uint32_t rgb) {
  //do what you need to do here, for example control RGB LED strip
  //brightnessとRGBが別々なのは、Alexaの仕様のようです。
  Serial.print("Brightness: ");
  Serial.print(brightness);
  Serial.print(", Red: ");
  Serial.print((rgb >> 16) & 0xFF); //get red component
  Serial.print(", Green: ");
  Serial.print((rgb >>  8) & 0xFF); //get green
  Serial.print(", Blue: ");
  Serial.println(rgb & 0xFF); //get blue
}

// 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 > 40){
      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;
}
```

サンプルコードを見ると、割とシンプルなことがわかります。
基本的に
1. addDevice()でデバイスを追加
2. callbackの関数の処理を書く
だけです。
1つのESP32が最大10個の別のデバイスとして認識されるので、コールバックを分けることが出来、色々な処理を1つのデバイスで実現することができます。


###3.サンプルコードをベースに、コードを変更してみる
動画とは異なり、純粋にどのような動作をするのかM5StickCのLCDで見れるように、EchoデバイスやAlexaアプリから送られてくる命令を表示するサンプルを作りました。割愛してないので、ssidとpasswordを変更したら動作します。

```.cpp
/*
   This is a basic example on how to use Espalexa with RGB color devices.
*/
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif
//#define ESPALEXA_ASYNC //コメントアウト
#include <Espalexa.h>
#include <M5StickC.h>

// prototypes
boolean connectWifi();

//callback function prototype
void colorLightChanged(uint8_t brightness, uint32_t rgb);
void brightnessChanged(uint8_t brightness);

// Change this!!
const char* ssid = "your ssid";
const char* password = "your password";

boolean wifiConnected = false;

Espalexa espalexa;

#define LED 10 //M5StickCに内蔵されているLEDを使う

void setup()
{
  Serial.begin(115200);
  pinMode(LED, OUTPUT);
  digitalWrite(LED, HIGH);
  // Initialise wifi connection
  wifiConnected = connectWifi();

  if (wifiConnected) {
    espalexa.addDevice("フルカラーデバイス", colorLightChanged);
    espalexa.addDevice("明るさデバイス", brightnessChanged);
    espalexa.begin();

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

  M5.Lcd.setRotation(1);
  M5.Lcd.setTextFont(4);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.printf("I'm linking to Alexa");


}

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


void brightnessChanged(uint8_t brightness) {
  Serial.print("brightness :");
  Serial.println(brightness);

  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 16);
  M5.Lcd.setTextFont(7);
  M5.Lcd.printf("%d", brightness);
}


//the color device callback function has two parameters
void colorLightChanged(uint8_t brightness, uint32_t rgb) {
  //do what you need to do here, for example control RGB LED strip
  Serial.print("Brightness: ");
  Serial.print(brightness);
  Serial.print(", Red: ");
  Serial.print((rgb >> 16) & 0xFF); //get red component
  Serial.print(", Green: ");
  Serial.print((rgb >>  8) & 0xFF); //get green
  Serial.print(", Blue: ");
  Serial.println(rgb & 0xFF); //get blue

  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextFont(2);
  M5.Lcd.setTextSize(1);
  M5.Lcd.printf("Bright:%d\n", brightness);
  M5.Lcd.printf("R: %d\n", (rgb >> 16) & 0xFF);
  M5.Lcd.printf("G: %d\n", (rgb >> 8) & 0xFF);
  M5.Lcd.printf("B: %d\n", rgb & 0xFF);
}

// 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 > 40) {
      state = false; break;
    }
    i++;
  }
  Serial.println("");
  if (state) {
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    digitalWrite(LED, LOW);
    delay(3000);
    digitalWrite(LED, HIGH);
  }
  else {
    Serial.println("Connection failed.");
    for (int i = 0; i < 5; i++) {
      digitalWrite(LED, LOW);
      delay(500);
      digitalWrite(LED, HIGH);
      delay(500);
    }
  }
  return state;
}
```
コードをM5StickCに書き込みすると

- 明るさのみが設定できる「明るさデバイス」
- 明るさと、プリセットされた色の選択ができる「フルカラーデバイス」

の2つのAlexa連携デバイスがAlexaアプリから認識できるようになります。

###4.Alexaアプリを使ってデバイスの登録を行う。
Echoデバイスを所有している方は、Alexaアプリをスマートフォンにインストールしているかと思います。
このAlexaアプリよりデバイスの追加をしてください。
下のタブから**[デバイス]**を選択し、右上の十字アイコンをタップ。表示されるウィンドウから**[デバイスを追加]**を選択。
セットアップするデバイスの種類は**[その他]**を選択してください。すると検出が始まります。

<img width="200" alt="add device.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/b1062cc9-4774-d6c4-6358-f590b46a6d80.png">　→　<img width="200" alt="IMG_2776.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/2bf4a46c-5137-3d87-67f3-f8872e2673a3.png">　→　<img width="200" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/834b778f-0784-770a-9a84-fe4aa749b2cf.png">　

検出が完了すると明るさデバイスと、フルカラーデバイスの2つが検出されています。ここからは1つずつしか出来ませんが、デバイスをセットアップすると、M5StickCが晴れてAlexa連携デバイスとして動くようになります！

　<img width="200" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/c89ba9c9-0894-6ded-01cf-81a36e6de1ef.png">　→　<img width="200" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/a00c3c5a-44fa-6b9d-5a61-69d32d7d0bef.png">　→　<img width="200" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/c7efc3db-4a31-4088-c3b7-bbbe86d11c89.png"> 

###5.AlexaアプリやEchoデバイスから動かしてみる。
試しにAlexaアプリからフルカラーデバイスを操作します。アプリで「フルカラーデバイス」選択して、操作すると、M5StickC上で明るさやRGBの各色のパラメータを確認することが出来ます。
<img width="200" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/c9014503-20d8-5844-4735-0109216f6f24.png"> 　　→　　<img width="400" rotate="90" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/3041f43c-0f33-42cf-87b6-2620136333c8.png">

同様に明るさセンサーも表示されます
<img width="200" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/ae66cc31-b001-abe1-a60a-d421681359f6.png"> 　　→　　<img width="400" rotate="90" alt="IMG_2777.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324560/ff7d4e0d-8bac-6865-5f30-6ac849f8865d.png">

0~100%という値が0~255で表示されるので、ON,OFF以外に使う時は注意が必要かもしれません。

###6.定型アクションを登録して、音声で操作してみる
Amazon Echoはカスタムのスキルを動かすだけでは無く、定型アクションという機能も有しています。
この機能を利用して任意の言葉でデバイスを動かしてみましょう。実際にオリジナルデバイスを音声をトリガーに操作できると感動します。
やり方は色々なサイトに情報があるので、そちらを参考にしてみてください。



##Tips
コードを修正するなかで、デバイスの名称を変えたり、RGBも操作できるようにしたくなってコールバックの定義の見直しをすることもあるかと思います。その時は一度Alexaアプリからデバイスを削除して、再登録してください。
この操作を行わないと、設定変更後のデバイスが検出されません。ご注意ください。

またこの記事では一部の機能しか使っていません。デバイスに初期値を設定することも出来たりするので、気になる方は他のサンプルコードやgitのReadmeを見てもらえればと思います。

#最後に
今年の初め頃は自分で**[RDM-01](http://www.rino-make-fun.com/2019/02/05/rad-01-dtmf-decoder-module/)**という基板を開発し、DTMFという信号を使ってAlexa連携のオリジナルデバイスを開発できるシステムを構築し販売を行なってきましたが、それもこのEspalexaで不要になりそうです。

このEspalexaを使えば今まで出来なかった電子機器の操作ができる可能性があります。
みなさんもオリジナルのデバイスを開発してみてはいかがでしょうか？