OpenHarmony上利用Paho MQTT连接云平台
扫描二维码
随时随地手机看文章
1. 引言
近年来,物联网(Internet of Things, IoT)技术的快速发展改变了各行各业,让设备之间实现了无缝连接和智能互动。从传感器到智能家电,物联网设备需要与云服务进行高效通信,以传输数据、接收指令并实现远程管理。这一转变促使了专门针对物联网部署需求开发的专业物联网平台和通信协议,如CoAP (Constrained Application Protocol)、AMQP (Advanced Message Queuing Protocol)、DDS (Data Distribution Service)、WebSocket等。除上述协议外,MQTT (Message Queuing Telemetry Transport)因其适应资源受限设备、简单易用的设计、强大的消息传输能力和广泛的应用支持而成为物联网通信的常用选择。它不仅满足了各种物联网设备和应用程序之间实时通信的需求,还提供了灵活的部署和扩展选项,适应了不同规模和复杂度的物联网解决方案[1]。
Paho MQTT是一个开源的MQTT库,由Eclipse Paho项目提供支持。它提供了多种编程语言的实现,包括C、C++、Java、Python等,旨在帮助开发者轻松地在各种设备和平台上实现MQTT通信协议。本文专注于将Paho MQTT客户端库移植到OpenHarmony项目中的LiteOS内核处理器上,如海思Hi3861芯片,实现与云平台的连接。在OpenHarmony项目的LiteOS内核处理器上集成MQTT不仅增强了设备与云端高效通信的能力,还为创建互联物联网生态系统的整体目标作出了贡献。
2. MQTT协议
MQTT协议是一种轻量级的、基于发布/订阅模式的消息传输协议,最早由IBM开发于1999年,用于传感器和施工机器之间的通信。随后,协议被开放,成为OASIS (Organization for the Advancement of Structured Information Standards,结构化信息标准推动组织)标准,并在众多物联网应用中得到广泛应用。MQTT协议在物联网领域起初设计用于传感器网络和设备间的低带宽、高效率通信。其轻量级和简单的发布/订阅模式使得设备能够节省能源和带宽,适用于智能家居、工业自动化和智能城市等多种场景。
2.1. MQTT的架构
MQTT的客户端是消息发布者或者消息订阅者,它们与MQTT Broker进行通信来实现数据的传输和接收[2]。客户端可以是各种设备或应用程序,包括传感器、嵌入式设备、移动应用等。MQTT的客户端和Broker之间的关系如图1所示。
Figure 1. MQTT network architecture
客户端在MQTT中的主要功能包括:
发布消息:客户端可以向Broker发布(或发送)消息,消息可以是任何格式的数据,通常包含有用的传感器数据、控制命令等。
订阅主题:客户端可以向Broker订阅(或接收)特定的主题(Topic),通过主题来过滤和接收感兴趣的消息。主题可以使用通配符来实现更灵活的订阅规则。
接收消息:客户端通过订阅主题,可以接收来自Broker转发的消息,并进行相应的处理和分析。
客户端可以选择不同的消息服务质量(QoS)级别来控制消息传递的可靠性和效率,包括至多传递一次(QoS 0)、至少传递一次(QoS 1)、恰好传递一次(QoS 2)。
MQTT的Broker是中介服务器,负责管理客户端之间的消息传递。Broker接收来自发布者(发布消息的客户端)的消息,并确保将这些消息按照订阅者(订阅消息的客户端)的需求正确分发。Broker在MQTT协议中的关键作用包括:
消息路由和分发:根据订阅关系,将发布者发送的消息分发给所有订阅了相关主题的客户端。
连接管理:管理客户端的连接和状态,确保每个连接的可靠性和安全性。
QoS管理:根据客户端设置的QoS级别,确保消息的按时传递和确认。
2.2. MQTT的报文
为了让客户端和Broker之间进行通信,MQTT协议定义了不同类型的消息(称为报文),如CONNECT、CONNACK、PUBLISH、PUBACK、SUBSCRIBE、SUBACK、UNSUBSCRIBE等。报文包含固定报头、可变报头(部分报文包含)、负载(部分报文包含)。固定报头定义了报文类型、控制标志和后续数据长度,可变头部则依据报文类型规定了各自的详细信息,如后续消息内容、QoS级别等。负载用于携带实际数据内容。这种设计使得MQTT能够在各种网络条件下高效传输消息,并支持灵活的通信需求和服务质量保证。
3. Paho MQTT库
3.1. 简介
Paho项目致力于提供开源的、可扩展的消息传递协议实现,支持M2M (Machine-to-Machine)和物联网的各种应用。它特别关注设备连接中的物理限制和成本问题。Paho MQTT C是Paho项目的一部分,是专门为C语言开发的MQTT客户端库。Paho MQTT C库旨在提供一个可靠、高效的实现,使开发者能够轻松地在C语言环境中实现MQTT的发布和订阅功能[3]。
3.2. 库文件内容
应用于嵌入式的Paho MQTT C库文件中,能够被移植的代码位于MQTTPacket、MQTTClient-C、MQTTClient文件夹。
MQTTPacket文件夹包含了MQTT协议报文的解析和封装功能。这些文件提供了处理MQTT协议中不同报文(如CONNECT、PUBLISH、SUBSCRIBE等)的代码实现。它们负责将MQTT消息编码为字节流(序列化),或者将接收到的字节流解析为可操作的消息(反序列化)。
MQTTClient-C文件夹包含了MQTT客户端的C语言实现的核心部分。这些文件实现了MQTT客户端的连接、发布、订阅、断开连接等功能。它们是构建在MQTTPacket基础之上的更高级别的抽象,提供了一个易于使用和集成的MQTT客户端接口。在移植过程中,我们需要对这个文件夹中的一些网络接口进行修改,才能利用其中的函数正常地与服务器通信。
MQTTClient是一个最初为mbed编写的C++库,现已移植到其他平台上使用。该库基于并且需要MQTTPacket。对于仅使用C语言的系统来说,可以忽略此文件夹。
表1说明了MQTTPacket文件夹中与客户端有关的文件的作用。
Table 1. The role of files in the MQTTPacket folder
由于这些文件只负责处理报文格式,不涉及与单片机有关的通信接口,因此在移植过程中直接复制到工程中即可,不需要修改。
MQTTClient-C文件夹中使用的主要是MQTTClient.c文件。该文件是MQTT客户端库的核心实现,负责实现MQTT协议的各种功能,提供了连接Broker、发布和订阅消息等高级抽象接口。它通过调用MQTTPacket文件夹中的程序来封装报文,并通过调用相关的网络接口将这些报文发送出去。在该文件中,初始化函数MQTTClientInit()需要调用Network结构体来传入有关Socket网络接口。该结构体涉及到Socket接口编号、接收函数、发送函数。因此需要编写程序来初始化Socket接口,并编写接收和发送函数,以供MQTTClient.c文件中的函数调用。还需要编写供超时判断用的计时函数。
官方的MQTTClient-C文件夹中已经打包好了一些例程,它们适配于FreeRTOS、Linux、CC3200。移植时需要依据这些例程编写一个程序文件(此处名称为Hi3861_PahoMQTT.c),为MQTTClient.c文件中的函数配置好网络接口和计时函数。网络接口利用Socket通过TCP的方式连接到服务器并进行通信,计时函数用于通信超时判断。要编写的文件中的程序内容如表2所示。
图2通过发布消息的流程来说明在Hi3861_PahoMQTT.c文件中增加的程序的作用。
Table 2. Programs that need to be written during migration
|
Figure 2. The process of publishing messages using the paho.mqtt.c library
从图2中可以看到,为了移植嵌入式的Paho MQTT C库而新增的几个函数的主要作用是初始化Socket接口、设置利用Socket进行接收和发送数据的函数,然后利用Socket连接服务器。配置完成后,设备后续与MQTT Broker进行连接认证、发布消息都依靠的是MQTTClient.c文件中的相关函数。这些函数通过调用MQTTPacket文件夹中的函数来进行报文的封装或解析,利用与Socket相关的函数进行数据的接收和发送。
4. 在Hi3861上移植Paho MQTT
Hi3861是海思推出的一款支持WiFi功能的处理器,其内核采用OpenHarmony架构下的LiteOS操作系统[4]。在官方提供的工程文件中,已经实现了在LiteOS系统下的WiFi连接接口和用于实现TCP/IP协议栈的LwIP (Light weight IP)库。通过调用这些接口,Hi3861可以连接到WiFi热点上面,获取到IP地址,并创建Socket套接字[5]。移植前需要将Paho MQTT C库相关文件复制到工程中,并设置好BUILD.gn项目构建文件。
在LiteOS操作系统中,setsockopt()函数用于配置套接字的选项,如协议级别、接收发送的超时时间、保活机制等,recv()用于从Socket接收数据,send()用于向Socket发送数据。因此,在第3.3节提到的NetworkRead()函数中需要先通过setsockopt()设置接收超时时间,然后调用recv()函数进行数据的接收;在NetworkWrite()函数中需要先通过setsockopt()设置发送超时时间,然后调用send()函数进行数据的发送。LiteOS中的socket()函数用来创建一个新的套接字(Socket),connect()函数用于在客户端套接字上建立与远程服务器的连接。因此这两个函数需要在NetworkConnect()函数中对这两个函数进行调用,从而连接到云平台的服务器上。
另外还需要移植用于超时判断的定时器相关函数,方法主要是通过内核Tick计数获取函数osKernelGetTickCount()和系统定时器计数获取函数osKernelGetSysTimerCount(),配合计数频率来计算秒数和微秒数,这涉及到对MQTTClient.c中的TimerIsExpired()、TimerCountdownMS()、TimerCountdown()、TimerLeftMS()这几个函数的重定义。
配置好MQTTClient.c中的函数接口后,即可调用MQTTClient.c中的相关函数进行MQTT的认证、订阅、取消订阅、发布、接收相关操作了。
5. 实验部署
本文采用华清远见研发的FS-Hi3861开发板进行Hi3861工程的部署,使用华为云平台的设备接入IoTDA资源作为MQTT服务器。
首先需要在华为云的设备接入IoTDA资源中创建产品及相关属性、命令。属性为设备上报的数据,命令为向设备下发的数据,这两种数据的话题和格式是不一样的。然后注册一个设备,获取到设备的鉴权信息,包括客户端ID、用户名、密码。
设备需要利用MQTTClient.c中的MQTTConnect()函数包装好鉴权信息,与服务器进行认证。认证通过后,在华为云中会显示设备处于在线状态。然后,设备可以利用MQTTPublish()函数按照华为云规定的格式向指定的话题发布属性数据。设备还可以利用MQTTSubscribe()函数订阅指定的话题,并通过MQTTRun()函数接收云平台下发的数据。
6. 未来工作
本文以Hi3861为例,介绍了在OpenHarmony项目的LiteOS内核上面通过移植Paho MQTT连接MQTT云平台的方法。由于云平台通常使用JSON格式进行数据传递,因此在未来的项目中,将通过移植cJSON库的方式进行数据的序列化和反序列化,从而更加方便地传输传感器数据并解析相关指令。另外,为了便于传感器网络的建立,未来的项目将探讨利用Hi3861进行Mesh组网,从而扩展传感范围的方式。为了适应低功耗需求,未来的项目也将探讨低功耗WiFi模式下MQTT的应用。