共用方式為


將 Raspberry Pi 裝置連線到遠端監視解決方案加速器 (Node.js)

在此教學課程中,您會實作一個 Chiller 裝置,此裝置會將下列遙測資料傳送給遠端監視解決方案加速器

  • 溫度
  • 壓力
  • 溼度

為了簡單起見,程式碼會產生 Chiller 的範例遙測值。 您可以將實際感應器連線到您的裝置並傳送實際的遙測來擴充範例。

範例裝置也會:

  • 將中繼資料傳送至解決方案來描述其功能。
  • 回應從解決方案中的裝置頁面觸發的動作。
  • 回應從解決方案中的裝置頁面傳送的設定變更。

若要完成此教學課程,您需要一個有效的 Azure 帳戶。 如果您沒有帳戶,只需要幾分鐘的時間就可以建立免費試用帳戶。 如需詳細資料,請參閱 Azure 免費試用

開始之前

在您為裝置撰寫任何程式碼之前,請先部署遠端監視解決方案加速器,並將新的實體裝置新增至該解決方案。

部署遠端監視解決方案加速器

您在本教學課程中建立的 Chiller 裝置會將資料傳送給遠端監視解決方案加速器的執行個體。 如果您尚未在您的 Azure 帳戶中佈建遠端監視解決方案加速器,請參閱部署遠端監視解決方案加速器

當遠端監視解決方案的部署程序完成之後,請按一下 [啟動],以在瀏覽器中開啟解決方案儀表板。

解決方案儀表板

將您的裝置新增到遠端監視解決方案

注意

如果您已經在解決方案中新增裝置,則可以略過此步驟。 不過,下一個步驟需要您裝置的連接字串。 您可以從 Azure 入口網站或使用 az iot CLI 工具來擷取裝置的連線字串。

對於連線到解決方案加速器的裝置,該裝置必須使用有效的認證向 IoT 中樞識別自己。 當您將裝置新增至解決方案時,會有機會儲存包含這些認證的裝置連接字串。 稍後在本教學課程中,您會將裝置連接字串包含在您的用戶端應用程式中。

若要在遠端監視解決方案中新增裝置,請在解決方案的 [裝置總管] 頁面中完成下列步驟:

  1. 選擇 [+ 新增裝置],然後選擇 [Real] 作為 [裝置類型]:

    新增真實裝置

  2. 輸入 Physical-chiller 作為裝置識別碼。 選擇 [對稱金鑰] 和 [自動產生金鑰] 選項:

    選擇裝置選項

  3. 選擇 [套用]。 然後記下裝置識別碼主要金鑰連接字串主要金鑰值:

    擷取認證

您現在已將實體裝置新增至遠端監視解決方案加速器中,並將其裝置連接字串記下。 在下列章節中,您可以實作使用裝置連接字串來連線到您解決方案的用戶端應用程式。

用戶端應用程式會實作內建 Chiller 裝置型號。 解決方案加速器裝置型號會指定下列相關裝置資訊:

  • 裝置回報至解決方案的屬性。 例如,Chiller 裝置會報告其韌體和位置的相關資訊。
  • 裝置傳送至解決方案的遙測類型。 例如,Chiller 裝置傳會送溫度、溼度和壓力值。
  • 您可以從解決方案中排程以在裝置上執行的方法。 例如,Chiller 裝置必須實作 RebootFirmwareUpdateEmergencyValveReleaseIncreasePressure 方法。

本教學課程示範如何將真實裝置連線到遠端監視解決方案加速器。 在本教學課程中,您使用的 Node.js 是適用於環境資源限制最小的絕佳選項。

如果您偏好模擬裝置,請參閱建立及測試新模擬裝置

必要的硬體

一部桌上型電腦,可讓您從遠端連線到 Raspberry Pi 上的命令列。

適用於 Raspberry Pi 3 的 Microsoft IoT 入門套件或對等的元件。 本教學課程會使用套件中的下列項目:

  • Raspberry Pi 3
  • MicroSD 卡 (具有 NOOBS)
  • USB Mini 連接線
  • 乙太網路連接線

必要的桌面軟體

您需要透過桌上型電腦上的 SSH 用戶端,才能從遠端存取 Raspberry Pi 上的命令列。

  • Windows 不包含 SSH 用戶端。 我們建議使用 PuTTY
  • 大部分的 Linux 散發套件和 Mac OS 都包含命令列 SSH 公用程式。 如需詳細資訊,請參閱使用 Linux 或 Mac OS 的 SSH

