在客户放弃概念证明( PoC )项目后,我在我桌子下面的盒子。
在过去几年中,我构建了一系列Windows 10 IoT Core现场网关应用程序,用于将LoRa设备连接到Azure IoT 中心、Azure IoT Central和Adafruit.IO 。这些运行良好,但现在我需要一个基于Microsoft Azure 云的解决方案,将连接到物联网 (TTN)的LoRaWAN设备连接到在Microsoft Azure中运行的应用程序。
我一直在为 .NET nanoFramework和GHI Electronics TinyCLR设备开发库的另一个项目,以启用与RAK811 LPWAN 模块( PoC 的Wisduino外形)的LoRaWAN连接。
在新西兰有两个全国性的网络(Spark IoT 、KotahiNet ),但我一直想探索 TTN 和The Things Industries的功能,它们看起来有足够的诊断功能来满足我的目的。
我假设如果您正在阅读这个项目故事,那么您熟悉为 Microsoft Azure 开发应用程序,尤其是 IoT 服务。TTN 应用程序和设备的配置在其他几个Hackster.IO项目中已经详细介绍过,这里不再赘述。
这个项目是我博客上一系列帖子的总结,我在其中更详细地介绍了解决方案的构建。
我最初连接了RAK WisGate 开发者网关并配置了 RAK7200 Track Lite 设备。在 TTN 应用程序设备数据选项卡中,我可以看到从设备接收到上行链路消息,并且大部分有效负载都被解码,这是一个好的开始。
我配置了我的 Arduino IDE,以便我可以访问 Seeeduino、LoRaWAN 示例,然后编译并将它们下载到我的设备。我使用本地网关测试了个性化激活 (ABP) 和空中激活 (OTAA)示例的修改版本,以确认我的设备配置良好。在我记得我需要打开我的Seeeduino Grove I2C 温度和湿度传感器连接器的电源之后,设备代码第二次工作了。
我为我的一个 TTN 应用程序配置了TTN HTTP集成,因此它将上行链路消息发布到带有 HTTP 触发端点的 Azure 函数。
我使用JSON2Csharp和从 TTN 网站下载的示例上行链路有效负载来生成一些C#类的初始版本,以对上行链路消息进行反序列化。
由于 JSON2CSharp 无法确定数字字段是整数还是无符号长整数,因此生成的代码存在一些问题。
TTN 文档指出,在成功解码上行链路消息时填充了 payload_fields 属性。TTN 有一个内置的Cayenne 低功耗有效负载 (LPP)消息解码器,RAK7200 Wisnode 轨道灯部分支持该解码器(还提供具有增强功能的定制解码器/编码器)。
我在我的Seeeduino LoRaWAN 设备上使用了第 3 方库(来自 Electronic Cats 的 CayenneLPP 库)来对包含温度和湿度信息的有效载荷进行编码。
解压payload_fields 属性让我有些痛苦。我尝试了许多不同的方法,但都失败了。经过大量实验后,我发现使用C# 对象是最简单的方法(尽管该字段的后处理更为复杂)。
public class PayloadV4
{
public string app_id { get; set; }
public string dev_id { get; set; }
public string hardware_serial { get; set; }
public int port { get; set; }
public int counter { get; set; }
public bool is_retry { get; set; }
public string payload_raw { get; set; }
//public JsonObject payload_fields { get; set; }
//public JObject payload_fields { get; set; }
//public JToken payload_fields { get; set; }
//public JContainer payload_fields { get; set; }
//public dynamic payload_fields { get; set; }
public Object payload_fields { get; set; }
public MetadataV4 metadata { get; set; }
public string downlink_url { get; set; }
}
我还必须在我的 PoC 应用程序中添加一些代码来解压缩具有嵌套字段的 RAK Wisnode 7200 Tracker 加速度计、陀螺仪和位置值。
然后,我使用Microsoft.Azure.Devices.Client库连接到Azure IoT Hub或Azure IoT Central (使用 DPS-KeyGen 生成连接字符串)并上传了我可以在Azure IoT explorer中看到的遥测消息。
对于我的HTTP 集成,我需要可靠地将上行链路消息转发到Azure IoT 中心或Azure IoT Central ,因此我使用Azure 存储队列在我的Azure Function HTTPTrigger端点和消息处理器之间提供弹性缓冲区。
我的解决方案需要强大且不会丢失任何消息,即使系统的某些部分由于故障或入站流量突然激增而无法使用。
用于接收物联网 (TTN) HTTP 集成 JSON 消息的代码使用了 Azure 函数 HTTPTrigger。(使用APIKey 保护),然后将它们放入Azure 存储队列以进行处理。
这段代码故意保持尽可能小和尽可能简单,这样就不会出错。经过一些实验,只用了不到两打 C# 行就创建了一个安全端点来接收上行链路消息并将它们放入Azure 存储队列。
通过存储来自 TTN 的原始上行链路事件 JSON,应用程序可以在无法反序列化时恢复(消息格式已更改或生成类问题)当队列处理器无法处理上行链路事件消息时(引发异常)重试几次后,它将最终进入有毒消息队列(以防故障是暂时的)。
上行链路消息队列处理器使用Azure 函数队列触发器从队列中提取消息,并在需要时预配设备,检索 Azure IoT 中心/Azure IoT 中央连接字符串或使用缓存的 DeviceClient。
对于开发和测试而言,能够预配单个设备确实很有用,尽管对于Azure IoT Central来说这并不容易(尤其是在弃用 DPS-KeyGen 的情况下)。通过Azure IoT Hub设备连接字符串在门户中可用,这很方便,但可扩展性不是很好。
Azure IoT 中心与 Azure IoT Central集成,并且Azure IoT Central强制使用设备预配服务 (DPS)。DPS 旨在支持管理 1000 台设备,这些设备需要一些自定义应用程序来进行压力和浸泡测试。
我的物联网HTTP 集成(TTN)旨在支持许多设备并与Azure IoT Central集成。DPS 支持使用可信平台模块 (TPM)进行设备认证,但这种方法不适用于我的应用程序。我的 TTN 应用程序集成使用具有对称密钥证明的组注册
尽管可以在Azure IoT 中心预配单个设备,但Azure 设备预配服务 (DPS)是首选方法。
scopeID 和主要/次要注册密钥在Azure Key Vault中配置,Azure QueueTrigger 函数可以安全地访问它们。
对于更复杂的部署,可以使用基于 applicationID(或 applicationID + 端口号)的不同 GroupEnrollment 密钥来配置物联网 (TTN)设备。这方面的一个例子是卡车上的跟踪设备报告位置数据,其中一个端口号和另一个端口号的货物温度和湿度,因此可以将遥测事件路由到正确的应用程序。
然后,在处理设备的第一条上行链路消息时,它会在 DPS 和 Azure IoT Hub 中“自动”创建。
物联网 (TTN) HTTP 集成上行链路消息必须进行配置,然后进行后处理,以便 Azure IoT Central 显示它们。
第一步是从 Administration\Device 连接复制 IDScope 和一个主/辅助密钥,并将它们存储在Azure Key Vault中。
对于更复杂的部署,可以根据启动配置的第一个上行链路消息中的 applicationid(或 applicationID + 端口号)使用不同的GroupEnrollment密钥来配置物联网 (TTN)设备。
在处理来自 TTN 设备的第一条上行链路消息后不久,它将在“未关联设备”选项卡中列出。
然后可以将设备与Azure IoT Central Device Template关联。
设备模板提供上行消息有效载荷字段到设备属性的映射。在此示例中,有效载荷字段已由TTN 应用集成 Cayenne 低功耗协议 (LPP)解码器生成。许多LoRaWAN设备使用 LPP 来最小化网络负载的大小。
一旦设备与模板相关联,就可以配置用户友好的设备名称等。
Azure IoT Central 具有映射功能,可用于显示设备的位置。
TTN LPP 解码器生成的位置有效负载的格式与 Azure IoT Central 所需的格式不同。我添加了临时代码(“一种具有成本效益的加速部署修改”又名黑客)来格式化 TelemetryEvent 有效负载,以便显示它。
if (token.First is JValue)
{
// Temporary dirty hack for Azure IoT Central compatibility
if (token.Parent is JObject possibleGpsProperty)
{
if (possibleGpsProperty.Path.StartsWith("GPS", StringComparison.OrdinalIgnoreCase))
{
if (string.Compare(property.Name, "Latitude", true) == 0)
{
jobject.Add("lat", property.Value);
}
if (string.Compare(property.Name, "Longitude", true) == 0)
{
jobject.Add("lon", property.Value);
}
if (string.Compare(property.Name, "Altitude", true) == 0)
{
jobject.Add("alt", property.Value);
}
}
}
jobject.Add(property.Name, property.Value);
}
在配置设备模板、将一些设备与模板关联并修改每个设备的属性后,我可以创建一个仪表板来查看我的Seeeduino LoRaWAN 设备返回的温度和湿度信息。
应用程序集成配置包含敏感信息,例如设备预配服务 (DPS) 组注册对称密钥和Azure IoT 中心连接字符串。
Azure Key Vault旨在保护连接字符串等敏感信息,因此我在资源组中添加了一个。
我编写了一个包装器,它根据上行链路消息有效负载中的物联网 (TTN)应用程序标识符和端口信息解析配置设置。resolve 方法首先查找 applicationId 和 port 的配置(由 – 分隔),然后是 applicationId,最后返回到默认值。
此功能用于 AzureIoTHub 连接字符串、DPS IDScopes、DPS 注册组对称密钥,也用于格式化缓存密钥。
Azure 函数配置设置(如 Azure 存储连接字符串)的值被替换为对 Azure Key Vault 中机密的引用。
在 Azure Key Vault“访问策略”中,我配置了“应用程序访问策略”,因此我的 Azure TTNAzureIoTHubMessageV2Processor 函数标识可以检索机密。
在我最初的实现中,我使用ConcurrentDictionary来存储Azure IoT Hub连接,以减少对设备预配服务 (DPS) 的调用次数。经过一些测试后,我将其替换为 a. Net ObjectCache位于System.Runtime.Caching命名空间中。
我使用缓存来存储Azure IoT Hub连接以减少对设备预配服务 (DPS)的调用次数,但连接数仍然太高。
因此,经过一番研究,我决定启用高级消息队列协议(AMQP)连接池。
return DeviceClient.Create(result.AssignedHub,
authentication,
new ITransportSettings[]
{
new AmqpTransportSettings(TransportType.Amqp_Tcp_Only)
{
PrefetchCount = 0,
AmqpConnectionPoolSettings = new AmqpConnectionPoolSettings()
{
Pooling = true,
}
}
}
);
在此之后,连接数显着减少
在对上行链路消息的处理进行扩展和浸泡测试后,我意识到我所做的一些设计和实现选择意味着处理下行链路消息并不容易。
如果您的应用程序只需要接收来自 LoRaWAN 设备的消息,此解决方案将是理想的。
为了支持下行消息,我很可能必须转换到MQTT 数据 API并删除一些高级配置选项。
这将需要一段时间,所以如果您有兴趣,请关注我的博客,我将在其中发布我的进展。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !