LoginSignup
9
19

More than 3 years have passed since last update.

Xiaomi Mijia 温湿度計 をIoTデバイスとして使う

Last updated at Posted at 2018-12-27

以前の投稿で、ローカルネットワークにMosquittoによるIoTブリッジサーバを立ち上げたので、これからいろんなデータをIoTに上げてみたいと思います。

AWS IoTにMosquittoをブリッジとしてつなぐ

(可視化はいろいろややこしそうなので、おいおい勉強します)

今回のIoTデバイスは、Xaomi Mijia 温湿度計 です。
以下の方が詳しくご紹介されています。(参考にさせていただきました。ありがとうございます)

Xiaomi Mijia 温湿度計レビュー 表示スクリーン付き+スマホ ログ確認
 https://bey.jp/?p=63555

(値段を調べてみるとわかるのですが、とにかく安いです。2千円しないです。。。)
この製品は、BLE通信で、温度と湿度を取得することができます。もちろん液晶がついているので、見ても確認できるのですが、これを継続的に蓄積しよう、ということです。

これを室内に配置し、Raspberry PiからBLE接続して、MQTTでAWS IoTに温度と湿度をアップします。AWS IoT側では、受信を契機に、DynamoDBにタンキングします。
なぜ、DynamoDBにしたかというと、今後さまざまなデバイスをIoTで上げようと思っていまして、それぞれのデータの内容が異なるため、スキーマレスなデータベースを使いたかったためです。

Xiaomi Mijia 温湿度計のセットアップ

電池入れて、好きな場所に置くだけです。理由は以降の章で分かります。

Raspberry Piのセットアップ

Node.jsからBLE通信ができるように準備をします。
私の場合は、Raspberry Pi 3ではないので、使っていなかったBLE4.0対応のUSBドングルをRaspberry Piにぶっさしています。(たいていのBLE4.0対応のUSBドングルで、大丈夫だと思います)

BLE接続に使うnpmモジュールは、毎度の「noble」です。
以下のページに、OSごとのセットアップ方法があるので、インストールしておいてください。

温湿度を取得しPublishする手順

温湿度を取得する実装に入る前に、仕組みから。

普通BLEデバイスは、ScanしてペアリングしてCharacteristicをReadして。。。などするかと思いますが、この温湿度計は、単にアドバタイズデータに温湿度が含まれているだけなのです。だから、温湿度計のセットアップもなにもいらないのです。

しかも、有志の方が、npmモジュールを用意してくださっているので、何も苦労はいりません。

node-xiaomi-gap-parser
 https://github.com/LynxyssCZ/node-xiaomi-gap-parser

MQTTのPublishは、これもnpmモジュール「mqtt」を使います。
以下の流れです。

① nobleでアドバタイズデータを取得
② node-xiaomi-gap-parserでアドバタイズデータを解析して、温湿度を取得
③ mqttで、イントラネットにあるMosquittoサーバにPubish
④ Mosquittoサーバは、AWS IoTにPublishデータを転送
⑤ AWS IoTは、受け取ったPublishデータをDynamoDBに格納

※③④の部分は、すでにセットアップが完了している前提です。以下を参考にしてください。
 AWS IoTにMosquittoをブリッジとしてつなぐ

温湿度を取得しPublishする実装

以下のnpmモジュールを使います。インストールしておきましょう。

npm init -y
npm install --save dotenv
npm install --save mqtt
npm install --save noble
npm install --save xiaomi-gap-parser

以下、実装です。

index.js
var noble = require('noble');
var xiaomi = require('xiaomi-gap-parser');
var mqtt = require('mqtt');
require('dotenv').config();

const MQTT_HOST = process.env.MQTT_HOST || 【Mosquittoサーバのホスト名】;
const MQTT_TOPIC = process.env.MQTT_TOPIC || 【Publishするトピック名】;
const SCAN_DURATION = process.env.SCAN_DURATION ? parseInt(process.env.SCAN_DURATION) : 【BLEスキャン時間】;

console.log('MQTT_HOST=' + MQTT_HOST);
console.log('MQTT_TOPIC=' + MQTT_TOPIC);

var client = mqtt.connect(MQTT_HOST);

function wait_async(timeout){
    return new Promise((resolve, reject) =>{
        setTimeout(resolve, timeout);
    });
}

noble.on('stateChange', async function(state) {
    console.log('stateChange: ' + state);
    if (state === 'poweredOn') {
            console.log('Start Scanning');
            noble.startScanning(["fe95"]);
            await wait_async(SCAN_DURATION);
            console.log('Stop Scanning');
            noble.stopScanning();
            process.exit();
    } else {
        noble.stopScanning();
        process.exit();
    }
});

noble.on('discover', function(peripheral) {
    var serviceData = peripheral.advertisement.serviceData;
    if (serviceData && serviceData.length) {
        for (var i in serviceData) {
            if( serviceData[i].uuid == 'fe95' ){
                var data = serviceData[i].data;
                var mijia = xiaomi.readServiceData(data);
                console.log(mijia);
                console.log(mijia.event.data);

                var date = new Date();

                var message = {
                    productId: mijia.productId,
                    counter: mijia.counter,
                    mac: mijia.mac,
                    tmp: mijia.event.data.tmp,
                    hum: mijia.event.data.hum,
                    createdat: date.getTime(),
                    createdatstr: date.toLocaleString()
                };
                console.log(message);
                client.publish(MQTT_TOPIC, JSON.stringify(message), 0, function(err, granted){
                    if( err ){
                        console.log(err);
                        return;
                    }
                    console.log('publish OK');
                });

                noble.stopScanning();
            }
        }
    }
});

