この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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でなく、そっちでちゃんと作ってみたいと思います。