Help us understand the problem. What is going on with this article?

kintone から MQTTS に送信(Subscribe) し Raspberry Pi の遠隔制御結果を表示(kintone側の処理)

More than 1 year has passed since last update.

処理内容

・kintone で 命令を選択し、制御命令レコードを追加すると、MQTTS にコマンドを送信(Publish)します
・MQTTS からコマンドを受信 (Subscribe) した Raspberry Pi が、制御コマンドを受信し、命令の処理(計測・撮影)を行います。
・Raspberry Pi から API 経由で kintone のレコードにデータを PUT して更新します。
・kintone の詳細画面で計測・撮影結果が表示されます。(結果が戻るか、指定回数に到達するまでは ReLoad を繰り返す。)
Raspberry Pi 側の処理結果は https://qiita.com/yukataoka/items/b3a7e5e9dc7471b5b66d を参照

完成画面

命令を選択し「保存」します。(保管と同時に、制御指示をMQTTSに送信。)
kintopneMqtt01.png
遠隔地の Raspberry Pi が計測温湿度(3秒後)・撮影画像(8秒後)が kintone に保管され、表示されます。
kintopneMqtt02.png

設定画面

kintone の設定画面を開きます。
kintopneMqtt03.png
以下のように JavaScript のライブラリィを参照し、カスタマイズしたプログラムをアップロードします。
kintopneMqtt04.png

参照するライブラリィは以下
https://sdk.amazonaws.com/js/aws-sdk-2.2.37.min.js
https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.min.js
https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core-min.js
https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/hmac-min.js
https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/sha256-min.js
https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js

ブラウザ内の AWS SDK for JavaScript
https://aws.amazon.com/jp/sdk-for-browser/

[JavaScript] moment.jsで日付時刻操作を行うTips
https://qiita.com/Sa2Knight/items/092a764679f960de81c9

crypto-js
https://github.com/brix/crypto-js

HTML5からMQTTに接続する方法
https://qiita.com/MahoTakara/items/8024f78d057d84cbc012

AWS IoTのMQTT over WebSocketにHTMLから接続してみた
https://dev.classmethod.jp/cloud/aws/aws-iot-mqtt-over-websocket/

kintone JavaScript コード

kintone の制御指示画面でレコードが追加された後、制御指示用の情報と、kintone の API 接続用の情報を MQTTS に送信しています。
kintone のレコード追加後に遷移した詳細画面で、Raspberry Pi から API 経由で更新されたセンサの情報や、カメラの画像を表示します。

