音声でWioLTEのLED発光色を変更する

今年、本ブログにて以下の2つのIoT関連記事を公開させていただきました。

クラウドと連携したIoTの仕組みを簡単に開発できる優れものであるWioLTEを購入し学習を進めています。 今回はAWS IoTのデバイスシャドウを用いてLED発光色のステータスを管理する仕組みを作りました。
スマートスピーカーのGoogle Home(Mini)を購入しました。 クラウドと連携させ、SORACOM SIMの回線状態を音声で変更するアプリケーションを作成してみました。

そこで気付いたのです。
この2つのアイディアを組み合わせると面白いことができそうだと。

アーキテクチャ

以下の構成で、音声によりWioLTEオンボードのLED発光色を変更する仕組みを作ってみます。

IFTTTからAPI Gateway経由でLambda関数を呼び出すところは前回と同じ、Lambda関数でAWS IoT Coreのデバイスシャドウ(Thing Shadow)の値を書き換えれば、SORACOM Beam経由で接続されたWioLTEのLED発光色を変更できるはずです。

ソースコード

先日のWioLTEに関する記事では、デバイスシャドウで受け渡す色情報は”red”,”blue”等の文字列にしておりましたが、今回はLambda側で設定した任意の色で光ることができるよう、HTMLカラーコードで受け渡すようにしてみます。

理由はGoogleアシスタントから取得できる音声認識データが、”赤”,”青”等の日本語の漢字変換済み文字列で取得されるので、”赤”⇒”red”⇒”#ff0000″と2回変換することが無駄と感じられたことです。

WioLTE側

デバイスシャドウのカラーコードが変更されたら、そのカラーコードでLEDを光らせる単純なものです。

16進数の解析処理で異常系処理が足りないのですが、試してみたところ暴走せずに適当な色に光るので、まぁいいかな、と・・・

#include <WioLTEforArduino.h>
#include <WioLTEClient.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <stdio.h>
#include <string.h>

#define APN               "soracom.io"
#define USERNAME          "sora"
#define PASSWORD          "sora"

#define MQTT_SERVER_HOST  "beam.soracom.io"
#define MQTT_SERVER_PORT  (1883)

#define ID                "(デバイス名)"
#define OUT_TOPIC         "$aws/things/(デバイス名)/shadow/update"
#define IN_TOPIC          "$aws/things/(デバイス名)/shadow/update/delta"

WioLTE Wio;
WioLTEClient WioClient(&Wio);
PubSubClient MqttClient;

void callback(char* topic, byte* payload, unsigned int length) {
  char subsc[length];
  for (int i = 0; i < length; i++) subsc[i]=(char)payload[i];
  subsc[length]='\0';
  SerialUSB.println("### Subscribe");
  SerialUSB.println(subsc);

  // JSON parse
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(subsc);

  if (!root.success()) {
    SerialUSB.println("parseObject() failed");
  }
  else{
    // Change LED color
    const char* ledcolor = root["state"]["LED"];
    SerialUSB.print("Change LED color to: ");
    SerialUSB.println(ledcolor);

    int r;int g;int b;

    char tmp[2];
    strncpy(tmp,ledcolor  ,2);sscanf(tmp,"%x",&r);
    strncpy(tmp,ledcolor+2,2);sscanf(tmp,"%x",&g);
    strncpy(tmp,ledcolor+4,2);sscanf(tmp,"%x",&b);

    Wio.LedSetRGB(r,g,b);
    SerialUSB.println("### LED Status Sent.");  

    // RePublish LED Status
    char data[100];
    sprintf(data,"{\"state\": {\"reported\" : {\"LED\" : \"%s\"}}}",ledcolor);
    MqttClient.publish(OUT_TOPIC, data);
  }
}

void setup() {
  delay(200);

  SerialUSB.println("");
  SerialUSB.println("--- START ---------------------------------------------------");
  
  SerialUSB.println("### I/O Initialize.");
  Wio.Init();
  
  SerialUSB.println("### Power supply ON.");
  Wio.PowerSupplyLTE(true);
  delay(500);

  SerialUSB.println("### Turn on or reset.");
  if (!Wio.TurnOnOrReset()) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Connecting to \""APN"\".");
  if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Connecting to MQTT server \""MQTT_SERVER_HOST"\"");
  MqttClient.setServer(MQTT_SERVER_HOST, MQTT_SERVER_PORT);
  MqttClient.setCallback(callback);
  MqttClient.setClient(WioClient);
  if (!MqttClient.connect(ID)) {
    SerialUSB.println("### ERROR! ###");
    return;
  }
  int qos=0;
  MqttClient.subscribe(IN_TOPIC,qos);
  SerialUSB.println("### Setup completed.");

  // Send Initialize LED Status
  char *data = "{\"state\": {\"reported\" : {\"LED\" : \"000000\"}}}";
  MqttClient.publish(OUT_TOPIC, data);
  SerialUSB.println("### LED Status Sent.");  
}