必要的 Raspberry Pi 軟體

如果您尚未這麼做,請在 Raspberry Pi 上安裝 Node.js 4.0.0 版或更新版本。 下列步驟向您示範如何在 Raspberry Pi 上安裝 Node.js v6:

  1. 使用 ssh 連線至您的 Raspberry Pi。 如需詳細資訊,請參閱 Raspberry Pi 網站上的 SSH (安全殼層)

  2. 若要更新 Raspberry Pi,請使用下列命令︰

    sudo apt-get update
    
  3. 使用下列命令,從 Raspberry Pi 移除任何現有的 Node.js 安裝:

    sudo apt-get remove nodered -y
    sudo apt-get remove nodejs nodejs-legacy -y
    sudo apt-get remove npm  -y
    
  4. 使用下列命令,下載 Node.js v6 並將其安裝在 Raspberry Pi 上:

    curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
    sudo apt-get install nodejs npm
    
  5. 若要確認您已成功安裝 Node.js 6.11.4 版,請使用下列命令︰

    node --version
    

建立 Node.js 解決方案

使用 ssh 與 Raspberry Pi 的連線完成下列步驟:

  1. 在 Raspberry Pi 的主資料夾中,建立名為 remotemonitoring 的資料夾。 在命令列中瀏覽至此資料夾:

    cd ~
    mkdir remotemonitoring
    cd remotemonitoring
    
  2. 若要下載並安裝完成範例應用程式所需的套件,請執行下列命令︰

    npm install async azure-iot-device azure-iot-device-mqtt
    
  3. remotemonitoring 資料夾中,建立名為 remote_monitoring.js 的檔案。 在文字編輯器中開啟這個檔案。 在 Raspberry Pi 上,您可以使用 nanovi 文字編輯器。

  4. remote_monitoring.js 檔案中,新增下列 require 陳述式︰

    var Protocol = require('azure-iot-device-mqtt').Mqtt;
    var Client = require('azure-iot-device').Client;
    var Message = require('azure-iot-device').Message;
    var async = require('async');
    
  5. require 陳述式之後新增下列變數宣告。 針對您在遠端監視解決方案中所佈建的裝置,使用您記下的值來取代 {device connection string} 預留位置值:

    var connectionString = '{device connection string}';
    
  6. 如需定義一些基本遙測資料,請新增下列變數︰

    var temperature = 50;
    var temperatureUnit = 'F';
    var humidity = 50;
    var humidityUnit = '%';
    var pressure = 55;
    var pressureUnit = 'psig';
    
  7. 如需定義一些屬性值,請新增下列變數︰

    var schema = "real-chiller;v1";
    var deviceType = "RealChiller";
    var deviceFirmware = "1.0.0";
    var deviceFirmwareUpdateStatus = "";
    var deviceLocation = "Building 44";
    var deviceLatitude = 47.638928;
    var deviceLongitude = -122.13476;
    var deviceOnline = true;
    
  8. 新增下列變數來定義要傳送至解決方案的報告屬性。 這些屬性包括將在 Web UI 中顯示的中繼資料:

    var reportedProperties = {
      "SupportedMethods": "Reboot,FirmwareUpdate,EmergencyValveRelease,IncreasePressure",
      "Telemetry": {
        [schema]: ""
      },
      "Type": deviceType,
      "Firmware": deviceFirmware,
      "FirmwareUpdateStatus": deviceFirmwareUpdateStatus,
      "Location": deviceLocation,
      "Latitude": deviceLatitude,
      "Longitude": deviceLongitude,
      "Online": deviceOnline
    }
    
  9. 若要列印作業結果,請新增下列協助程式函式︰

    function printErrorFor(op) {
        return function printError(err) {
            if (err) console.log(op + ' error: ' + err.toString());
        };
    }
    
  10. 新增下列協助程式函式,用來將排遙測值隨機化︰

    function generateRandomIncrement() {
        return ((Math.random() * 2) - 1);
    }
    
  11. 新增下列泛型函式以處理解決方案的直接方法呼叫。 此函式會顯示已叫用之直接方法的相關資訊,但在此範例中不會以任何方式修改裝置。 解決方案會使用直接方法在裝置上執行:

    function onDirectMethod(request, response) {
      // Implement logic asynchronously here.
      console.log('Simulated ' + request.methodName);
    
      // Complete the response
      response.send(200, request.methodName + ' was called on the device', function (err) {
        if (err) console.error('Error sending method response :\n' + err.toString());
        else console.log('200 Response to method \'' + request.methodName + '\' sent successfully.');
      });
    }
    
  12. 新增下列函式以處理解決方案的 FirmwareUpdate 直接方法呼叫。 此函式會驗證傳入直接方法酬載中的參數,然後以非同步方式執行韌體更新模擬:

    function onFirmwareUpdate(request, response) {
      // Get the requested firmware version from the JSON request body
      var firmwareVersion = request.payload.Firmware;
      var firmwareUri = request.payload.FirmwareUri;
    
      // Ensure we got a firmware values
      if (!firmwareVersion || !firmwareUri) {
        response.send(400, 'Missing firmware value', function(err) {
          if (err) console.error('Error sending method response :\n' + err.toString());
          else console.log('400 Response to method \'' + request.methodName + '\' sent successfully.');
        });
      } else {
        // Respond the cloud app for the device method
        response.send(200, 'Firmware update started.', function(err) {
          if (err) console.error('Error sending method response :\n' + err.toString());
          else {
            console.log('200 Response to method \'' + request.methodName + '\' sent successfully.');
    
            // Run the simulated firmware update flow
            runFirmwareUpdateFlow(firmwareVersion, firmwareUri);
          }
        });
      }
    }
    
  13. 新增下列函式以模擬長時間執行的韌體更新流程,此流程會將進度回報至解決方案:

    // Simulated firmwareUpdate flow
    function runFirmwareUpdateFlow(firmwareVersion, firmwareUri) {
      console.log('Simulating firmware update flow...');
      console.log('> Firmware version passed: ' + firmwareVersion);
      console.log('> Firmware URI passed: ' + firmwareUri);
      async.waterfall([
        function (callback) {
          console.log("Image downloading from " + firmwareUri);
          var patch = {
            FirmwareUpdateStatus: 'Downloading image..'
          };
          reportUpdateThroughTwin(patch, callback);
          sleep(10000, callback);
        },
        function (callback) {
          console.log("Downloaded, applying firmware " + firmwareVersion);
          deviceOnline = false;
          var patch = {
            FirmwareUpdateStatus: 'Applying firmware..',
            Online: false
          };
          reportUpdateThroughTwin(patch, callback);
          sleep(8000, callback);
        },
        function (callback) {
          console.log("Rebooting");
          var patch = {
            FirmwareUpdateStatus: 'Rebooting..'
          };
          reportUpdateThroughTwin(patch, callback);
          sleep(10000, callback);
        },
        function (callback) {
          console.log("Firmware updated to " + firmwareVersion);
          deviceOnline = true;
          var patch = {
            FirmwareUpdateStatus: 'Firmware updated',
            Online: true,
            Firmware: firmwareVersion
          };
          reportUpdateThroughTwin(patch, callback);
          callback(null);
        }
      ], function(err) {
        if (err) {
          console.error('Error in simulated firmware update flow: ' + err.message);
        } else {
          console.log("Completed simulated firmware update flow");
        }
      });
    
      // Helper function to update the twin reported properties.
      function reportUpdateThroughTwin(patch, callback) {
        console.log("Sending...");
        console.log(JSON.stringify(patch, null, 2));
        client.getTwin(function(err, twin) {
          if (!err) {
            twin.properties.reported.update(patch, function(err) {
              if (err) callback(err);
            });      
          } else {
            if (err) callback(err);
          }
        });
      }
    
      function sleep(milliseconds, callback) {
        console.log("Simulate a delay (milleseconds): " + milliseconds);
        setTimeout(function () {
          callback(null);
        }, milliseconds);
      }
    }
    
  14. 新增下列程式碼將遙測資料傳送至解決方案。 用戶端應用程式會將屬性新增至訊息,以找出訊息結構描述:

    function sendTelemetry(data, schema) {
      if (deviceOnline) {
        var d = new Date();
        var payload = JSON.stringify(data);
        var message = new Message(payload);
        message.properties.add('iothub-creation-time-utc', d.toISOString());
        message.properties.add('iothub-message-schema', schema);
    
        console.log('Sending device message data:\n' + payload);
        client.sendEvent(message, printErrorFor('send event'));
      } else {
        console.log('Offline, not sending telemetry');
      }
    }
    
  15. 新增下列程式碼,以建立用戶端執行個體︰

    var client = Client.fromConnectionString(connectionString, Protocol);
    
  16. 新增下列程式碼,以便:

    • 開啟連線。

    • 設定所需屬性的處理常式。

    • 傳送報告屬性。

    • 登錄直接方法的處理常式。 此範例會針對韌體更新直接方法,使用個別的處理常式。

    • 開始傳送遙測。

      client.open(function (err) {
      if (err) {
        printErrorFor('open')(err);
      } else {
        // Create device Twin
        client.getTwin(function (err, twin) {
          if (err) {
            console.error('Could not get device twin');
          } else {
            console.log('Device twin created');
      
            twin.on('properties.desired', function (delta) {
              // Handle desired properties set by solution
              console.log('Received new desired properties:');
              console.log(JSON.stringify(delta));
            });
      
            // Send reported properties
            twin.properties.reported.update(reportedProperties, function (err) {
              if (err) throw err;
              console.log('Twin state reported');
            });
      
            // Register handlers for all the method names we are interested in.
            // Consider separate handlers for each method.
            client.onDeviceMethod('Reboot', onDirectMethod);
            client.onDeviceMethod('FirmwareUpdate', onFirmwareUpdate);
            client.onDeviceMethod('EmergencyValveRelease', onDirectMethod);
            client.onDeviceMethod('IncreasePressure', onDirectMethod);
          }
        });
      
        // Start sending telemetry
        var sendDeviceTelemetry = setInterval(function () {
          temperature += generateRandomIncrement();
          pressure += generateRandomIncrement();
          humidity += generateRandomIncrement();
          var data = {
            'temperature': temperature,
            'temperature_unit': temperatureUnit,
            'humidity': humidity,
            'humidity_unit': humidityUnit,
            'pressure': pressure,
            'pressure_unit': pressureUnit
          };
          sendTelemetry(data, schema)
        }, 5000);
      
        client.on('error', function (err) {
          printErrorFor('client')(err);
          if (sendTemperatureInterval) clearInterval(sendTemperatureInterval);
          if (sendHumidityInterval) clearInterval(sendHumidityInterval);
          if (sendPressureInterval) clearInterval(sendPressureInterval);
          client.close(printErrorFor('client.close'));
        });
      }
      });
      
  17. 將變更儲存至 remote_monitoring.js 檔案。

  18. 若要啟動範例應用程式,請在 Raspberry Pi 上的命令提示字元中執行下列命令:

    node remote_monitoring.js
    

