LW01-NWPのセンシングデータをAWS IoTに送信し、AWS IoTからLW01-NWPの出力を制御することができます。LW01-NWPでなくてもLoRaWANデバイスなら、AWS IoTと連動することができます。
AWS IoTと連動するためにThe Things Network(無料)を利用します。有料なのはAWSだけになります。
AWS IoTにまで届けば、後はAWSサービスを使って、センシングデータを保存・分析・制御することができます。
用意するもの
LoRaWANゲートウェイは、The Things Networkと通信できるタイプであれば何でも構いません。
The Things Networkから動画で詳しい手順が公開されています。https://www.thethingsnetwork.org/docs/applications/aws/
動画で説明されていますが、連携するためのテンプレート(AWS CloudFormation)を実行します。このテンプレートを実行すると下記のことが行われます。
テンプレートの実行が成功すれば、いきなりAWS IoTでセンシングデータを確認できます。
テンプレートの入力の時にEC2のキーペアを選択する必要があるので、事前にAWS EC2キーペアの作成を行っておきます。既存のEC2のキーペアを使用する場合は作成する必要はありません。
AWS コンソール(https://aws.amazon.com/jp/console/)にログインし、EC2のサービスを選択します。

左のメニューからキーペアを選択します。右上のキーペアの作成を押します。
AWSは、キーペアを使って暗号鍵を2つ作成します。作成した2つの内AWSが1つを保管し、もう一つを自分で厳重に保管します(流出禁止、ダウンロードも1回しかできない仕様です)。
キーペアが流出してもAWS以外で暗号鍵を作り出せません。インターネット網からEC2にアクセスするときには、ダウンロードした暗号鍵を使用してアクセスします。
実際には暗号鍵自体を送信はせずに、AWSが用意したデータを暗号化したものを送り、AWSは保管している暗号鍵でそれを復号して検証しています。
AWSサービス間であれば、キーペアだけを使って関連性を設定できます。

名前を入力して、右下のキーペアを作成を押すとキーペアが作成され、自分の暗号鍵がダウンロードされます。

Quick Startページ(https://www.thethingsnetwork.org/docs/applications/aws/quick-start.html)を開きます。

赤枠のus us-west-2を選択します。その後のeu-west-1でもap-southeast-1でもかまいません。どちらにせよリージョンが違うので、次のページで東京リージョンに変更します。

右上のオレゴンを選択して、リージョンをアジアパシフィック(東京) ap-northeast-1に変更します。正確には使いたいAWS IoTがあるリージョンを選択します。
まずは、App IDを設定します。The Things Networkのコンソール(https://console.thethingsnetwork.org/)にログインして、アプリケーションの項目を開きます。

アプリケーションIDに表示されているものをApp IDの項目に入力します。
次にApp Access Keyを設定します。TTNの同じページの一番下にアクセスキーがあります。

クリップボードにコピーボタンを押してから、App Access Keyの項目で貼り付けを実行してください。文字数が多いのでCopy&Pasteの方が安全です。
次に追加されるEC2インスタンスの設定をします。

Enviroment Nameの項目は自由です。Instance Typeの項目にはt3.microを選択します。
そして、SSH Keyの項目で先ほど作成したEC2のキーペアを選択します。
次にLW01-NWPのセンサ情報の送受信先ポート番号を設定します。要は、LoRaWANデバイスのダウンリンクのポート番号を設定します。

LW01-NWPとパソコンをUSBケーブルで接続し、下記のコマンドを実行すると
xxxxxxxxxx#?MLW01-NWPの現在の設定情報が出力されます。
xxxxxxxxxx...Lora_DataPortNo:099...Lora-DataPortNoの項目がここで設定するLoRaWAN FPortの番号になります。
最後にAWS CloudFormationによってIAMリソースが作成される場合があることを承認しますに✓を付けてスタックの作成を実行します。成功すると、

スタックに追加された2つのステータスがCREATE_COMPLATEになります。
変更された内容は、
になります。
この段階で、LoRaWANデバイスから送信されたデータがAWS IoTに届くようになっています。
LW01-NWPは、送信データ量を少なくするためにフォーマットがバイナリデータになっています。バイナリデータのままAWS IoTに送ると後の処理が難しくなるので、The Things Networkのスクリプト機能でJSONフォーマットに変換します。
The Things NetworkのPayload Formatsを開きます。

decoderの項目に下記のコードを貼り付けます。
xfunction Decoder(bytes, port) { // Decode an uplink message from a buffer // (array) of bytes to an object of fields. var decoded = {}; var payload = ""; for( i = 0; i < bytes.length; i++ ){ payload += ('00' + bytes[i].toString(16)).slice(-2); } var common = payload.substr(4,24); var sw_data = parseInt(common.substr(0,4), 16); var output; if( (sw_data & 0x8000) == 0x8000 ) output = "on"; else output = "off"; var lat = parseInt(common.substr(4,8), 16); lat /= 1000000.0; var lon = parseInt(common.substr(12,8), 16); lon /= 1000000.0; var battery = (parseInt(common.substr(20,2), 16) == 1 ? "backup" : "normal"); var input = []; for( i = 0, idx = 0; i < 10; i++, idx++ ){ if( (sw_data & 0x01) == 0x01 ) input[idx] = "on"; else input[idx] = "off"; sw_data >>= 1; } var code; var ave_wind, max_wind; var rain_power, rain; var curr, volt, rs232, rs485; var data = payload.substr(28); for(i = 0; i < data.length - 4;){ var b = parseInt(data.substr(i,2), 16); i += 2; var len = parseInt(data.substr(i,2), 16); i += 2; switch(b){ case 1: // Wind ave_wind = parseInt(data.substr(i,4), 16); ave_wind /= 600.0; // m/s ave_wind = Math.round(ave_wind * 100) / 100.0; max_wind = parseInt(data.substr(i+4,4), 16); max_wind /= 600.0; // m/s max_wind = Math.round(max_wind * 100) / 100.0; break; case 2: // Rain rain_power = parseInt(data.substr(i,4), 16); rain_power *= 3; // mm/h rain = parseInt(data.substr(i+4,4), 16); rain *= 0.5; // mm break; case 3: // 4-20mA curr = []; for( k = 0, idx = 0; k < len*2; k += 4, idx++){ curr[idx] = parseInt(data.substr(i+k, 4), 16); } break; case 4: // 0-10V volt = []; for( k = 0, idx = 0; k < len*2; k += 4, idx++){ volt[idx] = parseInt(data.substr(i+k, 4), 16); } break; case 5: // RS232 rs232 = ""; // hex to ascii for( k = 0; k < len*2; k+=2){ code = parseInt(data.substr(i+k, 2), 16); rs232 += String.fromCharCode(code); } // hex //rs232 = data.substr(i, len*2); break; case 6: // RS485 rs485 = ""; // hex to ascii for( k = 0; k < len*2; k+=2){ code = parseInt(data.substr(i+k, 2), 16); rs485 += String.fromCharCode(code); } // hex //rs485 = data.substr(i, len*2); break; } i += len*2; } decoded.input = input; decoded.output = output; decoded.lat = lat; decoded.lon = lon; decoded.battery = battery; decoded.ave_wind = ave_wind; decoded.max_wind = max_wind; decoded.rain_power = rain_power; decoded.rain = rain; decoded.curr = curr; decoded.volt = volt; decoded.rs232 = rs232; decoded.rs485 = rs485; decoded.raw = payload; return decoded;}encoderの項目に下記のコードを貼り付けます。
x
function Encoder(object, port) { // Encode downlink messages sent as // object to an array or buffer of bytes. var bytes = []; var payload = "00"; if( object.state.output == "on") payload += "00800000000000000000000000"; else if( object.state.output == "off") payload += "00000000000000000000000000"; var len; if( object.state.rs232 ){ payload += "05"; // ascii to hex len = object.state.rs232.length; payload += ('00' + len.toString(16)).slice(-2); for( i = 0; i < object.state.rs232.length; i++) payload += ('00' + object.state.rs232.charCodeAt(i).toString(16)).slice(-2); // hex //len = object.state.rs232.length / 2; //payload += ('00' + len.toString(16)).slice(-2); //payload += object.state.rs232; } if( object.state.rs485 ){ payload += "06"; // ascii to hex len = object.state.rs485.length; payload += ('00' + len.toString(16)).slice(-2); for( i = 0; i < object.state.rs485.length; i++) payload += ('00' + object.state.rs485.charCodeAt(i).toString(16)).slice(-2); // hex //len = object.state.rs485.length / 2; //payload += ('00' + len.toString(16)).slice(-2); //payload += object.state.rs485; } if( payload.length > 2){ for( i = 0; i < payload.length; i += 2){ var k = payload.substr(i,2); bytes[i/2] = parseInt(k, 16); } } // if (port === 1) bytes[0] = object.led ? 1 : 0; return bytes;}右下のペイロード機能を保存を押すと登録されます。
AWS IoTサービスを開いて、左メニューのテストを選択します。

赤枠の中はApp IDになります(The Things NetworkのアプリケーションID)。
トピックへのサブスクライブを実行し、LW01-NWP(LoRaWANデバイス)からセンシングデータが送信されるのを待ちます。

受信に成功すると、データがJSONフォーマットで表示されます。この中のpayload_fieldsの項目がLW01-NWPから上がってきたセンシングデータになります。
また、LW01-NWPの出力を制御するには、
左メニューでモノを選択して、シャドウを選択します。

編集を選択して、シャドウステータスを編集することでLW01-NWPの出力を制御できます。
"desired" : {}の項目内に記述します。
"output" : "on"を記述すると、LW01-NWPの出力端子がONします。また、"output" : "off"を記述すると逆に出力端子がOFFします。
"rs232" : "012345"と記述すると、LW01-NWPのRS-232C出力端子から012345と出力されます。また、"rs485" : "ABCD"と記述すると、LW01-NWPのRS-485出力端子からABCDと出力されます。
記述した後に保存を押して確定してください。
The Things Networkの動画には、この後AWS DynamoDBに保存するまで紹介されています。データベースに保存するのであれば、グラフを表示するまで紹介したいので、これ以降については別のノートに記載したいと思います。
AWS IoTから出力の制御までできるのでアプリケーションの幅が広がります。
センシングデータをビジネスに展開できるかどうか検証するために、まずはデータを蓄積して分析したいというお話はよく聞きます。商用のIoT Platformも便利ですが、最初はシンプルな機能をAWSサービスで開発して、その結果を元に機能を充実させていくのも良いかもしれません。
AWSは固定額ではなく従量課金なので、数台の検証から始める場合にはありがたいです。ちなみに今回のノートを作成している間の費用は1日1ドル以下でした。