黃嵩仁
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- tags: 微軟,Azure --- # 6/14 Microsoft # Azure IoT 中樞 教學課程 (續) ## 訊息擴充 (message enrichments ) 訊息擴充 (message enrichments )功能能在訊息送至指定端點前附加額外資訊,可用來簡化下游處理的資料。 > 教學課程中,會建立指向兩個不同的儲存體容器的兩個端點(enriched、original)。將訊息傳送至 IoT 中樞時,它會傳送至這兩個儲存體容器。只有被傳送至enriched儲存體容器端點的訊息會被擴充。 sample code:https://github.com/Azure-Samples/azure-iot-samples-csharp/archive/master.zip * 安裝和設定資源 ``` # This command retrieves the subscription id of the current Azure account. # This field is used when setting up the routing rules. subscriptionID=$(az account show --query id -o tsv) # Concatenate this number onto the resources that have to be globally unique. # You can set this to "" or to a specific value if you don't want it to be random. # This retrieves a random value. randomValue=$RANDOM # This command installs the IOT Extension for Azure CLI. # You only need to install this the first time. # You need it to create the device identity. az extension add --name azure-cli-iot-ext # Set the values for the resource names that # don't have to be globally unique. location=westus2 resourceGroup=ContosoResourcesMsgEn containerName1=original containerName2=enriched iotDeviceName=Contoso-Test-Device # Create the resource group to be used # for all the resources for this tutorial. az group create --name $resourceGroup \ --location $location # The IoT hub name must be globally unique, # so add a random value to the end. iotHubName=ContosoTestHubMsgEn$randomValue echo "IoT hub name = " $iotHubName # Create the IoT hub. az iot hub create --name $iotHubName \ --resource-group $resourceGroup \ --sku S1 --location $location # You need a storage account that will have two containers # -- one for the original messages and # one for the enriched messages. # The storage account name must be globally unique, # so add a random value to the end. storageAccountName=contosostorage$randomValue echo "Storage account name = " $storageAccountName # Create the storage account to be used as a routing destination. az storage account create --name $storageAccountName \ --resource-group $resourceGroup \ --location $location \ --sku Standard_LRS # Get the primary storage account key. # You need this to create the containers. storageAccountKey=$(az storage account keys list \ --resource-group $resourceGroup \ --account-name $storageAccountName \ --query "[0].value" | tr -d '"') # See the value of the storage account key. echo "storage account key = " $storageAccountKey # Create the containers in the storage account. az storage container create --name $containerName1 \ --account-name $storageAccountName \ --account-key $storageAccountKey \ --public-access off az storage container create --name $containerName2 \ --account-name $storageAccountName \ --account-key $storageAccountKey \ --public-access off # Create the IoT device identity to be used for testing. az iot hub device-identity create --device-id $iotDeviceName \ --hub-name $iotHubName # Retrieve the information about the device identity, then copy the primary key to # Notepad. You need this to run the device simulation during the testing phase. # If you are using Cloud Shell, you can scroll the window back up to retrieve this value. az iot hub device-identity show --device-id $iotDeviceName \ --hub-name $iotHubName ##### ROUTING FOR STORAGE ##### # You're going to have two routes and two endpoints. # One points to container1 in the storage account # and includes all messages. # The other points to container2 in the same storage account # and only includes enriched messages. endpointType="azurestoragecontainer" endpointName1="ContosoStorageEndpointOriginal" endpointName2="ContosoStorageEndpointEnriched" routeName1="ContosoStorageRouteOriginal" routeName2="ContosoStorageRouteEnriched" # for both endpoints, retrieve the messages going to storage condition='level="storage"' # Get the connection string for the storage account. # Adding the "-o tsv" makes it be returned without the default double quotes around it. storageConnectionString=$(az storage account show-connection-string \ --name $storageAccountName --query connectionString -o tsv) # Create the routing endpoints and routes. # Set the encoding format to either avro or json. # This is the endpoint for container 1, for endpoint messages that are not enriched. az iot hub routing-endpoint create \ --connection-string $storageConnectionString \ --endpoint-name $endpointName1 \ --endpoint-resource-group $resourceGroup \ --endpoint-subscription-id $subscriptionID \ --endpoint-type $endpointType \ --hub-name $iotHubName \ --container $containerName1 \ --resource-group $resourceGroup \ --encoding json # This is the endpoint for container 2, for endpoint messages that are enriched. az iot hub routing-endpoint create \ --connection-string $storageConnectionString \ --endpoint-name $endpointName2 \ --endpoint-resource-group $resourceGroup \ --endpoint-subscription-id $subscriptionID \ --endpoint-type $endpointType \ --hub-name $iotHubName \ --container $containerName2 \ --resource-group $resourceGroup \ --encoding json # This is the route for messages that are not enriched. # Create the route for the first storage endpoint. az iot hub route create \ --name $routeName1 \ --hub-name $iotHubName \ --source devicemessages \ --resource-group $resourceGroup \ --endpoint-name $endpointName1 \ --enabled \ --condition $condition # This is the route for messages that are not enriched. az iot hub route create \ --name $routeName2 \ --hub-name $iotHubName \ --source devicemessages \ --resource-group $resourceGroup \ --endpoint-name $endpointName2 \ --enabled \ --condition $condition ``` * 檢視路由及設定訊息類 資源群組 > IoT 中樞 > 訊息路由 > "Enrich messages - preview" ![](https://i.imgur.com/2UqODbF.png) * 將訊息傳送至 IoT 中樞 > 中樞已設定規則: > * 將訊息路由傳送至儲存體端點 ContosoStorageEndpointOriginal 不進行擴充,並會儲存在儲存體容器original。 > * 會擴充並儲存在儲存體容器中將訊息路由傳送至儲存體端點 ContosoStorageEndpointEnriched enriched。 * 將訊息傳送至 IoT 中樞 ``` cd azure-iot-samples-csharp-master\iot-hub\Tutorials\Routing\SimulatedDevice ``` 修改Program.cs ``` static string myDeviceId = "contoso-test-device"; static string iotHubUri = "ContosoTestHubMsgEn.azure-devices.net"; // This is the primary key for the device. This is in the portal. // Find your IoT hub in the portal > IoT devices > select your device > copy the key. static string deviceKey = "{your device key here}"; ``` * 執行應用程式 ``` dotnet restore dotnet run ``` * 檢視資料 > 等待數分鐘後 資源群組 > 儲存體帳戶 > 儲存體總管 (預覽) > BLOB CONTAINERS ![](https://i.imgur.com/UEqfmKz.png) 下載結果可發現: enriched: ``` {"EnqueuedTimeUtc":"2019-06-07T15:32:12.4430000Z","Properties":{"level":"storage","myIotHub":"contosotesthubmsgen6147","Device location":"$twin.tags.location\t","customerID":"6ce345b8-1e4a-411e-9398-d34587459a3a"},"SystemProperties":{"connectionDeviceId":"Contoso-Test-Device","connectionAuthMethod":"{\"scope\":\"device\",\"type\":\"sas\",\"issuer\":\"iothub\",\"acceptingIpFilterRule\":null}","connectionDeviceGenerationId":"636955163414598356","enqueuedTime":"2019-06-07T15:32:12.4430000Z"},"Body":"eyJkZXZpY2VJZCI6IkNvbnRvc28tVGVzdC1EZXZpY2UiLCJ0ZW1wZXJhdHVyZSI6MjUuNjI1NzU4Mzc2MjYzOTAzLCJodW1pZGl0eSI6NjAuMzg5MjU0NjcwNzcxNDIyLCJwb2ludEluZm8iOiJUaGlzIGlzIGEgc3RvcmFnZSBtZXNzYWdlLiJ9"} ``` original: ``` {"EnqueuedTimeUtc":"2019-06-07T15:32:12.4430000Z","Properties":{"level":"storage"},"SystemProperties":{"connectionDeviceId":"Contoso-Test-Device","connectionAuthMethod":"{\"scope\":\"device\",\"type\":\"sas\",\"issuer\":\"iothub\",\"acceptingIpFilterRule\":null}","connectionDeviceGenerationId":"636955163414598356","enqueuedTime":"2019-06-07T15:32:12.4430000Z"},"Body":"eyJkZXZpY2VJZCI6IkNvbnRvc28tVGVzdC1EZXZpY2UiLCJ0ZW1wZXJhdHVyZSI6MjUuNjI1NzU4Mzc2MjYzOTAzLCJodW1pZGl0eSI6NjAuMzg5MjU0NjcwNzcxNDIyLCJwb2ludEluZm8iOiJUaGlzIGlzIGEgc3RvcmFnZSBtZXNzYWdlLiJ9"} ``` 比較可發現original版本裡面沒有"My IoT Hub", "device location", and "customerID"等資訊。 ## 使用計量和診斷紀錄 > 建議設定某些計量並啟用診斷記錄,之後若發生問題,就可以查看資料來協助您診斷問題,並加快修正速度。 > 本篇目標:啟用診斷記錄(設定某些要監看的計量,以及會在計量達到特定界限時引發的警示)、檢查記錄來找出錯誤 > eg. 讓系統在所傳送的遙測訊息數目超過特定界限時,或是在所使用的訊息數目接近 IoT 中樞每日允許的訊息配額時,傳送電子郵件 * 建立資源 ``` # This is the IOT Extension for Azure CLI. # You only need to install this the first time. # You need it to create the device identity. az extension add --name azure-cli-iot-ext # Set the values for the resource names that don't have to be globally unique. # The resources that have to have unique names are named in the script below # with a random number concatenated to the name so you can probably just # run this script, and it will work with no conflicts. location=westus resourceGroup=ContosoResources iotDeviceName=Contoso-Test-Device # Create the resource group to be used # for all the resources for this tutorial. az group create --name $resourceGroup \ --location $location # The IoT hub name must be globally unique, so add a random number to the end. iotHubName=ContosoTestHub$RANDOM echo "IoT hub name = " $iotHubName # Create the IoT hub in the Free tier. az iot hub create --name $iotHubName \ --resource-group $resourceGroup \ --sku F1 --location $location # The storage account name must be globally unique, so add a random number to the end. storageAccountName=contosostoragemon$RANDOM echo "Storage account name = " $storageAccountName # Create the storage account. az storage account create --name $storageAccountName \ --resource-group $resourceGroup \ --location $location \ --sku Standard_LRS # Create the IoT device identity to be used for testing. az iot hub device-identity create --device-id $iotDeviceName \ --hub-name $iotHubName # Retrieve the information about the device identity, then copy the primary key to # Notepad. You need this to run the device simulation during the testing phase. az iot hub device-identity show --device-id $iotDeviceName \ --hub-name $iotHubName ``` * 啟用診斷記錄 資源群組 (Contoso-Resources) > IoT 中樞 > 診斷設定 > 新增診斷設定 ![](https://i.imgur.com/sOCUS9s.png) ![](https://i.imgur.com/B6sBSQ0.png) * 設定計量 資源群組 (Contoso-Resources) > IoT 中樞 > 計量 ![](https://i.imgur.com/vsitO8U.png) ![](https://i.imgur.com/cyg2syJ.png) * 設定警示 資源群組 (Contoso-Resources) > IoT 中樞 > 警訊 > 檢視傳統警示 > 新增傳統警示 (針對"已傳送的遙測訊息") ![](https://i.imgur.com/zx9DHF7.png) ![](https://i.imgur.com/xg09GXb.png) ![](https://i.imgur.com/hJc0uQV.png) 以同樣方法,針對"已使用的訊息總數",建立傳統警示 ![](https://i.imgur.com/J9Wy8jI.png) ### 執行模擬裝置應用程式 sample code:https://github.com/Azure-Samples/azure-iot-samples-csharp/archive/master.zip ``` cd azure-iot-samples-csharp-master\iot-hub\Tutorials\Routing\SimulatedDevice ``` 修改Program.cs ``` static string myDeviceId = "contoso-test-device"; static string iotHubUri = "ContosoTestHub.azure-devices.net"; // This is the primary key for the device. This is in the portal. // Find your IoT hub in the portal > IoT devices > select your device > copy the key. static string deviceKey = "{your device key here}"; ``` > iotHubUri => IoT 中樞主機名稱 > deviceKey => 裝置金鑰 > Task.Delay:1000 => 10 * 執行和測試 ``` dotnet restore dotnet run ``` ![](https://i.imgur.com/z4GwhMA.png) ![](https://i.imgur.com/1UrtrT1.png) ![](https://i.imgur.com/dma8lYj.png) * 查看診斷記錄 資源群組 > 儲存體帳戶 > Blob > 容器 insights-logs-connections ![](https://i.imgur.com/vB59AGC.png) ## 執行手動容錯移轉 > 手動容錯移轉可讓客戶將其中樞的作業從主要區域容錯移轉到對應的 Azure 異地配對區域。發生區域性災難或延伸服務中斷的情況時,可以進行手動容錯移轉。 * 建立 IoT 中樞 > 同其他教學 * 執行手動容錯移轉 資源群組 > IoT 中樞 > 手動容錯移轉 (預覽) ![](https://i.imgur.com/aq20Cag.png) 初始容錯移轉 > 填寫 IoT 中樞的名稱 > 確定 > 過程中可看見 "從 West US 手動容錯移轉到 East US 正在進行中..."的狀態 ![](https://i.imgur.com/xjLkoGM.png) 容錯轉移完成後,可看見 IoT 中樞位置調換 ![](https://i.imgur.com/X3rA9nF.png) * 執行容錯回復 步驟同"執行手動容錯移轉" > 如果您剛執行完容錯移轉,必須等候大約一小時,才能要求容錯回復。 ## 設定裝置 > 除了從裝置接收遙測資料,可能也需要從後端服務設定您的裝置。 若要同步處理裝置與 IoT 中樞之間的狀態資訊,可使用裝置對應項(device twins,包含desired properties, reported properties, tags) > desired properties:由後端應用程式所設定,供裝置讀取 > reported properties:由裝置所設定,供後端應用程式讀取 > tags:由後端應用程式所設定,且一律不會傳送至裝置 ![](https://i.imgur.com/qK4LoGa.png) * 下載所需範例程式 https://github.com/Azure-Samples/azure-iot-samples-node/archive/master.zip * 設定 Azure 資源 > Azure CLI ``` hubname=tutorial-iot-hub location=centralus # Install the IoT extension if it's not already installed: az extension add --name azure-cli-iot-ext # Create a resource group: az group create --name tutorial-iot-hub-rg --location $location # Create your free-tier IoT Hub. You can only have one free IoT Hub per subscription: az iot hub create --name $hubname --location $location --resource-group tutorial-iot-hub-rg --sku F1 # Make a note of the service connection string, you need it later: az iot hub show-connection-string --name $hubname -o table # Set the name of your IoT hub: hubname=tutorial-iot-hub # Create the device in the identity registry: az iot hub device-identity create --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg # Retrieve the device connection string, you need this later: az iot hub device-identity show-connection-string --device-id MyTwinDevice --hub-name $hubname --resource-group tutorial-iot-hub-rg -o table ``` * service connection string ``` HostName=tutorial-iot-hub000000000000.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=gKDryI8xfYbNP+z+PanGU5vW/PVukoC5XzG4H+DwX98= ``` * device connection string ``` HostName=tutorial-iot-hub000000000000.azure-devices.net;DeviceId=MyTwinDevice;SharedAccessKey=DlqakhZN6T3YVd7xLIpGZhxbcNst8XnR57FIkCcWE5A= ``` ### 傳送狀態資訊 ``` cd azure-iot-samples-node-master\iot-hub\Tutorials\DeviceTwins ``` > SimulatedDevice.js 程式碼說明 * 使用裝置連接字串連線至 IoT 中樞 ``` // Get the device connection string from a command line argument var connectionString = process.argv[2]; ``` * 從用戶端物件中取得對應項 ``` // Get the device twin client.getTwin(function(err, twin) { if (err) { console.error(chalk.red('Could not get device twin')); } else { console.log(chalk.green('Device twin created')); ``` * 建立所需屬性更新的處理常式(handlers),以回應 JSON 階層中不同層級的更新 (檢視從後端應用程式傳送至裝置的所有所需屬性變更) ``` // Handle all desired property updates twin.on('properties.desired', function(delta) { console.log(chalk.yellow('\nNew desired properties received in patch:')); // Handle changes to the fanOn desired property twin.on('properties.desired.fanOn', function(fanOn) { console.log(chalk.green('\nSetting fan state to ' + fanOn)); // Update the reported property after processing the desired property reportedPropertiesPatch.fanOn = fanOn ? fanOn : '{unknown}'; }); ``` * 本機對應項物件會儲存一組完整的desired properties和reported properties。 從後端傳送的差異可能僅更新了desired properties的子集。 ``` // Handle desired properties updates to the climate component twin.on('properties.desired.components.climate', function(delta) { if (delta.minTemperature || delta.maxTemperature) { console.log(chalk.green('\nUpdating desired tempertures in climate component:')); console.log('Configuring minimum temperature: ' + twin.properties.desired.components.climate.minTemperature); console.log('Configuring maximum temperture: ' + twin.properties.desired.components.climate.maxTemperature); // Update the reported properties and send them to the hub reportedPropertiesPatch.minTemperature = twin.properties.desired.components.climate.minTemperature; reportedPropertiesPatch.maxTemperature = twin.properties.desired.components.climate.maxTemperature; sendReportedProperties(); } }); ``` * 從後端傳送的所需屬性不會指出正在對特定的所需屬性執行何種作業,程式碼必須從目前儲存於本機的所需屬性集,以及從中樞傳送的變更,來推斷作業類型。 > 模擬裝置如何對所需屬性中所列的元件處理插入、更新和刪除作業 ``` // Keep track of all the components the device knows about var componentList = {}; // Use this componentList list and compare it to the delta to infer // if anything was added, deleted, or updated. twin.on('properties.desired.components', function(delta) { if (delta === null) { componentList = {}; } else { Object.keys(delta).forEach(function(key) { if (delta[key] === null && componentList[key]) { // The delta contains a null value, and the // device has a record of this component. // Must be a delete operation. console.log(chalk.green('\nDeleting component ' + key)); delete componentList[key]; } else if (delta[key]) { if (componentList[key]) { // The delta contains a component, and the // device has a record of it. // Must be an update operation. console.log(chalk.green('\nUpdating component ' + key + ':')); console.log(JSON.stringify(delta[key])); // Store the complete object instead of just the delta componentList[key] = twin.properties.desired.components[key]; } else { // The delta contains a component, and the // device has no record of it. // Must be an add operation. console.log(chalk.green('\nAdding component ' + key + ':')); console.log(JSON.stringify(delta[key])); // Store the complete object instead of just the delta componentList[key] = twin.properties.desired.components[key]; } } }); } }); ``` * 將所需屬性變更從後端應用程式傳送至裝置 ``` cd azure-iot-samples-node-master\iot-hub\Tutorials\DeviceTwins ``` > ServiceClient.js 程式碼說明 * 連線至裝置身分識別登錄,並存取特定裝置的對應項 ``` // Create a device identity registry object var registry = Registry.fromConnectionString(connectionString); // Get the device twin and send desired property update patches at intervals. // Print the reported properties after some of the desired property updates. registry.getTwin(deviceId, async (err, twin) => { if (err) { console.error(err.message); } else { console.log('Got device twin'); ``` * 後端應用程式傳送至裝置的所需屬性修補程式(patches) ``` // Turn the fan on var twinPatchFanOn = { properties: { desired: { patchId: "Switch fan on", fanOn: "false", } } }; // Set the maximum temperature for the climate component var twinPatchSetMaxTemperature = { properties: { desired: { patchId: "Set maximum temperature", components: { climate: { maxTemperature: "92" } } } } }; // Add a new component var twinPatchAddWifiComponent = { properties: { desired: { patchId: "Add WiFi component", components: { wifi: { channel: "6", ssid: "my_network" } } } } }; // Update the WiFi component var twinPatchUpdateWifiComponent = { properties: { desired: { patchId: "Update WiFi component", components: { wifi: { channel: "13", ssid: "my_other_network" } } } } }; // Delete the WiFi component var twinPatchDeleteWifiComponent = { properties: { desired: { patchId: "Delete WiFi component", components: { wifi: null } } } }; ``` * 後端應用程式將所需屬性更新傳送至裝置 ``` // Send a desired property update patch async function sendDesiredProperties(twin, patch) { twin.update(patch, (err, twin) => { if (err) { console.error(err.message); } else { console.log(chalk.green(`\nSent ${twin.properties.desired.patchId} patch:`)); console.log(JSON.stringify(patch, null, 2)); } }); } ``` #### 執行應用程式 * 執行模擬裝置應用程式 ``` cd azure-iot-samples-node-master\iot-hub\Tutorials\DeviceTwins npm install node SimulatedDevice.js "{your device connection string}" ``` * 執行後端應用程式 ``` cd azure-iot-samples-node-master\iot-hub\Tutorials\DeviceTwins npm install node ServiceClient.js "{your service connection string}" ``` ![](https://i.imgur.com/so9sv1e.png) ![](https://i.imgur.com/vFQtc6o.png) ### 接收狀態資訊 > 後端應用程式會以報告屬性的形式接收來自裝置的狀態資訊,後端應用程式可從儲存於中樞的裝置對應項讀取報告屬性的目前值。 * 模擬裝置所傳送之修補程式 (更新修補程式中的欄位,再將其傳送至中樞) ``` // Create a patch to send to the hub var reportedPropertiesPatch = { firmwareVersion:'1.2.1', lastPatchReceivedId: '', fanOn:'', minTemperature:'', maxTemperature:'' }; ``` * 將包含報告屬性的修補程式傳送至中樞 ``` // Send the reported properties patch to the hub function sendReportedProperties() { twin.properties.reported.update(reportedPropertiesPatch, function(err) { if (err) throw err; console.log(chalk.blue('\nTwin state reported')); console.log(JSON.stringify(reportedPropertiesPatch, null, 2)); }); } ``` * 後端應用程式會透過裝置對應項,存取裝置目前的報告屬性值 ``` // Display the reported properties from the device function printReportedProperties(twin) { console.log("Last received patch: " + twin.properties.reported.lastPatchReceivedId); console.log("Firmware version: " + twin.properties.reported.firmwareVersion); console.log("Fan status: " + twin.properties.reported.fanOn); console.log("Min temperature set: " + twin.properties.reported.minTemperature); console.log("Max temperature set: " + twin.properties.reported.maxTemperature); } ``` #### 執行應用程式 同上一步驟中的 "執行應用程式" ![](https://i.imgur.com/tHezTjb.png) ![](https://i.imgur.com/IsEDaus.png)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully