M5Stack NeoPixel互換 LEDテープでLチカ

 先日の記事で使った距離センサを買った時に、面白そうだったのでついでに買ってみた LED テープを試してみたいと思います。

www.switch-science.com

 ドキュメントはこちらに用意されています。

docs.m5stack.com

LEDテープ接続

 まずは LED テープを M5Stack に接続します。 LED テープの両端は Grove コネクタになっているので、 Grove ケーブルで M5Stack に接続します。 LED テープは複数のテープを Grove ケーブルで連結していくことができるようになっていますが、 Input と Output の向きが決まっていますので、裏面を見て方向を確認し、 Input 側を M5Stack と接続します。今回は1本しか接続していませんが、複数接続する場合は1本目の Output が2本目の Input に接続されるようにします。

f:id:akanuma-hiroaki:20190302220746j:plain

 今回使っている LED テープは50cmのものなので、接続すると下記のような感じになります。ちなみに Grove ケーブルは付属していませんので、別途用意する必要があります。

f:id:akanuma-hiroaki:20190302220818j:plain

ユニット追加

 それでは LED を操作してみたいと思いますが、距離センサの時と同様に UI Flow で動かしてみたいと思いますので、まずは LED テープのユニットを追加します。エミュレータの左下の + ボタンをクリックします。

f:id:akanuma-hiroaki:20190302214851p:plain

 UI Flow が対応しているユニットのリストが表示されますので、 NEOPIXEL を選択します。 Port は A で、count には LED の数を設定します。50cm のテープには 72 個の LED があったので、72 を設定しています。設定したら OK をクリックします。

f:id:akanuma-hiroaki:20190302215005p:plain

 設定されるとエミュレータの左下に、追加されたユニットということで neopixel0 が表示されます。

f:id:akanuma-hiroaki:20190302215110p:plain

LED 操作

 ユニットが追加されると Units メニューに Neopixel が表示されるようになり、 LED を操作するための様々なブロックが使用できるようになります。

f:id:akanuma-hiroaki:20190302215212p:plain

 まずは LED 全体の色を1秒おきに切り替えるサンプルです。下記のようにブロックを配置します。

f:id:akanuma-hiroaki:20190302215302p:plain

 実際に動かしてみた時の様子はこちらです。この例では特に明るさは設定していませんが、デフォルトで結構明るく点灯するようになっています。

 もう一つのサンプルとして LED の明るさを徐々に変化させてみます。0 から 100 の間で明るさを増減させています。

f:id:akanuma-hiroaki:20190302215347p:plain

 実際の様子はこちらです。

まとめ

 今回使用した LED テープは 50cm のものですが、他に 10cm, 20cm, 100cm, 200cm のバリエーションがあり、さらに連結もしていけます。テープという名の通り裏側の紙を剥がせばどこかに貼り付けることも簡単にできますので、デジタルサイネージ的なものやインジケーター的なものも結構簡単に作れてしまいそうです。

 距離センサ等の他のユニットと組み合わせて使えば簡単にプロトタイピングできそうですが、 M5Stack Core や Gray では Grove コネクタが一つしかついていないので、 Grove コネクタを複数備えた他の M5Stack が欲しいところです。

SORACOM Lagoon の Free プランで Harvest のデータをグラフ表示

 前回の記事では if-up2019 で発表された Unified Endpoint のことを書きましたが、他にもいくつか発表があり、そのうちの一つとして SORACOM Lagoon に Freeプランと Proプランが追加されたという発表がありました。

blog.soracom.jp

 今までは月額で980円ということで、興味はあったものの試していなかったのですが、 Freeプランでは一人で使ってみる分には無料で使えるので、改めて使ってみました。

SORACOM Lagoon の利用開始設定

 Lagoon を使うには利用設定が必要になります。ユーザコンソールの共通メニューから ダッシュボード作成・共有 をクリックします。

f:id:akanuma-hiroaki:20190223123230p:plain

 Lagoon の利用開始設定の画面が表示されますので、 SORACOM Lagoon の利用を開始する ボタンをクリックします。

f:id:akanuma-hiroaki:20190223123507p:plain

 料金プランの選択ダイアログが表示されますので、 Freeプラン を選択して 続行する をクリックします。

f:id:akanuma-hiroaki:20190223123648p:plain

 Lagoon を利用するためのパスワードを設定します。アカウントには現在ユーザコンソールにログインしているメールアドレスが使われ、そのメールアドレスとここで設定するパスワードで Lagoon を利用できるようになります。パスワードを入力したら 利用開始 をクリックします。

f:id:akanuma-hiroaki:20190223123822p:plain

 すると Lagoon のユーザが追加されますので、 SORACOM Lagoon console にアクセス をクリックします。

f:id:akanuma-hiroaki:20190223124058p:plain

 Lagoon へのログインフォームが表示されますので、先ほど設定した時のメールアドレスとパスワードを入力して Lagoon にログインします。

f:id:akanuma-hiroaki:20190223124208p:plain

Lagoon で Harvest のデータを表示する

 Lagoon では SORACOM Harvest に収集したデータを簡単に表示することができますので試してみます。 Lagoon にログインすると下記のように Home Dashboard が表示されますが、最初は何もありません。ダッシュボードを追加するために、まずは左上の Home をクリックします。

f:id:akanuma-hiroaki:20190223124611p:plain

 Home 画面で 新しいダッシュボード をクリックします。

f:id:akanuma-hiroaki:20190223124807p:plain

 ダッシュボードには様々なパネルを追加することができます。今回はグラフ表示のために Graph を選択します。

f:id:akanuma-hiroaki:20190223124907p:plain

 パネルが追加されたら Panel Title をクリックして表示されるメニューから 編集 をクリックします。

f:id:akanuma-hiroaki:20190223161915p:plain

 Lagoon のデータソースには Harvest 以外にも Grafana が選択できます。 default だと Harvest のデータが使用されますので、今回は default のままにします。

f:id:akanuma-hiroaki:20190223162046p:plain

 今回は Harvest に前回の記事で Unified Endpoint を試した時のサンプルデータが入っていますので、そのうちの一つの項目である humidity が対象として表示されています。今回はもう一つの項目である temperature も表示しますので、 クエリを追加 をクリックして対象データを追加します。

f:id:akanuma-hiroaki:20190223204319p:plain

 追加したデータの方では temperature を選択して、下記のように2つのデータを対象とします。

f:id:akanuma-hiroaki:20190223204538p:plain

 対象データを設定すると、デフォルトでは直近6時間分のデータが表示されるのですが、今回のデータを登録したのは一週間前なので、対象期間を変更してみます。 Last 6 hours と表示されているところをクリックすると、対象期間が選択できますので、今回は 先週の今日 をクリックします。

f:id:akanuma-hiroaki:20190223204714p:plain

 すると一週間前の1日分のデータが表示されます。データは1日のうちの一部だけなので、グラフでデータが表示されている部分をドラッグして選択します。

f:id:akanuma-hiroaki:20190223204837p:plain

 すると選択した部分が拡大して表示されます。対象期間を簡単に選択できるので便利ですね。

f:id:akanuma-hiroaki:20190223205021p:plain

 グラフを見やすくする為に少し設定を変更してみます。 全般 タブを選択してタイトルと説明文を設定します。

f:id:akanuma-hiroaki:20190223164422p:plain

 次に タブで各軸のラベルを設定します。

f:id:akanuma-hiroaki:20190223171913p:plain

 ここまででひとまずの設定ができたので、設定画面からダッシュボードの名前を設定して保存しておきます。

f:id:akanuma-hiroaki:20190223172845p:plain

ダッシュボードの共有(Makerプラン以上)

 ちなみに Lagoon では他の Lagoon ユーザにダッシュボードを共有することができます。画面上部の 共有 ボタンをクリックします。

f:id:akanuma-hiroaki:20190223173426p:plain

 共有用のリンクが表示されますので、このリンクを他のユーザに共有することでダッシュボードにアクセスしてもらうことができます。

f:id:akanuma-hiroaki:20190223173827p:plain

 ただし、その為には Lagoon のユーザを追加する必要がありますが、 Freeプランでは登録できるユーザ数は一人(自分)だけなので、ユーザを追加しようとしても下記のようにエラーが表示されて追加できません。複数ユーザで使用する場合には Maker 以上のプランにする必要があります。

f:id:akanuma-hiroaki:20190223174110p:plain

アラートを追加する

 Lagoon ではデータの内容によってアラートを出すことが可能です。まずはアラートの通知方法を設定するために、チャンネルを作成します。左のメニューから 通知チャンネル を選択します。

f:id:akanuma-hiroaki:20190223194956p:plain

 チャンネルリストの画面が表示されますが、最初はまだチャンネルがないので、表示されている チャンネルを追加 ボタンをクリックします。

f:id:akanuma-hiroaki:20190223195138p:plain

 チャンネルの設定フォームが表示されます。通知方法としては slack や webhook など様々なものが選択可能です。今回は Email を選択します。対象のメールアドレスはセミコロンで区切って複数指定することが可能です。 送信テスト をクリックするとテストメールが送信されます。正しく設定できたら 保存 をクリックします。

f:id:akanuma-hiroaki:20190223195323p:plain

 作成されたチャンネルはリスト画面に表示されます。