檢視裝置遙測資料

您可在解決方案的 [裝置總管] 頁面中,檢視從裝置傳送的遙測。

  1. 在 [裝置總管] 頁面上選取裝置清單中已佈建的裝置。 面板會顯示您裝置的相關資訊,包括裝置遙測繪圖:

    請參閱裝置詳細資料

  2. 選擇 [壓力] 以變更遙測顯示器:

    檢視壓力遙測

  3. 若要檢視有關您裝置的診斷資訊,請向下捲動至 [診斷]:

    檢視裝置診斷

在裝置上採取行動

若要在裝置上叫用方法,請使用遠端監視解決方案中的 [裝置總管] 頁面。 例如,在遠端監視解決方案中,Chiller 裝置會實作 Reboot 方法。

  1. 選擇 [裝置] 以巡覽至解決方案的 [裝置總管] 頁面。

  2. 在 [裝置總管] 頁面中選取裝置清單中已佈建的裝置:

    選取您的實體裝置

  3. 若要顯示可在裝置上呼叫的方法清單,請依序選擇 [作業] 和 [方法]。 若要排程可在多個裝置上執行的作業,您可以在清單中選取多個裝置。 [作業] 面板會顯示所有您選取之裝置的通用方法。

  4. 選擇 [重新開機],將作業名稱設定為 RebootPhysicalChiller,然後選擇 [套用]:

    排程韌體更新

  5. 當模擬裝置處理該方法時,會在執行裝置程式碼的主控台中顯示一系列訊息。

注意

若要追蹤解決方案中的作業狀態,請選擇 [檢視作業狀態]。

後續步驟

自訂遠端監視解決方案加速器一文中,說明了一些自訂解決方案加速器的方法。