当前位置: 首页 > 知识库问答 >
问题:

我们如何将Android/iOS应用连接到Azure IoT Central并发送遥测数据?

锺离伟彦
2023-03-14

我们已经在Azure IOT Central上注册了我们的IOT设备,并且能够在IOT Central上可视化遥测数据。但是,当我们使用Azure在Android示例应用IOT central上生成的主键发送遥测时,我们无法可视化数据。

我错过什么了吗?我们如何将iOS和Android注册为IoT Central上的一个设备以可视化数据?

共有1个答案

岳池暝
2023-03-14

以下屏幕片段显示了使用REST Post请求向IoT Central App发送遥测数据的示例:

单击按钮:

    private async void FabOnClick(object sender, EventArgs eventArgs)
    {
        double tempValue = double.Parse(floatingTemperatureLabel.Text);
        var payload = new { Temperature = tempValue };

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("Authorization", sasToken);
            await client.PostAsync(requestUri, new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json"));
        }

        View view = (View) sender;
        Snackbar.Make(view, $"Sent: {tempValue}", Snackbar.LengthLong).SetAction("Action", (Android.Views.View.IOnClickListener)null).Show();
    }

遥测数据发送至物联中心APP:

注意,上面示例的requestUri和sasToken是在我的单独工具上创建的,但它们可以根据IoTC应用程序的scopeId、deviceId和SAS令牌从自定义azure函数中获得。

更新:

对于运行时注册(包括分配给设备模板),可以使用具有以下有效负载的REST PUT请求

{
  "registrationId":"yourDeviceId",
  "payload": {
     "__iot:interfaces": {
       "CapabilityModelId":"yourCapabilityModelId"
     }
   }
}

请注意,授权头(如sasToken)可以由以下操作生成:

string sasToken = SharedAccessSignatureBuilder.GetSASToken($"{scopeId}/registrations/{deviceId}", deviceKey, "registration");

注册过程的状态可以通过REST GET获得,其中DeviceRegistrationResult为您提供IoT Central应用程序的底层Azure IoT Hub的名称空间,例如assignedHub属性。

正如我上面提到的,所有这些REST API调用都可以由azure函数处理(实现),并且您的移动应用程序将根据您的IoTC应用程序的scopeId、deviceId、deviceTemplateId和sas-token在post响应中获得requestUrl和sasToken。

更新-2:

我正在添加azure函数(HttpTriggerGetConnectionInfo)的示例,以获取设备http协议到IoT Central应用程序的连接信息:

该函数要求将以下变量添加到应用程序设置中:

  • AzureIOTC_ScopeID
  • AzureIOTC_Sastoken

run.csx:

#r "Newtonsoft.Json"

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    int retryCounter = 10;
    int pollingTimeInSeconds = 3;

    string deviceId = req.Query["deviceid"];
    string cmid = req.Query["cmid"];

    if (!Regex.IsMatch(deviceId, @"^[a-z0-9\-]+$"))
        throw new Exception($"Invalid format: DeviceID must be alphanumeric, lowercase, and may contain hyphens");

    string iotcScopeId = System.Environment.GetEnvironmentVariable("AzureIoTC_scopeId"); 
    string iotcSasToken = System.Environment.GetEnvironmentVariable("AzureIoTC_sasToken"); 

    if(string.IsNullOrEmpty(iotcScopeId) || string.IsNullOrEmpty(iotcSasToken))
        throw new ArgumentNullException($"Missing the scopeId and/or sasToken of the IoT Central App");

    string deviceKey = SharedAccessSignatureBuilder.ComputeSignature(iotcSasToken, deviceId);

    string address = $"https://global.azure-devices-provisioning.net/{iotcScopeId}/registrations/{deviceId}/register?api-version=2019-03-31";
    string sas = SharedAccessSignatureBuilder.GetSASToken($"{iotcScopeId}/registrations/{deviceId}", deviceKey, "registration");

    using (HttpClient client = new HttpClient())
    {
        client.DefaultRequestHeaders.Add("Authorization", sas);
        client.DefaultRequestHeaders.Add("accept", "application/json");
        string jsontext = string.IsNullOrEmpty(cmid) ? null : $"{{ \"__iot:interfaces\": {{ \"CapabilityModelId\":\"{cmid}\"}} }}";
        var response = await client.PutAsync(address, new StringContent(JsonConvert.SerializeObject(new { registrationId = deviceId, payload = jsontext }), Encoding.UTF8, "application/json"));

        var atype = new { errorCode = "", message = "", operationId = "", status = "", registrationState = new JObject() };
        do
        {
            dynamic operationStatus = JsonConvert.DeserializeAnonymousType(await response.Content.ReadAsStringAsync(), atype);
            if (!string.IsNullOrEmpty(operationStatus.errorCode))
            {
                throw new Exception($"{operationStatus.errorCode} - {operationStatus.message}");
            }
            response.EnsureSuccessStatusCode();
            if (operationStatus.status == "assigning")
            {
               Task.Delay(TimeSpan.FromSeconds(pollingTimeInSeconds)).Wait();
               address = $"https://global.azure-devices-provisioning.net/{iotcScopeId}/registrations/{deviceId}/operations/{operationStatus.operationId}?api-version=2019-03-31";
               response = await client.GetAsync(address);
            }
            else if (operationStatus.status == "assigned")
            {
               string assignedHub = operationStatus.registrationState.assignedHub;
               string cstr = $"HostName={assignedHub};DeviceId={deviceId};SharedAccessKey={deviceKey}";
               string requestUri = $"https://{assignedHub}/devices/{deviceId}/messages/events?api-version=2020-03-01";
               string deviceSasToken = SharedAccessSignatureBuilder.GetSASToken($"{assignedHub}/{deviceId}", deviceKey);

               log.LogInformation($"IoTC DeviceConnectionString:\n\t{cstr}");
               return new OkObjectResult(JObject.FromObject(new { iotHub = assignedHub, requestUri = requestUri, sasToken = deviceSasToken, deviceConnectionString = cstr }));
            }
            else
            {
                throw new Exception($"{operationStatus.registrationState.status}: {operationStatus.registrationState.errorCode} - {operationStatus.registrationState.errorMessage}");
            }
        } while (--retryCounter > 0);

        throw new Exception("Registration device status retry timeout exprired, try again.");
    } 
}

public sealed class SharedAccessSignatureBuilder
{
    public static string GetHostNameNamespaceFromConnectionString(string connectionString)
    {
        return GetPartsFromConnectionString(connectionString)["HostName"].Split('.').FirstOrDefault();
    }
    public static string GetSASTokenFromConnectionString(string connectionString, uint hours = 24)
    {
        var parts = GetPartsFromConnectionString(connectionString);
        if (parts.ContainsKey("HostName") && parts.ContainsKey("SharedAccessKey"))
            return GetSASToken(parts["HostName"], parts["SharedAccessKey"], parts.Keys.Contains("SharedAccessKeyName") ? parts["SharedAccessKeyName"] : null, hours);
        else
            return string.Empty;
    }
    public static string GetSASToken(string resourceUri, string key, string keyName = null, uint hours = 24)
    {
        try
        {
            var expiry = GetExpiry(hours);
            string stringToSign = System.Web.HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
            var signature = SharedAccessSignatureBuilder.ComputeSignature(key, stringToSign);
            var sasToken = keyName == null ?
                String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}", HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry) :
                String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}", HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
            return sasToken;
        }
        catch
        {
            return string.Empty;
        }
    }

    #region Helpers
    public static string ComputeSignature(string key, string stringToSign)
    {
        using (HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(key)))
        {
            return Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        }
    }

    public static Dictionary<string, string> GetPartsFromConnectionString(string connectionString)
    {
        return connectionString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Split(new[] { '=' }, 2)).ToDictionary(x => x[0].Trim(), x => x[1].Trim(), StringComparer.OrdinalIgnoreCase);
    }

    // default expiring = 24 hours
    private static string GetExpiry(uint hours = 24)
    {
        TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
        return Convert.ToString((ulong)sinceEpoch.TotalSeconds + 3600 * hours);
    }

    public static DateTime GetDateTimeUtcFromExpiry(ulong expiry)
    {
        return (new DateTime(1970, 1, 1)).AddSeconds(expiry);
    }
    public static bool IsValidExpiry(ulong expiry, ulong toleranceInSeconds = 0)
    {
        return GetDateTimeUtcFromExpiry(expiry) - TimeSpan.FromSeconds(toleranceInSeconds) > DateTime.UtcNow;
    }

    public static string CreateSHA256Key(string secret)
    {
        using (var provider = new SHA256CryptoServiceProvider())
        {
            byte[] keyArray = provider.ComputeHash(UTF8Encoding.UTF8.GetBytes(secret));
            provider.Clear();
            return Convert.ToBase64String(keyArray);
        }
    }

    public static string CreateRNGKey(int keySize = 32)
    {
        byte[] keyArray = new byte[keySize];
        using (var provider = new RNGCryptoServiceProvider())
        {
            provider.GetNonZeroBytes(keyArray);
        }
        return Convert.ToBase64String(keyArray);
    }
    #endregion
}