kintoneAwsIot.js
(function() {
    "use strict";

    // レコード追加時イベント
    kintone.events.on('mobile.app.record.create.show', function(event) {
        alert("この機能をモバイルで使う時は、ブラウザのPC表示でご利用ください!");
        window.history.back(-1);
    });

    // レコード追加後のイベント
    var eventsCreateSuccess = ['app.record.create.submit.success'];
    kintone.events.on(eventsCreateSuccess, function(event) {
        var record = event.record;
        // 計測・撮影した Raspberry から kintone へ Respons を送信するための情報を送信
        var sendData = '{';
        sendData += '"command":"'+record['command']['value']+'",';
        sendData += '"token":"KINTONE_TOKEN",';
        sendData += '"appid":"KINTONE_APP_ID",';
        sendData += '"domain":"KINTONE_SUBDOMEIN",';
        sendData += '"key":"'+record['レコード番号']['value']+'"';
        sendData += '}'
        sendMqtts(sendData);
        return event;
    });

    // レコード詳細表示時イベント
    var eventsDetailShow = ['app.record.detail.show'];
    kintone.events.on(eventsDetailShow, function(event) {
        var record = event.record;
        if(record['command']['value'] == "温湿度取得" && record['temp']['value'] == ""){
            setInterval("location.reload()",3000);
        }else if(record['command']['value'] != "温湿度取得" && record['picture']['value'] == ""){
            setInterval("location.reload()",5000);
        }
    });

    var data = {
        messages: []
    };

    function SigV4Utils(){}
    SigV4Utils.sign = function(key, msg) {
        var hash = CryptoJS.HmacSHA256(msg, key);
        return hash.toString(CryptoJS.enc.Hex);
    };
    SigV4Utils.sha256 = function(msg) {
        var hash = CryptoJS.SHA256(msg);
        return hash.toString(CryptoJS.enc.Hex);
    };
    SigV4Utils.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
        var kDate = CryptoJS.HmacSHA256(dateStamp, 'AWS4' + key);
        var kRegion = CryptoJS.HmacSHA256(regionName, kDate);
        var kService = CryptoJS.HmacSHA256(serviceName, kRegion);
        var kSigning = CryptoJS.HmacSHA256('aws4_request', kService);
        return kSigning;
    };

    function getCredentials(done) {
        AWS.config.region = 'ap-northeast-1';
        var cognitoidentity = new AWS.CognitoIdentity();
        var params = {
            IdentityPoolId: 'ap-northeast-1:COGNITO_IDENTITY_POOL_ID'
        };
        cognitoidentity.getId(params, function(err, objectHavingIdentityId) {
            if (err) return done(err);
            cognitoidentity.getCredentialsForIdentity(objectHavingIdentityId, function(err, data) {
                if (err) return done(err);
                var credentials = {
                    accessKey: data.Credentials.AccessKeyId,
                    secretKey: data.Credentials.SecretKey,
                    sessionToken: data.Credentials.SessionToken
                };
                console.log('CognitoIdentity has provided temporary credentials successfully.');
                done(null, credentials);
            });
        });
    }

    function getEndpoint(regionName, awsIotEndpoint, done) {
        getCredentials(function (err, creds) {
            if (err) return done(err);

            var time = moment.utc();
            var dateStamp = time.format('YYYYMMDD');
            var amzdate = dateStamp + 'T' + time.format('HHmmss') + 'Z';
            var service = 'iotdevicegateway';
            var region = regionName;
            var secretKey = creds.secretKey;
            var accessKey = creds.accessKey;
            var algorithm = 'AWS4-HMAC-SHA256';
            var method = 'GET';
            var canonicalUri = '/mqtt';
            var host = awsIotEndpoint;
            var sessionToken = creds.sessionToken;

            var credentialScope = dateStamp + '/' + region + '/' + service + '/' + 'aws4_request';
            var canonicalQuerystring = 'X-Amz-Algorithm=AWS4-HMAC-SHA256';
            canonicalQuerystring += '&X-Amz-Credential=' + encodeURIComponent(accessKey + '/' + credentialScope);
            canonicalQuerystring += '&X-Amz-Date=' + amzdate;
            canonicalQuerystring += '&X-Amz-SignedHeaders=host';

            var canonicalHeaders = 'host:' + host + '\n';
            var payloadHash = SigV4Utils.sha256('');
            var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\nhost\n' + payloadHash;

            var stringToSign = algorithm + '\n' +  amzdate + '\n' +  credentialScope + '\n' +  SigV4Utils.sha256(canonicalRequest);
            var signingKey = SigV4Utils.getSignatureKey(secretKey, dateStamp, region, service);
            var signature = SigV4Utils.sign(signingKey, stringToSign);

            canonicalQuerystring += '&X-Amz-Signature=' + signature;

            var wssEndpoint = 'wss://' + host + canonicalUri + '?' + canonicalQuerystring;
            wssEndpoint += '&X-Amz-Security-Token=' + encodeURIComponent(sessionToken);
            done(null, wssEndpoint);
        });
    }

    var client;
    getEndpoint(
        'ap-northeast-1', 
        'HOST_NAME.iot.ap-northeast-1.amazonaws.com',
        function(err, endpoint) {
            if (err) {
                console.log('Failed', err);
                return;
            }
            var clientId = Math.random().toString(36).substring(7);
            client = new Paho.MQTT.Client(endpoint, clientId);
            var connectOptions = {
                useSSL: true,
                timeout: 3,
                mqttVersion: 4,
                onSuccess: subscribe
            };
            client.connect(connectOptions);
            client.onMessageArrived = onMessage;
            client.onConnectionLost = function(e) { console.log(e) };
        }
    );

    function subscribe() {
        client.subscribe("command/picture");
    }

    function sendMqtts(content) {
        var message = new Paho.MQTT.Message(content);
        message.destinationName = "human/picture";
        client.send(message);
    }

    function onMessage(message) {
        data.messages.push(message.payloadString);
    }
})();

参照情報

MQTT の基本知識 ( https://www.ibm.com/developerworks/jp/iot/library/iot-mqtt-why-good-for-iot/index.html )
MQTT Over the WebSocket Protocol ( https://docs.aws.amazon.com/iot/latest/developerguide/protocols.html#mqtt-ws )
AWS IoTのMQTT over WebSocketにHTMLから接続してみた ( https://dev.classmethod.jp/cloud/aws/aws-iot-mqtt-over-websocket/ )
AWS IoTのMQTT over WebSocketをCognito(Unauth)で認証して使ってみた ( https://dev.classmethod.jp/cloud/aws/aws-iot-mqtt-over-websocket-cognito-identity-unatuh/ )

yukataoka
若い頃にUターンしたIT屋で、出身は高知県仁淀川町。IoTとクラウドを活用した地域課題解決などに挑戦。 コミュニティ活動の傍ら、サイボウズ公認 kintone エバンジェリスト も務める複業者。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away