f:id:akanuma-hiroaki:20190223195456p:plain

 次にダッシュボードの編集画面の アラート タブから アラートを作成 ボタンをクリックします。

f:id:akanuma-hiroaki:20190223195649p:plain

 アラートの設定画面からアラートの条件を設定します。 query では対象のデータ項目と対象の時間を設定できます。今回は温度を対象にするので、 B を対象として、5分間のデータを対象にします。条件としては平均値、最小値など様々な方法が指定できます。今回は最大値( max() )を使用して、5分間の最大値が23を超えた場合にアラートを出します。

f:id:akanuma-hiroaki:20190223200136p:plain

 通知の送り先としては先ほど作成したチャンネルを指定します。メールの文面は自由に設定できます。

f:id:akanuma-hiroaki:20190223200334p:plain

 指定した閾値を超えると下記のようなメールが送られてきます。

f:id:akanuma-hiroaki:20190223200416p:plain

まとめ

 センサー等で集めたデータを可視化するのは自分でやると結構な手間がかかる部分ですが、 Lagoon を使うことで Harvest に入っているデータであれば画面から設定するだけで簡単に可視化することができました。今回 Freeプランができたことで、開発者が自分で使うぐらいであれば無料で使うことができるので、デバイスで取得したデータは json で Unified Endpoint を経由して Harvest に投げ込んでおいて Lagoon で可視化するという、サーバサイドの一連の処理を無料で簡単に用意することができるようになりました。ますますプロトタイピングのハードルが下がりましたね。

Unified Endpoint で SORACOM の複数サービスにデータを送る

 先日 2/14 にソラコムさんの if-up2019 IoT Technology Conference に参加してきました。

if-up2019.soracom.jp

 ソラコムさんはイベントに合わせていつも新発表をされますが、今回の新発表の一つとして Unified Endpoint が発表されました。

blog.soracom.jp

 こちらは独立した新サービスではなく新機能ということで、すでに Public Beta として使えるようになっていたので早速試してみました。

機能概要

 Unified Endpoint を簡単に説明すると、 SORACOM の複数のサービスへのデータ送信を一つのURLでまとめて受け取れるようにしたものです。 SORACOM では今までに Beam, Funnel, Harvest といったサービスが提供されていて、クラウドサービスとのデータ連携や可視化が便利にできるようになっていましたが、送信先URLはそれぞれ違っています。なので後から送信先サービスを変更する場合、例えば Beam で自前のサーバに送っていたけど Funnel で AWS IoT に連携するようにする、といった場合にはデバイス上の実装に含まれているURL指定を変更する必要がありました。

 これが Unified Endpoint を利用することにより、デバイスの実装では常に Unified Endpoint の URL にデータを送っていれば、ユーザーコンソールの設定だけで送信先サービスを変更できるようになります。もちろん送信内容はあらかじめ汎用性を考慮しておく必要はありますが、設置済みデバイスの実装に手を入れずに送信先を変更できるというのはとても便利なのではないかと思います。

設定

 今回は Raspberry Pi を SORACOM Air でセルラーネットワークに接続し、温度と湿度のデータを JSON で SORACOM Harvest と SORACOM Funnel 経由で AWS IoT に送信してみます。 AWS IoT に接続するにはデバイスの認証が必要ですが、今回の認証は以前下記の記事で試した SORACOM Krypton での認証を使用します。

blog.akanumahiroaki.com

 それではそれぞれのサービスの設定を行なっていきます。まず Unified Endpoint は有効にするための設定は特に必要なく、自動的に使用されます。レスポンスの形式を変更する場合はユーザコンソールのSimグループの設定画面から設定変更を行います。今回はデフォルトの Auto 設定のまま使用してみます。

f:id:akanuma-hiroaki:20190216154948p:plain

 Harvest の設定は ON/OFF の切り替えのみですので、ユーザコンソールから ON にします。

f:id:akanuma-hiroaki:20190216155126p:plain

 Funnel から AWS IoT への転送設定は、 Funnel の利用を ON にして、転送先サービスで AWS IoT を選択します。転送先 URL には AWS IoT のエンドポイントに MQTT のトピックス名を組み合わせて指定し、認証情報は Krypton で設定した認証情報を指定します。

f:id:akanuma-hiroaki:20190216160208p:plain

 これで設定は一通り完了です。データ送信については今回は擬似的に温度と湿度データを生成して送信する Ruby スクリプトを実装してデータを送信してみます。送信先の URL には Unified Endpoint の URL を指定し、30秒ごとにデータを送信してみます。

require 'bundler/setup'
require 'httpclient'
require 'logger'