void loop() {
  MqttClient.loop();
}

Lambda関数

IFTTTから来た日本語の色情報からカラーコードに変換して、デバイスシャドウのdesiredに書き込むところを担当します。デモンストレーション用にソラコムのコーポレートカラーであるチェレステ色(#34cdd7)も登録しました。

前回の記事でロールを作成していないようであれば、使用するサービスとしてLambda、アタッチするポリシーに AWSIoTDataAccess を指定してロールを作成しておきます。

import boto3
import json
sns = boto3.client('sns')
iot = boto3.client('iot-data')
thingName = '(デバイス名)'
temp_threshold = 30

def lambda_handler(event, context):
    # eventを分解
    ledcolor_text = event['ledcolor']
    
    # テキストによりLED発光色を決定
    if ledcolor_text=="赤":
        ledcolor_desired="ff0000"
    elif ledcolor_text=="緑":
        ledcolor_desired="00ff00"
    elif ledcolor_text=="青":
        ledcolor_desired="0000ff"
    elif ledcolor_text=="黄色":
        ledcolor_desired="ffff00"
    elif ledcolor_text=="紫":
        ledcolor_desired="ff00ff"
    elif ledcolor_text=="水色":
        ledcolor_desired="00ffff"
    elif ledcolor_text=="白":
        ledcolor_desired="ffffff"
    elif ledcolor_text=="ソラコム":
        ledcolor_desired="34cdd7"
    else:
        ledcolor_desired="000000"

    shadow_stream = iot.get_thing_shadow(thingName = thingName)
    shadow_string = json.loads(shadow_stream['payload'].read().decode('utf-8'))
    ledcolor_status = shadow_string['state']['desired']['LED']

    if ledcolor_desired != ledcolor_status:
        # IoT シャドウを更新
        payload = {"state": {"desired": {"LED": ledcolor_desired }}}
        response = iot.update_thing_shadow(
            thingName = thingName,
            payload = json.dumps(payload)
        )

    return 'desired={} status={}'.format(ledcolor_desired,ledcolor_status)

API Gatewayの設定

API Gatewayの設定方法は前回と同じです。

APIの作成をクリックし、API名を入力します。


続いてアクションからメソッドの作成を選択し、POSTアクションを作成、作成したLambda関数を呼び出すようにします。

最後にアクションからAPIのデプロイを選択します。
APIエンドポイントのURLが表示されるので、それをメモしておきます。

IFTTTにて Content-Type 以外のヘッダが指定できないため、API GatewayのAPI Keyによる認証ができません。悪用されないようご注意下さい。

IFTTTの設定

IFTTTの設定について、前回は休止状態と速度クラス別に5パターンのアプレットを登録しましたが、今回は色別に設定を登録するのが面倒であるため以下の2パターンにします。

  • LEDを点灯、(色名)
  • LEDを消灯

今回もNew Appletをクリックし、thisにはGoogle Assistantの”Say a phrase with a text ingredient”を選択します。

“What do you want to say”の箇所に「LEDを点灯、$」と入力しました。

thatには今回もwebhookを選択し、API GatewayのURLと色名を以下のように入力します。
これで$の箇所で認識された色名を後続に渡すことができるようになります。

『LEDを消灯』の方の設定方法は割愛しますが、triggerは”Say a simple phase”で単純に設定すればOKです。

SORACOM UG #10 でデモしました。

以上で設定は完了です。
うまく設定されていれば、『OK Google, LEDを点灯、赤』と言えば赤色に、『OK Google, LEDを点灯、ソラコム』と言えばチェレステ色に点灯するはずです。

作ってみて思ったのですが(今回は色が変わるだけですが)音声コントロールでデバイスが操作できるとワクワクして妄想が膨らむのは気のせいでしょうか。

そんな期待を込めて、先日のSORACOM UG #10のLT枠で発表させていただきました。

外部勉強会では初めてのLTだったもので、発表に至らぬところがあれば失礼いたしました。
良い経験をさせて頂いたので、今後の学習・発表に繋げて行ければと考えています。

次のアイディアは何にしようかな・・・