目次
- 赤外線リモコンを自作する - その1データ解析編
- 赤外線リモコンを自作する - その2データ送信編
- 赤外線リモコンを自作する - その3温度/湿度センサー編
- 赤外線リモコンを自作する - その4サーバサイド編
- 赤外線リモコンを自作する - その5クライアント編
赤外線リモコンを自作する - その4サーバサイド編
今回は、赤外線リモコンであるArduinoに対して外部から命令を出すためのサーバサイドプログラムについて説明します。
開発環境
サーバサイドプログラムはAWS上にServerless Frameworkを使ってデプロイします。
- AWS (API GW, Lambda, DynamoDB, SQS)
- Serverless Framework
sls -v
Running "serverless" from node_modules
Framework Core: 3.19.0
Plugin: 6.2.2
SDK: 4.3.2
全体の流れ
予めその1データ解析編で取得した赤外線信号を、DynamoDBに保存します。
利用者は、タスク(赤外線信号の送信命令)をサーバに登録します。
Arduinoは定期的にセンサ値(温度、湿度)をサーバにアップロードする。この際、タスクが存在する場合には、サーバは返り値として赤外線信号を返します。
赤外線信号を受信した場合には、Arduinoが赤外線信号を送信し、機器を操作します。
サーバサイドプログラム
package.json
{
"dependencies": {
"aws-sdk": "^2.1158.0"
}
}
serverless.yml
service: homeir
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs12.x
stage: v1
region: ap-northeast-1
profile: default
logRetentionInDays: 7
environment:
DB_TABLE_COMMANDS: ${self:service}-commands
DB_TABLE_SENSOR_VALUES: ${self:service}-sensor-values
SQS_TASK_URL: { Ref: TaskQueue }
iam:
role:
statements:
- Effect: Allow
Action: # 必要に応じて制限してください
- dynamodb:*
- s3:*
- sqs:*
Resource: "*"
package:
patterns:
- '!**'
- handler.js
functions:
send:
handler: handler.send
events:
- http:
method: post
path: /send
cors:
origins:
- '*'
saveCommand:
handler: handler.saveCommand
createTask:
handler: handler.createTask
events:
- http:
method: post
path: /tasks
cors:
origins:
- '*'
resources:
Resources:
DbTableCommands:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:provider.environment.DB_TABLE_COMMANDS}
AttributeDefinitions:
- AttributeName: key
AttributeType: S
KeySchema:
- AttributeName: key
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
DbTableSensorValues:
Type: AWS::DynamoDB::Table
Properties:
TableName: ${self:provider.environment.DB_TABLE_SENSOR_VALUES}
AttributeDefinitions:
- AttributeName: timestamp
AttributeType: N
KeySchema:
- AttributeName: timestamp
KeyType: HASH
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TaskQueue:
Type: "AWS::SQS::Queue"
Properties:
QueueName: ${self:service}-task
MessageRetentionPeriod: 7200 # メッセージ保持秒数
handler.js
'use strict';
const aws = require('aws-sdk');
const dynamoDB = new aws.DynamoDB.DocumentClient();
const sqs = new aws.SQS({apiVersion: '2012-11-05'});
module.exports.saveCommand = async (event) => {
await dynamoDB.put({
TableName: process.env.DB_TABLE_COMMANDS,
Item: {
key: process.env.key,
command: process.env.command
}
}).promise();
return "OK";
}
module.exports.createTask = async (event) => {
console.log(event.body);
const json = JSON.parse(event.body);
// 送信すべき赤外線データを取得
const commandItem = await dynamoDB.get({
TableName: process.env.DB_TABLE_COMMANDS,
Key: {
key: json.key
}
}).promise();
if (!commandItem) {
return {
statusCode: 404,
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
body: JSON.stringify({ 'success': false }),
};
}
const commandStr = commandItem.Item.command;
const length = (commandStr.match(/\,/g) || []).length + 1
await sqs.sendMessage({
MessageBody: JSON.stringify({
command: commandStr,
length: length,
ceratedAt: Date.now()
}),
QueueUrl: process.env.SQS_TASK_URL
}).promise();
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
body: JSON.stringify({ 'success': true }),
};
}
module.exports.send = async (event) => {
console.log(event.body);
const json = JSON.parse(event.body);
// センサデータの保存
await dynamoDB.put({
TableName: process.env.DB_TABLE_SENSOR_VALUES,
Item: {
timestamp: Date.now(),
humidity: json.humidity,
temperature: json.temperature
}
}).promise();
// SQSより赤外線情報を取得
const sqsMessage = await sqs.receiveMessage({
QueueUrl: process.env.SQS_TASK_URL,
MaxNumberOfMessages: 1
}).promise();
console.log(sqsMessage);
var response = {'command': '', 'length': 0};
if (sqsMessage.Messages) {
const message = sqsMessage.Messages[0];
const json = JSON.parse(message.Body)
response['command'] = json.command;
response['length'] = json.length;
await sqs.deleteMessage({
QueueUrl: process.env.SQS_TASK_URL,
ReceiptHandle: message.ReceiptHandle
}).promise()
}
console.log(response);
return {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json',
},
body: JSON.stringify(response),
};
};
関数説明
saveCommand
saveCommand関数を使って、赤外線信号をDBに保存します。keyに信号名、commandに赤外線信号を設定し、直接Lambdaを呼び出すことでDynamoDBに保存します。
sls invoke -f saveCommand --env key=workplace_light_off --env command=3502,1732,44...
createTask
利用者が、タスク(赤外線信号の送信命令)をサーバに登録するためのものです。SlackやLINE APIを経由して操作できる想定なので、POSTメソッドにより外部から呼び出せます。
タスクを受け取った場合には、タスク名から赤外線信号を取得し、SQSに命令を登録します。
BodyとしてsaveCommandで登録した信号名を指定します。
{
"key": "workplace_ac_off"
}
send
Arduinoがセンサ値(温度、湿度)をアップロードするためのエンドポイントです。
アップロードの際、タスクが存在する場合には、サーバは返り値として赤外線信号を返します。
次回
次回、クライアントであるArduino側のプログラムを解説します。