配置mydeviceid并分配给urn:rk2019IOTCPreview:tester_653:1设备模板的函数的用法:

GET: https://yourFncApp.azurewebsites.net/api/HttpTriggerGetConnectionInfo?code=****&deviceid=mydeviceid&cmid=urn:rk2019iotcpreview:Tester_653:1

下面的屏幕片段显示了我们可以从设备模板中获取CapabilityModelId(cmid)的位置。注意,错误的值将返回:500内部服务器错误响应代码。

移动应用程序中收到的响应将允许将遥测数据发送到IoT中央应用程序:

{
  "iotHub": "iotc-xxxxx.azure-devices.net",
  "requestUri": "https://iotc-xxxxx.azure-devices.net/devices/mydeviceid/messages/events?api-version=2020-03-01",
  "sasToken": "SharedAccessSignature sr=iotc-xxxxx.azure-devices.net%2fmydeviceid&sig=xxxxxxxx&se=1592414760",
  "deviceConnectionString": "HostName=iotc-xxxxx.azure-devices.net;DeviceId=mydeviceid;SharedAccessKey=xxxxxxx"
 }

响应对象可以缓存在移动应用程序中,并在其过期之前刷新。请注意,上述sasToken的有效期为24小时(默认值)。

下面的屏幕片段展示了从移动应用程序向Azure IoT Central应用程序发送遥测数据的概念:

 类似资料:
  • > 我想从Android应用程序接收响应和发布数据,以填写注册表格。 我想访问一个从第一页返回< code>session_id的网站。意味着当应用程序启动时,我将访问主页并获取< code>session_id,使用< code>session_id和我的应用程序中的一些字段,我想将数据发布到网站的服务器。 应该用什么来做上面的任务? 我已经使用Jsoup和Parse HTML来获得(不在主线程

  • 我正在尝试上传文件,我正在使用“角度文件上传”。正在尝试连接并将文件发送到mongodb,但它在firebug上出现此错误 网络::错误_不安全_响应 在mongodb.log日志文件中,它显示连接已完成: 2014-11-09T11:57:05.512 0400 I网络[initandlisten]连接已从xxx接受。x、 x.x:53749#2(1个连接现在打开) 2014-11-09T11:

  • 我正在尝试为Lightstep中的Hello world示例设置跟踪安装https://lightstep.com/blog/opentelemetry-nodejs/: 我的tracing.js如下: 我的收集器正在本地主机4317上侦听,配置文件如下: 我正在运行otelcontribcol\u darwin\u amd64版本23。它会将痕迹发送到我的logz。io帐户。它适用于Java和P

  • 本程序是利用Socket编程将陀螺仪和加速度计信息(6位)发送到服务器。 我的问题是如何做套接字连接和从Android Wear发送数据到服务器(使用套接字连接) 以下是完整的程序:: 相关问题:Android wear不启动线程 我也阅读了http://developer.android.com/training/wearables/data-layer/index.html,但仍然在尝试最好的

  • 问题内容: 我已经用create-react- app构建了一个Todo应用。我正在使用的商店基于本地存储(对象窗口的JS属性)。现在,我创建了一个MySQL数据库,并希望连接到该数据库,因此状态将显示数据库中的值,并将通过操作进行更新。 我尝试使用db.js通过“节点”控制台连接到数据库并输出值。有用。 是否可以使用此脚本将应用程序的状态连接到数据库? 问题答案: 您无法直接连接它们。 在网络浏

  • 问题内容: 关于如何将对象从我的android应用程序发送到数据库,我 有点茫然 由于我是新手,所以我不太确定哪里出了问题,我从中提取了数据,我不知道如何将对象发布到我们的服务器。 任何建议将不胜感激 问题答案: 您需要使用一个类与服务器进行通信。像这样: 这是您的方法。 这是您的活动课程中的新课程。 这行:将其作为HTTP POST请求,应在您的服务器上作为POST请求处理。 然后,在服务器上,