UNIFIED_ENDPOINT = 'http://uni.soracom.io'
LOG_FILE = 'env_monitor.log'

class ENV_MONITOR
  def initialize(interval)
    @interval = interval
    @http_client = HTTPClient.new
    @logger = Logger.new(LOG_FILE)
    @temperature_range = 20.0..25.0
    @humidity_range = 40..60
  end

  def execute
    loop do
      temperature = Random.rand(@temperature_range)
      humidity = Random.rand(@humidity_range)
      payload = '{"temperature": "%.1f", "humidity": "%.1f"}' % [temperature, humidity]
      res = @http_client.post(UNIFIED_ENDPOINT, payload, 'Content-Type' => 'application/json')
      @logger.info("PAYLOAD: #{payload} / Response: #{res.status}")
      @logger.info(res.body)

      sleep(@interval)
    end
  end
end

if __FILE__ == $PROGRAM_NAME
  monitor = ENV_MONITOR.new(30)
  monitor.execute
end

動作確認

 それでは SORACOM Air でネットワーク接続した上でスクリプトを動かして動作を確認してみます。スクリプトを動かすと下記のようにログに出力されます。今回は Unified Endpoint のレスポンスの設定を Auto にしていますので、レスポンスの detail には Funnel と Harvest それぞれからの statusCode が含まれます。 Funnel からは 204、 Harvest からは 201 が返って来ていますので、それぞれへのデータ送信は成功しているようです。

