下記の投稿の後編です。
Wio LTE M1/NB1(BG96) でパケ死せずにデータを受け取る - Qiita
以前、SORACOM LTE-M ButtonからLINEにメッセージを送る - Qiita という記事を書きまして、その後も日々このボタンをポケットに入れ、機会あるたびに人にデモしていたのですが、ボタンを押したらLINEメッセージが届くだけでは、「すごい」と言われても、「それで?」「だから?」という気持ちが透けて見えてきました。
そのため、LTE-Mボタンを押したら、パトランプが光るようなデモができたらと考えました。そして、どうせなら、受信側も、LTE-M回線を経由して、Wio LTEを使えたら良いのではと考えました。
前回の投稿で、Wio LTE M1/NB1(BG96) を利用して、AWSからデータを受信するところまで実現したので、今回の投稿では、ボタンを押してパトランプを光らせるまでの全体を完成させようと思います。
できたものがこちらです。
#あのボタン を押したことを Wio LTE M1/NB1で受信して、パトランプとブザーを鳴らしてみた。音量注意。#SORACOM #AWS pic.twitter.com/4UMEiCktyH
— Kosuke Koiwai (@kocko) April 28, 2019
目次
- IoTボタンをおしたらLINEメッセージを飛ばすLambda
- IoTボタンを押したらDynamoDBに値を格納するLambda
- 最新のDynamoDBの値を取得して返すLambda
- 手軽なパトランプを探して購入
- WioLTEとパトランプの接続
- WioLTEのコード
- ケースは100均で
##IoTボタンをおしたらLINEメッセージを飛ばすLambda
一昔前に作った、 SORACOM LTE-M ButtonからLINEにメッセージを送る - Qiita のコードを適宜いじっています。ちょっとシンプルになったかも。
シングルクリックで、注意アラート、ダブルクリックで、警告アラート、長押しで、アラート解除という設定にしました。
const https = require('https');
exports.handler = async(event) => {
var messagetext;
switch (event.deviceEvent.buttonClicked.clickType) {
case 'SINGLE':
messagetext = "\u{26A0} 注意アラートが発砲されました。\u{26A0} ";
break;
case 'DOUBLE':
messagetext = "\u{1F6A8} 警告アラートが発砲されました。\u{1F6A8} ";
break;
default:
messagetext = "\u{2139} アラートが解除されました。\u{2139} ";
break;
}
await linePush(messagetext);
};
function linePush(messagetext) {
return new Promise((resolve, reject) => {
var payload = {
"to": [process.env.RECIPIENT_LINE_ID],
"messages": [{
"type": "text",
"text": messagetext
}]
}
var body = JSON.stringify(payload);
var req = https.request({
hostname: "api.line.me",
port: 443,
path: "/v2/bot/message/multicast",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body),
"Authorization": "Bearer " + process.env.CHANNEL_ACCESS_TOKEN
}
}, function(res) {
if (res.statusCode === 200) {
console.log('Posted to LINE');
resolve();
}
else {
console.error('Failed to post to LINE ' + res.statusCode);
reject();
}
});
req.write(body);
req.end();
});
}
##値を保存するためのDynamoDBの設定
前回の投稿で作ったWio-LTE側のコードは、数秒おきにAWSにステータスを問い合わせる作りになっています。つまり、AWSに直近のステータス(シングルクリックか、ダブルクリックか、長押しか)を保存しておく必要があります。
ちょっとオーバースペックではありますが、DynamoDBに保存することとします。
SORACOM Beam を使用して AWS と接続する | SORACOM Developers を参考にしながら進めようと思ったのですが、コードのバージョンが古いのかうまくいかなかったので、結局手で作りました。
###DynamoDBを作る
実際、最新のステータスだけ取れればいいので、1カラム、1レコードのDBで問題ないのですが、どうせなので履歴を残しておくことにしましょう。
最新のステータスを取得できるよう、タイムスタンプをソートキーにしておきます。プライマリキー、ソートキーはDBの作成時にしか作成できないので、注意です。(私は4回ほど作り直しました。。。)
キャパシティーモードは、デフォルト設定でも大丈夫だと思いますが、一応最小設定にしておきます。
IoTボタンを押したらDynamoDBに値を格納するLambda
IoTボタンを押されたら、上で作成したDynamoDBに情報を格納するようにします。
IoTボタンとLambdaを接続する方法は、こちらを参照してください。
SORACOM LTE-M Button powered by AWS をクリックしてSlackに通知する | Getting Started with SORACOM LTE-M Button | SORACOM Developers
var AWS = require("aws-sdk");
const dynamo = new AWS.DynamoDB.DocumentClient({ region: "ap-northeast-1" });
exports.handler = async(event) => {
var putItem = {};
var d = new Date(event.deviceEvent.buttonClicked.reportedTime);
putItem.timestamp = Math.floor(d.getTime() / 1000);
putItem.deviceId = event.deviceInfo.deviceId;
switch (event.deviceEvent.buttonClicked.clickType) {
case 'SINGLE':
putItem.buttonState = 1;
break;
case 'DOUBLE':
putItem.buttonState = 2;
break;
default:
putItem.buttonState = 0;
break;
}
var params = {
Item: putItem,
TableName: 'BeamDemo'
};
try {
await dynamo.put(params).promise();
}
catch (err) {
console.error('DynamoDB error:', err);
}
};
##最新のDynamoDBの値を取得して返すLambda
前回の投稿 参照。最後に \n
をつけないと、Beamがタイムアウトしてしまいます。
var AWS = require("aws-sdk");
var docClient = new AWS.DynamoDB.DocumentClient();
const dynamo = new AWS.DynamoDB.DocumentClient({ region: "ap-northeast-1" });
exports.handler = async(event) => {
var retval;
var params = {
TableName: 'BeamDemo',
KeyConditionExpression : "#k = :val",
ExpressionAttributeNames : {"#k" : "deviceId"},
ExpressionAttributeValues : {":val" : "デバイスID" },
ScanIndexForward: false,
Limit: 1
};
await dynamo.query(params).promise().then((data) => {
retval = data.Items[0].buttonState;
}).catch((err) => {
console.error(err);
});
return (retval + "\n");
};
##手軽なパトランプを探して購入
最初は、本家純正のパトライトを使おうと思ったのですが、さすがに値段が高いのと、バッテリで駆動できないので。。。
ラジコンカー用のミニチュアパトランプをAmazonで購入。
商品説明を見ると、電圧は4.8v-6vとあります。Wio-LTEは3.3v駆動なのでちょっと足りないですが、試してみてもし動かなかったらUSBの5vでなんとかすることにしましょう。安いし。
##WioLTEとパトランプの接続
LEDなので、そこまで消費電流多くないはずだし、もともとラジコンのバッテリとつなぐ前提のものなので、抵抗挟まずに直結してもきっと大丈夫でしょうということで、Groveコネクタをちょん切って、パトランプのコネクタもちょん切って、結線します。
パトランプは、3本線が生えていますが、これは、こちらのページによると、ラジコン用のコネクタでした。
茶色線が「-」赤色線が「+」オレンジ色の線が信号線
とのことなので、茶色とGND、赤色とDIGITALピンをつなぎます。 digitalWrite(PIN, HIGH)
すれば、光るはず。オレンジ色の線は、信号線で、光るパターンを変更できるそうなのですが、今回は利用しません。
パトランプのコネクタはとても細くて、爪で被覆を剥けてしまうレベルでした。
せっかくなので、Grove IoT スターターキット for SORACOM(Wio LTE JP Version) についてきた、ブザーも接続します。
##WioLTEのコード
前回の投稿 で書いたコードに手を加えて、接続したパトランプとブザーを鳴らせるようにしました。
#include <WioCellLibforArduino.h>
#define INTERVAL (10000)
#define RECEIVE_TIMEOUT (10000)
#define RED_PIN (WIO_D19)
#define YELLOW_PIN (WIO_D20)
#define BUZZER_PIN (WIO_D38)
WioCellular Wio;
bool socketStatus;
void setup() {
delay(200);
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
SerialUSB.begin(115200);
SerialUSB.println("");
SerialUSB.println("--- START ---------------------------------------------------");
SerialUSB.println("### I/O Initialize.");
Wio.Init();
SerialUSB.println("### Power supply ON.");
Wio.PowerSupplyCellular(true);
delay(500);
SerialUSB.println("### Turn on or reset.");
#ifdef ARDUINO_WIO_LTE_M1NB1_BG96
Wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_M1);
Wio.SetSelectNetwork(WioCellular::SELECT_NETWORK_MODE_MANUAL_IMSI);
#endif
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### ERROR! ###");
return;
}
SerialUSB.println("### Connecting to \"soracom.io\".");
if (!Wio.Activate("soracom.io", "sora", "sora")) {
SerialUSB.println("### ERROR! ###");
return;
}
socketStatus = false;
SerialUSB.println("### Setup completed.");
}
void loop() {
char data[100];
int v;
int connectId;
sprintf(data, "0");
if(!socketStatus){
SerialUSB.println("### Open.");
//beam.soracom.io:23080
connectId = Wio.SocketOpen("beam.soracom.io", 23080, WIO_UDP);
if (connectId < 0) {
SerialUSB.println("### ERROR! ###");
goto err;
}
socketStatus = true;
}
SerialUSB.println("### Send.");
SerialUSB.print("Send:");
SerialUSB.print(data);
SerialUSB.println("");
if (!Wio.SocketSend(connectId, data)) {
SerialUSB.println("### ERROR! ###");
goto err_close;
}
SerialUSB.println("### Receive.");
int length;
length = Wio.SocketReceive(connectId, data, sizeof (data), RECEIVE_TIMEOUT);
if (length < 0) {
SerialUSB.println("### ERROR! ###");
goto err_close;
}
if (length == 0) {
SerialUSB.println("### RECEIVE TIMEOUT! ###");
goto err_close;
}
SerialUSB.print("Receive:");
SerialUSB.print(data);
SerialUSB.println("");
v = int(data[0]);
if (v == '0'){
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(RED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}else if(v == '1'){
digitalWrite(YELLOW_PIN, HIGH);
digitalWrite(RED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}else if(v == '2'){
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(RED_PIN, HIGH);
digitalWrite(BUZZER_PIN, HIGH);
}
goto err;
err_close:
SerialUSB.println("### Close.");
if (!Wio.SocketClose(connectId)) {
SerialUSB.println("### ERROR! ###");
goto err;
}
socketStatus = false;
err:
delay(INTERVAL);
}
ケースは100均で
近くの100円ショップに行って、ゼムクリップのケースを買ってきて、穴を開けてミニチュアパトランプをつけました。
ブザーの基板部分にはテープを貼り、Wio-LTEの基板と当たってショートしないようにします。
これで、モバイルバッテリさえあれば準備不要でデモができるキットが完成しました。