どうしても IoTしたいので光センサを作りました
「光センサーで IoT(前編)」にてちゃんと光センサーを作ってラズパイに繋げてセンサーからの値を取れるようにしました。
「光センサーで IoT(中編)」にて Node-REDで値を取ってみました。
ここでは、AWSのエミュレーターである Localstackに取得したデータを溜めてみたいと思います。
なお、LocalStackのインストールについては「LocalStackを使う (AWSエミュレーター)」に書いています。
AWSのどのサービスをターゲットにするかを考えると、IoT Coreが適当と思います。
思います…が、LocalStackにおいて IoT Coreは Pro Edition(有償)の機能でして、お金をかけないと使えませんので断念しました。orz...
次に、AWS MQが良いのでは?と思いました。
思いました…が、以下略。
じゃ、何だったらできんのよ?!
ということで、DynamoDBにすることにします。
だんだん IoTから離れていってしまっていますが…。
貧乏は辛いですねぇ。
DynamoDBは AWSの KVS(Key Value Store)・NoSQLデータベースです。
AWSとしては結構前からあるサービスで、それ故に利用する例も多くあって、ある意味枯れていて、ある意味安定している、そんなサービスかと思います。
ここでは、Node-REDから DynamoDBにデータを定期的に書きにいきたい訳ですので、Node-REDのパレットから DynamoDBに対応したものがないか探してみました。
虫眼鏡のところで「dynamodb」として検索した結果です。
う〜ん、ちょっと古いですねぇ。
[node-red-contrib-aws]が対象ですので、対象行のノードを追加を押します。
ダウンロードした結果追加されたノードがこれらです。
この中から[AWS DynamoDB]ノードを使います。
これらノードのドキュメントはこちら。
node-red-contrib-aws 0.7.0
先に LocalStackの DynamoDBにテーブルを作っておきましょう。
LocalStackをインストールした UbuntuServer 22.04.2に LocalStack実行用に作った[localstack]ユーザーにて作業しています。
LocalStackをインストールすると一緒に入る awslocalコマンドでテーブルを作りますが、このコマンドは awsコマンドのラッパーですので、ドキュメントは awsコマンドのものを見ましょう。
ステップ 1: テーブルを作成します
テーブル名は「Hikari」とし、マシン名をパーティションキーに、日付時刻をソートキーにして、光センサーで取った明るさの値を格納するようにします。
localstack@UbuntuServer2204-1:~$ awslocal dynamodb create-table --table-name Hikari --attribute-definitions AttributeName=MachineName,AttributeType=S AttributeName=DateTime,AttributeType=S --key-schema AttributeName=MachineName,KeyType=HASH AttributeName=DateTime,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "MachineName",
"AttributeType": "S"
},
{
"AttributeName": "DateTime",
"AttributeType": "S"
}
],
"TableName": "Hikari",
"KeySchema": [
{
"AttributeName": "MachineName",
"KeyType": "HASH"
},
{
"AttributeName": "DateTime",
"KeyType": "RANGE"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": 1679882473.199,
"ProvisionedThroughput": {
"ReadCapacityUnits": 5,
"WriteCapacityUnits": 5
},
"TableSizeBytes": 0,
"ItemCount": 0,
"TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/Hikari",
"TableId": "0bb0032b-ce56-4d0b-85d6-5de94db342bd"
}
}
テーブルができました。
では、光センサーのあるラズパイの Node-REDでフローを作っていきましょう。
このアイコンのノードが DynamoDBを操作するものになります。
これをフローエディタの格子のところにドラッグ&ドロップしますとこんなノードのアイコンになるので、ダブルクリックします。
DynamoDBの接続先を設定するため、えんぴつアイコンを押します。
[Name]と[Region]はそのまま、[Access Id]と[Secret Key]は LocalStackの場合は嘘のもので構いませんので、適当に入れておきます。
追加を押します。
データを入力するには、[Operation]を[PutItem]にします。
[TableName]は上で作った通り[Hikari]にします。
[Item]はこのノードに対して、[msg.Item]で与えたもので上書きされますので空にしておいて下さい。
完了を押します。
以前作った CdSセルのノードと合わせてこんなフローを作りました。
injectionノードの実行間隔は 1分毎に変えています。
右にある緑色のは debugノードと言って、出力内容を確認するためのものです。
では、[データ編集]というノードの説明。
これは functionノードと言って、JavaScriptをそのまま記述するノードです。
ノーコードとは相反するノードですけど、ちょっと込み入った事をする際にはこれを使うことになります。
余り使い過ぎると「素の Node.jsで書いた方が話が早くね?」と本末転倒な事になりますのでご注意下さい。
実際に書いたソースはこうなっています。
let now = new Date();
let year = now.getFullYear();
let month = ("0" + (now.getMonth() + 1)).slice(-2);
let date = ("0" + now.getDate()).slice(-2);
let hour = ("0" + now.getHours()).slice(-2);
let minute = ("0" + now.getMinutes()).slice(-2);
let lx = String(msg.payload);
let datetime = year + month + date + hour + minute;
msg.Item = {"MachineName":{"S":"raspberrypi"},"DateTime":{"S": datetime },"lx":{"N": lx}};
msg.AWSConfig = {"endpoint":"http://192.168.1.102:4566"};
return msg;
ソートキーとなる日時を作り、光センサー(CdSセル)から取った数値と合わせて、書き込むデータ(msg.Item)を作っています。
このデータの作り方は、以下を参考にしました。
ステップ 2: コンソールまたは AWS CLI を使用して、テーブルにデータを書き込みます
[lx]が「ルクス」のことで、明るさの値です。
また、本物の AWSではなく LocalStackを使う場合は、[msg.AWSConfig]という変数に[endpoint]という名前で LocalStackの URLを入れてやります。
([192.168.1.120]は UbuntuServer 22.04.2の IPです)
この 2つを DynamoDBのノードに渡してやることで、LocalStackの DynamoDBに動的に生成したデータを渡すことができます。
こうしないと[127.0.0.1]にバインドされてしまい、外部からアクセスできません。
上手く行きますと 1分おきに 1行のデータが DynamoDBに書かれるはずです。
暫く放っておいてから、awslocalコマンドで DynamoDBからデータを取得してみました。
[MachineName]が[raspberrypi]のものを全件取得しています。
localstack@UbuntuServer2204-1:~$ awslocal dynamodb query --table-name Hikari --key-condition-expression 'MachineName = :machinename' --expression-attribute-values '{ ":machinename": { "S": "raspberrypi" }}'
{
"Items": [
{
"MachineName": {
"S": "raspberrypi"
},
"lx": {
"N": "500"
},
"DateTime": {
"S": "202203271541"
}
},
{
"MachineName": {
"S": "raspberrypi"
},
"lx": {
"N": "534"
},
"DateTime": {
"S": "202303272102"
}
},
{
"MachineName": {
"S": "raspberrypi"
},
"lx": {
"N": "513"
},
"DateTime": {
"S": "202303272103"
}
},
〜〜〜 中略 〜〜〜
{
"MachineName": {
"S": "raspberrypi"
},
"lx": {
"N": "892"
},
"DateTime": {
"S": "202303272118"
}
}
],
"Count": 18,
"ScannedCount": 18,
"ConsumedCapacity": null
}
上手く取得できていますね。\(^o^)/
明るさの値もちゃんと変化しています。
こうして一旦データベースに入ってしまえば、利用は如何様にもできますね。
ただ、できれば IoT Coreを使いたかったです(残念)。
この「後編」ですが、実はかなりハマりました。
DynamoDBのノードにある[Item]という入力欄は、[msg.payload]と書き込めば良いと思っていたのですが、全然うまく行かずエラーになるばかり。
結局このノードのソースを見て、[msg.Item]を渡されると、それでオーバーライドするって事が分かって解決しました。
数日を費やし、随分と遠回りしてやっと上手く生きましたけど、おかげさまで随分と勉強になりました。