如何使用HTTPAPI构建IoT应用程序IoT设备的使用越来越多,从显示天气的闹钟到列出食品价格的冰箱。不管具体细节如何,这些设备都依赖API与数据源进行通信。但是,您究竟如何连接消息、数据和设备?本文展示了如何为IoT设备设计和建模数据的示例。此处使用带有显示屏的小型模块化物联网设备M5Stack并将其连接到纽约市大都会交通管理局(NYCMTA)API,以显示各个地铁站的实时火车到达时间。在关注M5Stack的同时,将讨论的概念将适用于跨各种设备设计IoT应用程序。1.先决条件本文将重点关注有关如何从API请求数据的更大的概念性想法。一些编程知识将非常有用。虽然M5Stack不是必需的,但如果您有的话,您可以继续将完成的项目上传到您自己的设备。考虑到这一点,可以下载VSCodeIDE和M5Stack插件。如果您以前从未启动过M5Stack,请按照他们的指南设置WiFi和必要的固件。对于这个项目,我们将使用Python3,它是M5Stack使用的主要编程语言。为此,需要注册纽约大都会交通管理局(NYCMTA)开发者帐户,以获得免费的开发者API密钥,以访问他们的实时地铁数据。最后,您应该注册一个免费的Gravitee帐户以使用API设计器,这将使您更容易可视化和理解API调用中的数据流。这个项目的原始材料是受这个开源项目的启发,所以如果有帮助,请继续并启动该存储库。2.设计API交互在写一行代码之前,先考虑完成这个项目需要哪些信息:地铁站信息。哪些列车经过这些地铁站。这些列车的最新实时数据。根据文档,API分为静态数据源和实时数据源。静态数据源包含有关站点的信息。有了这些信息,就可以从实时数据馈送API中获取实际的实时地铁数据。纽约大都会交通管理局(MTA)提供的数据采用以下CSV格式:电子表格stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station因为唯一需要的静态信息是车站ID,它可以简单地随机提取一个站点ID并将其用于实时提要。在这种情况下,选择Hoyt-Schermerhorn站是因为它相对复杂:两列独立的火车经过该站(A和C)。车站还通过它们是北行(N)还是南行(S)来标识。SpreadsheetA42,,Hoyt-SchermerhornSts,,40.688484,-73.985001,,,1,A42N,,Hoyt-SchermerhornSts,,40.688484,-73.985001,,,0,A42A42S,,Hoyt-SchermerhornSts,,40.6803498,-7,,0,A42从这些行中,只需要父站ID(A42)来识别经过该站的地铁,包括北行(A42N)和南行(A42S)。实时提要以Google的GTFS格式表示,该格式基于ProtocolBuffers(也称为protobuf)。虽然纽约大都会交通管理局(NYCMTA)没有记录其特定提要的示例,但GTFS记录了。从GTFS文档中,可以确定如何以protobuf格式获取特定车站的最新列车到达时间。这是来自GTFS端点的示例响应,已转换为JSON以便于可视化:JSON{"trip":{"trip_id":"120700_A..N","start_time":"20:07:00","start_date":"20220531","route_id":"A"},"stop_time_update":[{"arrival":{"time":1654042672},"departure":{"time":1654042672},"stop_id":"H06N"},//...更多站点...{"arrival":{"time":1654044957},"departure":{"time":1654044957},"stop_id":"A42N"}]}由于纽约市MTAAPI提供有了很多信息,使用GraviteeAPIDesigner来模拟API返回的内容,映射和可视化数据是非常有用的。以下是API设计者思维导图:API设计者可以帮助识别API的所有资源(端点),以及与资源关联的数据属性。这些属性将包括端点所需的输入及其提供的输出。在此映射中,有一个路径为/gtfs/的资源。可以根据需要附加更多属性,并且可以使用数据类型对每个属性进行注释。通过查看此地图,可以绘制一条从端点到右下角标识的到达和离开时间的直接路径。因此,为了表达需要的数据,需要:标识要获取地铁信息的车站ID。向NYCMTA的GTFS源发出感兴趣的地铁线路的HTTP请求。迭代结果,将响应数组中的stop_id与stop_id进行比较。然后可以根据特定车站和地铁的时间信息采取行动。这代表了一些活动部件,但它不应该是无法处理的东西。3.编码在M5Stack上运行任何程序之前,首先要确保代码在本地运行。这里将安装一些Python包,使项目更容易构建。Shellpip3install--upgradegtfs-realtime-bindingspip3installprotobuf3_to_dictpip3installrequests前两个包将协议缓冲区转换为Python字典(或哈希),这使得数据模型更易于使用。最后一个包可以更轻松地从Python发出HTTP请求。以下将通过导入Python包启动程序:Pythonfromgoogle.transitimportgtfs_realtime_pb2importrequestsimporttime接下来,将向NYCMTAGTFS提要发出HTTP请求:Pythonapi_key="YOUR_API_KEY"headers={'x-api-key':api_key}feed=gtfs_realtime_pb2.FeedMessage()response=requests.get('https://api-endpoint.mta.info/Dataservice/mtagtfsfeeds/nyct%2Fgtfs-ace',headers=headers)feed.ParseFromString(response.内容)呈现到目前为止一切顺利。这里使用的GTFS端点是A/C/E列车的端点,可以通过URL上的-ace后缀来识别。(除了这个演示,火车E在这里不感兴趣。)下面将GTFSprotocolbuffer响应转换为字典:可见结构是什么样的。如果这是一个真实的项目,这样的分析可能有助于确定字典中的哪些键和值需要迭代。Pythondefstation_time_lookup(train_data,station):对于train_data中的火车:iftrains.__contains__('trip_update'):unique_train_schedule=trains['trip_update']对于unique_arrival_times中的scheduled_arrivals:stop_id=scheduled_arrivals.get('stop_id',False)ifstop_id==f'{station}N':time_data=scheduled_arrivals['arrival']unique_time=time_data['time']ifunique_time!=None:northbound_times.append(unique_time)elifstop_id==f'{station}S':time_data=scheduled_arrivals['arrival']unique_time=time_data['time']ifunique_time!=None:southbound_times.append(unique_time)#保留一个globallisttocollectvarioustraintimesnorthbound_times=[]southbound_times=[]#为Hoyt-Schermerhornstation_time_lookup(realtime_data,'A42')运行上面的站ID函数并没有那么复杂:为此遍历A/C线的地铁信息数group对于每个数组条目,验证它是否具有它需要的所有键的值。这是防御性编码,因为您不能100%确定此第三方服务在需要时会提供所需的内容。之后遍历所有站台信息,当需要的南北行列车父ID(A42)落地时停止。最后,在两个单独的全局变量中保留即将到来的火车到达时间的列表。接下来,展示这些信息:Python#按时间顺序对收集到的时间进行排序northbound_times.sort()southbound_times.sort()#从列表中弹出最早和第二早到达时间nearest_northbound_arrival_time=northbound_times[0]second_northbound_arrival_time=northbound_times[1]nearest_arrival_souttimehsouthbound_times[0]second_southbound_arrival_time=southbound_times[1]###M5STACK的UI应该在这里###defprint_train_arrivals(direction,time_until_train,nearest_arrival_time,second_arrival_time):iftime_until_train<=0:next_arrival_time=second_arrival_timeelsenearest_arrival_time:next_arrival_time_s=timestrftime("%I:%M%p",time.localtime(next_arrival_time))print(f"下一班{direction}火车将在{next_arrival_time_s}"到达)#抓取当前时间,以便您可以找出分钟数到达current_time=int(time.time())time_until_northbound_train=int(((nearest_northbound_arrival_time-current_time)/60))time_until_southbound_train=int(((nearest_southbound_arrival_time-current_time)/60))current_time_s=time.strftime("%I:%M%p")print(f"目前是{current_time_s}")print_train_arrivals("northbound",time_until_northbound_train,nearest_northbound_arrival_time,second_northbound_arrival_time)print_train_arrivals("southbound",time_until_southbound_train,nearest_southbound_arrival_time,time_until_southbound_arrival_time)北行到站和南行列车时间排序的关键步骤做的大部分工作就是格式化数据。乘坐前两次(“最快”到达的地铁)。将这些时间与当前时间进行比较,以得出地铁行驶的分钟数。将这些地铁到达时间传递给print_train_arrivals。如果下一班火车不到一分钟,将显示第二个到达时间。恐怕我赶不上那个地铁了!否则,将显示最近的到达时间。如果您在终端上运行此脚本,您应该会看到类似于以下内容的消息:ShellIt'scurrently05:59PMThenextnorthboundtrainwillarriveat06:00PMThenextsouthboundtrainwillarriveat06:02PM4.部署到M5Stack现在在本地测试Python代码可以与NYCMTAAPI通信,是时候让这段代码在M5Stack上运行了。对M5Stack进行编程的最简单方法是通过免费的UIFlowIDE,它只是一个通过WiFi与设备通信的网页。您可以通过他们的文档了解更多关于如何配置您的设备以进行WiFi访问的信息。虽然M5Stack可以通过所见即所得的UI元素进行编程,但它也可以接受(并运行)Python代码。然而,WYSIWYG元素的主要优势在于它可以更轻松地可视化屏幕上绘制的文本:在此GIF中,在示例M5Stack屏幕上创建了一个标签,其默认字符串为“Text”。切换到Python时,看到标签是一个名为M5TextBox的对象的实例化。拖动标签时,Python中的3DX和Y坐标(构造函数中的前两个参数)发生变化。这样可以很容易地看到程序的显示方式。也可以通过单击标签本身来更改Python代码中使用的变量(以及其他属性):大多数情况下,编写Python脚本仅需稍作修改即可在M5Stack上使用。Python代码可以从您的本地计算机复制并粘贴到UIFlowIDE的Python选项卡中。在代码中,找到###UIFORM5STACKSHOULDHERE###注解并将其下方的所有内容替换为以下代码:Pythontime_label=M5TextBox(146,27,"",lcd.FONT_Default,0xFFFFFF,rotate=0)northbound_label=M5TextBox(146,95,"",lcd.FONT_Default,0xFFFFFF,rotate=0)southbound_label=M5TextBox(146,163,"",lcd.FONT_Default,0xFFFFFF,rotate=0)defprint_train_arrivals(方向,标签,time_until_train,nearest_arrival_time,second_arrival_time):如果time_until_train<=0:next_arrival_time=second_arrival_timeelsenearest_arrival_time:next_arrival_time_s=time.strftime("%I:%M%p",time.localtime(next_arrival_time))label.setText(f"The下一个{direction}火车将在{next_arrival_time_s}")whileTrue:#抓取当前时间,以便您可以找出到达分钟)/60))time_until_southbound_train=int(((nearest_southbound_arrival_time-current_time)/60))current_time_s=time.strftime("%I:%M%p")time_label.setText(f"目前是{current_time_s}")print_train_arrivals("北行",northbound_label,time_until_northbound_train,nearest_northbound_arrival_time,second_northbound_arrival_time)print_train_arrivals("southbound",southbound_label,time_until_southbound_train,nearest_southbound_arrival_time,time_until_southbound_train)sleep5大部分应该看起来很熟悉,有两个主要的修改使这个代码在M5Stack上工作首先,创建标签:时间标签(time_label)北行标签(northbound_label)southboundlabel(southbound_label)其次,将所有内容放入while循环中,该循环将获取当前时间并设置标签文本。循环将休眠五秒钟,然后重新启动该过程。就是这样!当您点击运行时,您应该会看到地铁字符串每五秒更新一次,并包含最新的路线数据。V.结论物联网设备经常被爱好者使用,但如果继续这个项目,有几个现实世界的考虑因素。一个考虑因素是速率限制,确保以有效的方式从MTAAPI请求数据。另一个考虑因素是连通性。如果设备暂时无法访问WiFi,它将如何重新建立连接以获取所需的信息?一旦您开始考虑这些生产级别的问题,或者如果您想跨多个设备扩展您的项目,您还需要考虑API管理。上面提到了GraviteeDesigner,在设计阶段非常有用。Gravitee还有其他API管理工具,如API网关、监控和实时分析、部署等。对于习惯于为传统服务器和Web浏览器编写代码的开发人员来说,物联网应用程序开发似乎令人望而生畏。然而,物联网设备的功能改进其实很小。当今的设备内置了对流行语言和框架的支持,使物联网成为构建或集成API和应用程序的一种有趣且创新的方式。原文链接:https://dzone.com/articles/building-an-iot-application-using-an-http-api-1