I, [2019-02-16T06:34:37.185769 #23241]  INFO -- : PAYLOAD: {"temperature": "20.8", "humidity": "58.0"} / Response: 200
I, [2019-02-16T06:34:37.186107 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:35:09.495921 #23241]  INFO -- : PAYLOAD: {"temperature": "25.0", "humidity": "41.0"} / Response: 200
I, [2019-02-16T06:35:09.496257 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:35:43.716117 #23241]  INFO -- : PAYLOAD: {"temperature": "20.1", "humidity": "52.0"} / Response: 200
I, [2019-02-16T06:35:43.716453 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:36:15.356277 #23241]  INFO -- : PAYLOAD: {"temperature": "23.3", "humidity": "47.0"} / Response: 200
I, [2019-02-16T06:36:15.356612 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:36:49.526506 #23241]  INFO -- : PAYLOAD: {"temperature": "20.4", "humidity": "52.0"} / Response: 200
I, [2019-02-16T06:36:49.526851 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:37:21.146493 #23241]  INFO -- : PAYLOAD: {"temperature": "23.7", "humidity": "47.0"} / Response: 200
I, [2019-02-16T06:37:21.146824 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:37:58.956850 #23241]  INFO -- : PAYLOAD: {"temperature": "22.3", "humidity": "59.0"} / Response: 200
I, [2019-02-16T06:37:58.957189 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:38:30.626827 #23241]  INFO -- : PAYLOAD: {"temperature": "21.2", "humidity": "50.0"} / Response: 200
I, [2019-02-16T06:38:30.627169 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:39:03.247368 #23241]  INFO -- : PAYLOAD: {"temperature": "21.7", "humidity": "59.0"} / Response: 200
I, [2019-02-16T06:39:03.247702 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}
I, [2019-02-16T06:39:34.887067 #23241]  INFO -- : PAYLOAD: {"temperature": "22.1", "humidity": "55.0"} / Response: 200
I, [2019-02-16T06:39:34.887410 #23241]  INFO -- : {"result":"ok","detail":{"SoracomFunnel":{"statusCode":204},"SoracomHarvest":{"statusCode":201}}}

 レスポンスのフォーマットについては下記公式ドキュメントに詳しく書かれていますのでご参照ください。

Unified Endpoint : 機能の説明 | 開発者ガイド | SORACOM Developers

 Harvest の画面でも送信されたデータが確認できます。

f:id:akanuma-hiroaki:20190216160003p:plain

まとめ

 Unified Endpoint から転送可能なのは Beam, Funnel, Harvest の3つですが、 Beam に転送できるということは実質的には何処へでも転送できるということになりますので、実装時にはとりあえず Unified Endpoint 宛に JSON でデータを送るようにしておけば、後からの変更はかなり柔軟にできるのではないでしょうか。認証でも Krypton 等を組み合わせることができますし、様々な処理をデバイスからクラウド側へオフロードできるようになって、どんどん便利になってきました。プロトタイプ段階からこういったサービスを活用してアーキテクチャを考えていくことが重要になってきますね。

M5Stack 用の距離センサと UIFlow でプロトタイピング

 M5Stack には GPIO にジャンパーコードを接続して色々なセンサーなどを接続することができますが、 GROVE コネクタで簡単に接続できる M5Stack 用のユニットパーツが色々と販売されています。 UIFlow でも色々なユニットパーツに対応していて気になっていたのですが、今まで試していなかったので、今回は距離センサを試してみました。

www.switch-science.com

 ユニットについてのドキュメントも用意されていて、下記で参照できます。 Arduino IDE でのサンプルコード等も掲載されています。

docs.m5stack.com

測定方式

 距離センサにも色々な方式がありますが、このセンサは ToF(Time-of-Flight)方式と言われるもので、赤外線が物体に反射して戻ってくるまでの時間から距離を計算するタイプのものです。搭載されているセンサモジュールは下記のモジュールです。

www.switch-science.com

 ちなみに以前下記の記事で使用したセンサも方式としてはToF方式ですが、赤外線ではなく超音波を使ったものになります。

blog.akanumahiroaki.com

距離センサを接続

 それでは距離センサを M5Stack に接続してみます。センサには本体とGROVEコネクタ用のケーブルがセットになっています。

f:id:akanuma-hiroaki:20190209140134j:plain

 センサ本体の裏面は下記のような感じで、センサモジュールの型番やピンアウトが記載されています。

f:id:akanuma-hiroaki:20190209140203j:plain

 接続はセンサユニットを M5Stack 本体の GROVE コネクタに接続するだけで、それ以外にジャンパーコードを使ったり半田付けをする必要はありません。

f:id:akanuma-hiroaki:20190209140304j:plain

距離を表示

 今回は UIFlow を使って処理を実装してみます。 UIFlow の M5Stack エミュレータの下にユニット追加ボタンがあるのでクリックします。

f:id:akanuma-hiroaki:20190209142955p:plain

 すると UIFlow が対応しているユニットパーツのリストが表示されますので、下の方にスクロールして ToF を選択します。 Port は A を選択します。

f:id:akanuma-hiroaki:20190209143211p:plain

 追加するとエミュレータの下に ToF が表示されるようになります。

f:id:akanuma-hiroaki:20190209143319p:plain

 ToF が追加されるとメニューの UnitsTOF が追加され、センサから距離を読み取るためのブロックが使用できるようになります。

f:id:akanuma-hiroaki:20190209143443p:plain

 これを使用して今回は、無限ループで一秒毎に距離情報を読み出してラベルの位置に表示する簡単な処理を作ってみました。

f:id:akanuma-hiroaki:20190209151125p:plain

 これを実際に M5Stack の実機で動かして、実際の距離と読み出した値の差を簡単に確認してみました。読み取った数字の単位が何なのかは少し調べてみた感じではわからなかったのですが、やってみた感じでは ミリメートル と思って良さそうです。数ミリの誤差はあるものの、検証の仕方も適当ですし、大幅に外れた値が取れることはなかったので、かなり精度は良さそうに感じています。

まとめ

 センサーユニットの物理的な接続も GROVE コネクタで繋ぐだけですし、モニタも M5Stack 本体についていて、さらに UIFlow を使うことで直接コードも書かずに簡単に距離センサーのプロトタイピングができてしまいました。 IoT のプロトタイピングでは如何に電子工作せずにコードを書かずに手軽に実行するかというのが重要だと思いますので、その点 M5Stack とそのユニットパーツはかなり有効に使えそうに思います。今回簡易に試した限りでは距離センサの精度も高そうなので、アイディア次第では十分実用的に使えそうです。

M5Stack UIFlow の Custom Function を試す

 数日前に Twitter で M5Stack の UIFlow で Custom Function というのがリリースされたのを見たので、今回は試しに触ってみました。Custom Function というのは内容を自由に設定してブロックを作ることができる機能です。

UIFlow 用のファームウェアを Mac で Flash する

 UIFllow を使うには M5Stack に UIFlow 用のファームウェアを Flash しておく必要があります。以前は Mac からファームウェアを Flash するにはターミナルからコマンドで操作する必要がありましたが、最近では Windows と同じように Flash 用のツールが提供されているのでそれを使ってみます。ツールは UIFlow の設定画面からダウンロードすることができますので、 UIFlow にアクセスして設定画面を開きます。

f:id:akanuma-hiroaki:20190202214739p:plain

 設定画面に M5Burner-Flow-For-MacOS というリンクがありますので、これをクリックするとファイルのダウンロードダイアログが開きます。

f:id:akanuma-hiroaki:20190202214801p:plain

 デフォルトでは M5Burner_MacOS.zip というファイル名で保存されますので、これを解凍すると M5Burner_MacOS.app というアプリケーションになります。これを実行するとツールが起動するのですが、起動時に下記のようなダイアログが表示されて、ツールが起動できないことがあります。

f:id:akanuma-hiroaki:20190202214828p:plain

 これは MacOS のセキュリティ設定によるもので、製造者が信頼できない場合は実行することができなくなっています。以前の MacOS ではセキュリティ設定で、全てのアプリケーションを実行可能にすることもできたのですが、最近の MacOS ではその選択肢がなくなっています。

f:id:akanuma-hiroaki:20190202214947p:plain

 これを変更するには、ターミナルから下記のようにコマンドを実行し、全てのアプリケーションの実行を許可します。

$ sudo spctl --master-disable

 するとセキュリティ設定画面にもそれが反映されて、項目が追加で表示されます。

f:id:akanuma-hiroaki:20190202215039p:plain

 ちなみにずっとこのままの設定にしておくのは良くないので、ツールの実行が終わったら下記のようにコマンドを実行するか、セキュリティ設定画面から設定を戻しておくのが良いかと思います。

$ sudo spctl --master-enable

 ツールが起動すると下記のようなフォームが表示されます。執筆時の UIFlow の最新のバージョンは 1.1.2 なので、ファームウェアのバージョンも同じバージョンを選択して、 Flash ボタンをクリックします。しばらく待つとファームウェアが M5Stack に Flash されます。

f:id:akanuma-hiroaki:20190202215147p:plain

 これ以降の操作は以前と同様です。下記記事でも手順を書いていますので、よろしければご参照ください。

blog.akanumahiroaki.com

Custom Function を試す

 それでは Custom Function を試してみます。 UIFlow の 1.1.2 では下記のようにメニューに Custom (Beta) という項目が追加になっています。内容としては Create *.m5b fileOpen *.m5b file という2つがあります。まずは Create *.m5b file をクリックします。

f:id:akanuma-hiroaki:20190202215529p:plain

 すると UIFlow Block Maker というサイトに遷移します。

f:id:akanuma-hiroaki:20190202215627p:plain

 この UIFlow Block Maker で Custom Function の内容を指定し、ブロックを作成するわけです。今回はとりあえず画面に「Hello World!」のような文字列を表示する Function を実装してみます。「World」の部分は引数として指定できるようにします。設定項目は下記のようにしました。

  • Namespace: greeting

  • Block Color: 青

  • Name: hello

  • Type: Executable

  • Parameter: name (String)

 Block Code は実際に実行されるソースコードを設定しておくところで、今回は下記のようにしています。 UIFlow でラベルを指定した時に実行されるコードと同じものです。

label0 = M5TextBox(51, 39, "Hello ${name}!!", lcd.FONT_Default, 0xFFFFFF)

 画面左側の内容を変更していくと、右側のソースコードにリアルタイムに反映されていきます。今回のケースでは最終的には下記のような設定になります。設定が終わったら Download ボタンをクリックします。

f:id:akanuma-hiroaki:20190202220013p:plain

 するとファイルのダウンロード用のダイアログが開きます。今回は greeting.m5b というファイル名で保存しておきます。

 ファイルを保存したら、今度は Custom (Beta) の項目から Open *.m5b lib を選択します。するとファイルのアップロード用のダイアログが開きますので、先ほど保存した greeting.m5b を選択します。

f:id:akanuma-hiroaki:20190202220140p:plain

 m5b ファイルがアップロードされると、下記のように Custom (Beta) の項目に先ほど UIFlow Block Maker で作成した Custom Function のブロックが追加されます。

f:id:akanuma-hiroaki:20190202220307p:plain

 今回作成した Custom Function では表示する名前を引数として取ることにしていますので、その内容と共にブロックを設定して、実行ボタンをクリックします。

f:id:akanuma-hiroaki:20190202220508p:plain

 すると M5Stack で下記のように文字列が表示されます。

f:id:akanuma-hiroaki:20190202220609j:plain

まとめ

 手順として一度 .m5b ファイルをローカルに保存してそれをアップロードするという手順が直感的ではない気はしましたが、 UIFlow 自体にはまだファイルを保存しておくことができない中で、複数のアプリで汎用的に使用するコードをまとめておけるようになったのは便利かと思います。まだ Beta ということで、ブロックに表示されるのがパラメータの変数名だったりとまだわかりづらいところはありますが、今後使いやすくなっていくのではないかと思います。

AWS Cloud9 で API Gateway からの Lambda 実行

 前回の記事で Cloud9 での Lambda Function 開発について書きましたが、 Cloud9 から API Gateway 経由での Lambda Function 実行について書いていなかったので今回はそのやり方について書いておきます。

ローカル実行

 まずはローカルで API Gateway 経由で Lambda Function を動かしてみます。実行用のペインのプルダウンから API Gateway (local) を選択します。

f:id:akanuma-hiroaki:20190126192904p:plain

 そして Run ボタンをクリックすればローカルで実行できます。

f:id:akanuma-hiroaki:20190126181123p:plain

 ただ、前回実装した内容だと、下記のように Internal Server Error になってしまいます。

f:id:akanuma-hiroaki:20190126184900p:plain

 ローカル実行だと詳細なログが出ないようなのですが、同じ内容をリモートの環境で動かすと下記のエラーが出ています。

Execution failed due to configuration error: Malformed Lambda proxy response

 これは Lambda Function が返すレスポンスが API Gateway が期待するフォーマットになっていなかったことによるものです。下記ページにもその内容が書かれています。

aws.amazon.com

 API Gateway 経由で Lambda Function を使うには、 Lambda Function のレスポンスは下記のフォーマットの json で返す必要があります。

{
    "isBase64Encoded": true|false,
    "statusCode": httpStatusCode,
    "headers": { "headerName": "headerValue", ... },
    "body": "..."
}

 なので実装を下記のように変更します。変更したのは return の内容のみです。

import boto3

PHONE_NUMBER = "8180XXXXXXXX"
MESSAGE = "Test Message from Lambda"

sns = boto3.client('sns')

def lambda_handler(event, context):
    sns.publish(PhoneNumber = PHONE_NUMBER, Message = MESSAGE)
    return {
        'body': 'Sent SMS',
        'statusCode': 200,
        'isBase64Encoded': False
    }

 変更後に再度実行すれば良いのですが、私が試した限りでは、このまま API Gateway (local) で再実行しても、 Lambda Function の変更内容が反映されませんでした。一度 Lambda (local) を実行してから実行することで Lambda Function の変更内容が反映されて API Gateway 経由で実行されるようになり、下記のように成功レスポンスが返るようになります。

f:id:akanuma-hiroaki:20190126192606p:plain

リモート実行

 Lambda Function をリモートにデプロイした後は、リモートの API Gateway 経由の実行も可能です。実行用のペインのプルダウンで API Gateway (remote) を選択します。

f:id:akanuma-hiroaki:20190126192127p:plain

 あとはこれまで同様に Run ボタンをクリックするだけです。

f:id:akanuma-hiroaki:20190126192252p:plain

 リモートの API Gateway 経由で Lambda Function が実行され、結果が表示されます。

f:id:akanuma-hiroaki:20190126192350p:plain

まとめ

 ローカルで Lambda Function の実装を変更した場合に、 API Gateway での再実行だけではなく一度 Lambda Function 単体で実行しないと反映されないというのはちょっとわかりづらいところでしたが、それ以外は特に難しいことはなくローカルでもリモートでも Cloud9 から実行することができました。 Lambda Function を使用する場合には API Gateway を組み合わせるケースが多いと思いますので、 Cloud9 でそこも含めて開発できるのは便利ですね。

AWS Cloud9 で Lambda Function 開発

 前回の記事で Cloud9 で Rails の開発環境を作ってみましたが、 Cloud9 では Lambda Function の開発も行えるということで、やってみました。

Lambda Function の作成

 それでは Lambda 関数を作成してみます。 Cloud9 の新しい環境を作成すると Welcome ページが表示されますが、そこに Create Lambda Function というリンクがありますのでクリックします。

f:id:akanuma-hiroaki:20190119181629p:plain

 すると Lambda Function の作成フォームが表示されますので、まずは関数名とアプリケーション名を入力し、 Next をクリックします。

f:id:akanuma-hiroaki:20190119181750p:plain

 次に Function のテンプレートを選択します。今回はブランクから作成するので empty-python を選択して Next をクリックします。

f:id:akanuma-hiroaki:20190119181932p:plain

 続いて Function のトリガーを選択します。 API Gateway が選択できるようになっているので選択してみました。 Resours Path と Security を設定して Next をクリックします。

f:id:akanuma-hiroaki:20190119182215p:plain

 最後にメモリサイズと Role を選択して、 Next をクリックします。

f:id:akanuma-hiroaki:20190119182308p:plain

 これまでの設定内容の確認画面が表示されますので、間違いがなければ Finish をクリックします。

f:id:akanuma-hiroaki:20190119182424p:plain

 すると Lambda Function が作成され、 Cloud9 のエディタに表示されます。左側のディレクトリツリーにもファイルとして表示されます。

f:id:akanuma-hiroaki:20190119182935p:plain

 ということは EC2 の Linux 上にも存在するということなので、前回の記事でやったように、 vim でファイルを編集することも可能です。 vim で編集した内容はすぐに Cloud9 のエディタにも反映されます。 Linux 上で触れれば git でバージョン管理をすることも可能になりますので、継続的に開発していくにはかなり便利になりそうです。

$ pwd
/home/ec2-user/environment
$ ls -l SesSample/SesSample/
total 8
-rw-r--r-- 1 ec2-user ec2-user 75 Jan 19 07:53 lambda_function.py
-rw-r--r-- 1 ec2-user ec2-user 49 Jan 19 07:54 lambda-payloads.json

 Lambda のコンソールの方で確認してみると、そちらにも Function が作成されています。

f:id:akanuma-hiroaki:20190119183055p:plain

 内容も同様でブランクのテンプレートになっています。

f:id:akanuma-hiroaki:20190119183206p:plain

 Function のトリガーに API Gateway を選択したので、コンソールから確認すると API Gateway が作成されています。

f:id:akanuma-hiroaki:20190119183311p:plain

Lambda Function の実行

 それではひとまず Cloud9 から Lambda Function を動かしてみます。 Run ボタンをクリックします。

f:id:akanuma-hiroaki:20190119183515p:plain

 Function 実行のためのペインが開きますので、さらに Run をクリックします。

f:id:akanuma-hiroaki:20190119183609p:plain

 すると Function が実行され、下部に実行結果が表示されます。

f:id:akanuma-hiroaki:20190119183708p:plain

SMS 送信のサンプル

 今回はサンプルとして SMS 送信処理を実装してみます。内容は下記のようにシンプルなもので、 Amazon SNS から固定の宛先に SMS を送信します。

import boto3

PHONE_NUMBER = "080XXXXXXXX"
MESSAGE = "Test Message from Lambda"
REGION = "ap-southeast-1"

sns = boto3.client('sns')

def lambda_handler(event, context):
    sns.publish(PhoneNumber = PHONE_NUMBER, Message = MESSAGE)
    return 'Sent SMS'

 これを実行すると下記のように SMS が送信されます。

f:id:akanuma-hiroaki:20190119184045p:plain:w300

Lambda Function のデバッグ

 Cloud9 のエディタからは Lambda Function をデバッグすることもできます。行番号の前をクリックするとブレイクポイントとして赤いマークがつきますので、この状態で Run をクリックします。

f:id:akanuma-hiroaki:20190119184307p:plain

 実行用のペインでは今度はデバッグ用のボタンをクリックします。

f:id:akanuma-hiroaki:20190119184529p:plain

 すると関数の実行がブレイクポイントで停止し、右側ペインにコールスタックや変数の内容が表示されます。実行ボタンをクリックすると関数の実行が続行されます。

f:id:akanuma-hiroaki:20190119185417p:plain

Lambda Function のデプロイ

 Cloud9 から Lambda Function を作成した場合、作成時だけは同じ内容がリモートにも反映されますが、それ以降の変更内容は Cloud9 上にあるだけで、リモートには反映されていません。リモートにデプロイするには右側ペインで AWS Resources を選択し、ローカルで変更した Function を選択してデプロイボタンをクリックします。

f:id:akanuma-hiroaki:20190119190407p:plain

 すると Function がリモートにデプロイされ、 Lambda のコンソールからも内容が反映されていることが確認できます。

f:id:akanuma-hiroaki:20190119205513p:plain

 また、 Cloud9 からはリモートの Lambda Function を実行することも可能です。実行用のペインで Lambda (remote) を選択して Run をクリックします。

f:id:akanuma-hiroaki:20190119205828p:plain

 するとローカルの Function 実行時と同様に結果が下部に表示されます。今回はリモートの Function に SMS 送信の権限がないということでエラーになってしまいました。

f:id:akanuma-hiroaki:20190119210204p:plain

 Lambda のコンソール上で権限のある Role を割り当ててから再度実行すると、下記のように成功し、 SMS が送信されました。

f:id:akanuma-hiroaki:20190119210720p:plain

まとめ

 Lambda Function は Lambda のコンソールで実装すると普通のコードのようなバージョン管理ができないのが難点と感じていました。かといってバージョン管理できるように手元の端末上でコードを書いた場合はそのまま Function を実行することはできないので、毎回リモートに反映する必要があります。その点 Cloud9 は環境が EC2 の Linux 上にあり、書いたコードをそのまま実行することもバージョン管理することも可能なので、 Lambda の開発をかなり便利にしてくれそうに感じています。

 Cloud9 では Ruby ランタイムにはまだ対応していないようで、関数作成時のテンプレートにも Ruby のテンプレートは存在せず、リモートからのインポートの機能を使っても、 Python の Function はインポートできても Ruby の Function はインポートできませんでした。ここは今後の対応を待ちたいところです。