[日本語Alexa] 私の部屋の蛍光灯のリモコンが壊れたので・・・Echoで操作できるようにした
1 私の部屋の蛍光灯のリモコンが壊れました・・・
私の部屋の蛍光灯は、壁とかにスイッチが無く、リモコンでしか操作できなかったのですが、その大事なリモコンが壊れてしまいました。
幸い、2階の部屋の蛍光灯が同じリモコンだったので、一応、凌げるのですが、いちいち取りに行くのは面倒なので・・・Alexaでオン・オフできるようにしてみました。
本記事は、そのレポートです。
2 赤外線リモコンのパターン取得
最初にリモコンの赤外線パターンをコピーしました。写真では、読み取るために赤外線受信機をArduinoに接続しています。
オシロスコープで見ると最大5VのON/OFFが確認できるます。
トリガーを設定してみると、なんとなくパターンらしいものも見れます。
Arduinoでは、赤外線リモコンを扱う便利なライブラリが公開されていますので、今回はそれをそのまま利用させて頂きました。
動画は、赤外線パターンを読み取っているようすです。
シリアル接続で取得したデータが、以下のような感じです。+の数字が赤外線ONの時間(ms)で、-の数字がOFFの時間です。ベンダー名とヘキサのコードも認識されています。なお、unsigned int rawData[67] のように、同じパターンを送信する場合に使用できる配列も用意されていて超便利です。
Encoding : NEC Code : 41B658A7 (32 bits) Timing[67]: +9000, -4300 + 700, - 400 + 700, -1550 + 650, - 450 + 700, - 450 + 650, - 450 + 650, - 450 + 700, - 450 + 650, -1550 + 650, -1600 + 650, - 450 + 650, -1550 + 700, -1550 + 650, - 450 + 700, -1500 + 650, -1600 + 700, - 400 + 700, - 450 + 650, -1550 + 700, - 400 + 700, -1550 + 650, -1550 + 650, - 450 + 650, - 500 + 650, - 450 + 700, -1550 + 650, - 450 + 700, -1500 + 650, - 500 + 650, - 450 + 700, -1550 + 700, -1500 + 650, -1550 + 650 unsigned int rawData[67] = {9000,4300, 700,400, 700,1550, 650,450, 700,450, 650,450, 650,450, 700,450, 650,1550, 650,1600, 650,450, 650,1550, 700,1550, 650,450, 700,1500, 650,1600, 700,400, 700,450, 650,1550, 700,400, 700,1550, 650,1550, 650,450, 650,500, 650,450, 700,1550, 650,450, 700,1500, 650,500, 650,450, 700,1550, 700,1500, 650,1550, 650}; // NEC 41B658A7 unsigned int data = 0x41B658A7;
3 赤外線のパターン送信
IRremote Arduino Libraryを利用すると、パターンの送信も簡単にできるのですが、Alexaから操作するためにはインターネット上にエンドポイントが必要になるので、最初からEndPointを持ったArduino Spark Coreを使用することにしました。
[参考]
Wi-Fi付きのArduino互換機 Spark Core (その1) 初期設定とTINKERによるアクセス
Wi-Fi付きのArduino互換機 Spark Core (その2) Spark Build と WebAPIによるアクセス
Wi-Fi付きのArduino互換機 Spark Core (その3) Spark Photon 19$
写真は、Spark Coreに赤外線LEDをつないでいるようすです。Spark Coreの端子からのON/OFFしただけでは、ちょっと電流不足で蛍光灯まで届かなかったので、SC1815でドライブしました。
送信するコードについては、IRremoteをSpark Core用にカスタマイズされたものを使用させて頂きました。
https://github.com/qwertzguy/Spark-Core-IRremote
こちらのライブラリは、PWMではなく digitalWrite() と delayMicroseconds() でIRパルスを生成しています。パルスの最大周波数は166kHzなので、普通(ほとんどのIRコードは38khz)は、そのままで問題なく使えるはずです。
Spark Codeでは、エンドポイントを自由に設計できます。次のコードでは、int Infrared(String _); というWebAPIを公開しています。(引数及び戻り値は固定)
#include "IRremote.h" //WebAPIの型宣言 int Infrared(String _); IRsend irsend(D3); void setup() { //WebAPIの公開 Spark.function("Infrared", Infrared); } void loop() { } int khz = 38; // 38kHz carrier frequency for the NEC protocol unsigned int irSignal[] = {9000,4300, 700,400, 700,1550, 650,450, 700,450, 650,450, 650,450, 700,450, 650,1550, 650,1600, 650,450, 650,1550, 700,1550, 650,450, 700,1500, 650,1600, 700,400, 700,450, 650,1550, 700,400, 700,1550, 650,1550, 650,450, 650,500, 650,450, 700,1550, 650,450, 700,1500, 650,500, 650,450, 700,1550, 700,1500, 650,1550, 650}; int Infrared(String _){ int len = sizeof(irSignal) / sizeof(int); irsend.sendRaw(irSignal, len, khz); return 1; }
そして、エンドポイントは、デバイスのIDとトークンでアクセスできます。
$ curl https://api.spark.io/v1/devices/{デバイスID}/{ファンクション名} \ -d access_token={アクセストークン} \ -d “パラメータ文字列”
curlで蛍光灯をON/OFFしているようすです。
4 スマートホームスキルの作成
ここまでくれば、後はスマートホームスキルで、エンドポイントを叩くだけです。 Alexa開発者コンソールで「スマートホームスキル」を作成します。
スマートホームスキルでは、アカウントリンクが必須のため、とりあえずLogin with Amazonでアカウントリンクしていますが、今回は、トークンは利用していません。
参考:[Alexa] Login with Amazon との Account Linking で名前を呼びかける挨拶とかメールを送信するスキルを作ってみました
スマートホームスキルは、カスタムスキルと違ってLambdaでしか作成できません。また、トリガーは、Alexa Smart Homeとなります。
Alexaアプリで、作成したスキルを有効にするとアカウントリンクが始まります。
Amazonのアカウントで認可をしておきます。
アカウントリンクの後に表示される端末検出で、「端末の検出」 を選択します。
この時点で、Alexa.Discoveryのディレクティブがスキルに渡され、Lambdaで返したデバイスが表示されることになります。
作成したスマートホームスキルは、以下のとおりです。Discoveryに対しては、Alexa.PowerControllerのデバイスを返し、Alexa.PowerControllerのアクションであるTurnOnとTurnOffで、先程作成したエンドポイントを叩いているだけです。
const http = require('request'); exports.handler = function (request, context) { if (request.directive.header.namespace === 'Alexa.Discovery' && request.directive.header.name === 'Discover') { console.log("Discover request " + JSON.stringify(request)); handleDiscovery(request, context, ""); } else if (request.directive.header.namespace === 'Alexa.PowerController') { if (request.directive.header.name === 'TurnOn' || request.directive.header.name === 'TurnOff') { console.log("TurnOn or TurnOff Request " + JSON.stringify(request)); handlePowerControl(request, context); } } function handleDiscovery(request, context) { let payload = { "endpoints": [ { "endpointId": "demo_id", "manufacturerName": "SAPPOROWORKS", "friendlyName": "蛍光灯", "description": "私の部屋のリモコンが壊れた蛍光灯", "displayCategories": ["LIGHT"], "capabilities": [ { "interface": "Alexa.PowerController", "version": "3", "type": "AlexaInterface", "properties": { "supported": [{ "name": "powerState" }], } } ] } ] }; let header = request.directive.header; header.name = "Discover.Response"; console.log("Discovery Response: " + JSON.stringify({ header: header, payload: payload })); context.succeed({ event: { header: header, payload: payload } }); } function handlePowerControl(request, context) { let requestMethod = request.directive.header.name; let powerResult; if (requestMethod === "TurnOn") { powerResult = "ON"; } else if (requestMethod === "TurnOff") { powerResult = "OFF"; } let contextResult = { "properties": [{ "namespace": "Alexa.PowerController", "name": "powerState", "value": powerResult, "uncertaintyInMilliseconds": 50 }] }; let responseHeader = request.directive.header; responseHeader.namespace = "Alexa"; responseHeader.name = "Response"; responseHeader.messageId = responseHeader.messageId + "-R"; let response = { context: contextResult, event: { header: responseHeader }, payload: {} }; console.log("Alexa.PowerController " + JSON.stringify(response)); let deviceId = 'xxxxxxxxxxxxxxxxxx'; let accessToken = 'xxxxxxxxxxxxxxxxxx'; let options = { url: 'https://api.spark.io/v1/devices/' + deviceId + '/Infrared', method: 'POST', json: true, form: {"access_token":accessToken} } http(options, function (error, res, body) { console.log('STATUS: ' + res.statusCode); context.succeed(response); }) console.log('finish'); } };
5 最後に
動作している様子は次のとおりです。今回のリモコンは、ボタンが1個で、押すたびに「消灯」「蛍光灯2本」「蛍光灯1本」「豆球」がローテーションするタイプなので、実は、「つけて」と言っても「消して」と言っても同じです。
でも、これで、もう2階までリモコンを取りに行かなくてもいいので嬉しいです。
今回、初めてスマートホームスキルを作成してみました。仕組みが分かってしまえば、結構簡単に作れそうです。先日、赤外線リモコン学習デバイスがAmazonから届いていたので・・・今度は、Arduinoでなく、そっちでちゃんと作ってみたいと思います。