以下を環境に合わせて変更してください。

【Mosquittoサーバのホスト名】
【Publishするトピック名】例えば、「awsiot/mijia」とします。
【BLEスキャン時間】:温湿度計のアドバタイズ間隔は長めなので、このスキャン時間も眺めがよいです。例えば、30000(30秒)とします。

Publishするデータのフォーマットは以下の部分です。

var message = {
    productId: mijia.productId,
    counter: mijia.counter,
    mac: mijia.mac,
    tmp: mijia.event.data.tmp,
    hum: mijia.event.data.hum,
    createdat: date.getTime(),
    createdatstr: date.toLocaleString()
};

DynamoDBのテーブルを作成する。

Publishデータを格納するDynamoDBのテーブルをあらかじめ作成しておきます。

image.png

テーブル名は例えば、「MqttDataset」とします。
パーティションキーは「type」(文字列)とし、ソートキーとして「createdat」(文字列)としました。

AWS IoT側でDynamoDBに登録されるようにする

AWS IoTで受け取ったPublishデータをDynamoDBに登録するために、AWS IoTのルールを定義します。
左側のナビゲータから、ACTを選択します。

image.png

右上の作成を押下します。
適当な名前を付けます。例えば、「IoTAnalytics_mijia」

image.png

ルールクエリステートメントには、以下のように指定します。FROMの部分はトピック名です。

 SELECT * FROM 'awsiot/mijia'

「アクションの追加」ボタンを押下します。

image.png

DynamoDBテーブルにメッセージを挿入する を選択します。

image.png

テーブル名には、さきほど作成したDynamoDBのテーブル名を指定します。
そうすると、ハッシュキーやレンジキーおよびそのタイプが自動的に入力されます。
ハッシュキー値には、今後追加されるであろう他のデータと区別するために、「mijia」としました。
レンジキーの値には、「${capturedat}」としました。
このcapturedatは、さきほどの実装の中で説明しましたが、Publishデータの中の1項目で、温湿度を取得した日時です。

それから、ロールにDynamoDBに書き出す権限を与えます。ページの下の方に、アクセス権を与えるためのボタンがあります。
新規ロール作成の場合には「新しいロールの作成」ボタンを押下すればよいですが、既存のロールを再利用する場合は、「ロールの更新」ボタンを忘れずに押下しておきましょう。

image.png

実行

まずは、AWS IoT側で、Subscribe状態にしておきます。

image.png

nobleでBLEを使うので、Admin権限が必要です。
以下の感じで結果が出力されれば成功です。

> node index.js
MQTT_HOST=mqtt://【Mosquittoサーバのホスト名】
MQTT_TOPIC=awsiot/mijia
stateChange: poweredOn
Start Scanning
{ productId: 426,
  counter: 252,
  frameControl: [ 'MAC_INCLUDE', 'EVENT_INCLUDE' ],
  mac: 'XXXXXXXXXXXX',
  event:
   { eventID: 4109,
     length: 4,
     raw: 'ce00cd01',
     data: { tmp: 20.6, hum: 46.1 } } }
{ tmp: 20.6, hum: 46.1 }
{ productId: 426,
  counter: 252,
  mac: 'XXXXXXXXXXXX',
  tmp: 20.6,
  hum: 46.1,
  capturedat: '2018-12-27 18:26:20' }
publish OK
Stop Scanning

温度が20.6度、湿度が46.1% であることがわかります。
AWS IoT側にもPublishされました。

image.png

DynamoDBにも登録されました。

image.png

※ときどき、温度と湿度のいずれかが欠ける時があるようです。

今回は一例として、Mijiaを使った温湿度を取得しました。
今後も取得するデータを増やしていこうと思います。

(補足) 10分毎に温湿度を取得する

さて、node index.js とすることで、MQTT経由で温湿度がアップされるようになりました。
ただ、1実行1アップですので、定期的に実行させたいです。

そこで、以前に作った「PM2 Process Management」を使いたいと思います。

PM2をもっともっと快適に!

image.png

「App追加」ボタンを押下して、アプリ定義を追加します。

image.png

  • name:適当な名前を付けます。例えば、「MqttMijia」とします。
  • memo:適当に
  • script:index.js
  • cwd:index.jsを置いたフォルダを記載します。
  • url:指定不要です。Webページがないため
  • cron:10分間隔で起動させたいため「*/10 * * * *」としました。
  • autorestart:false(cronを使うため)

そして、AppListのMqttMijiaの「開始」ボタンを押下すると、起動して、稼働監視対象になります。
statusがonlineになりますが、1分ぐらいたってから「ProcList更新」ボタンを押下すると、stoppedになります。10分間隔で、起動・停止を繰り返すためです。

以上です。

9
19
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
19