网络协议软件手册
1.HTTP/HTTPS
1.1. HTTP/HTTPS 概述
HTTP(Hyper Text Transfer Protocol,超文本传输协议)是互联网上使用最广泛的网络协议。它是客户端与服务器之间请求和响应的标准,也是用于将超文本从 WWW 服务器传输到本地的通信协议。
另一方面,HTTPS(Hyper Text Transfer Protocol over Secure Socket Layer)是以安全为目的的 HTTP 通道,它在传统 HTTP 的基础上对通信进行加密和身份认证(增加 SSL/TLS 安全协议),从而保证通信过程中的安全性。
1.1.1 HTTP 与 HTTPS 的区别
HTTP 与 HTTPS 的区别如下表所示。
| 协议 | HTTP | HTTPS |
|---|---|---|
| CA 证书 | 不需要 | 需要 |
| 通信加密方式 | 明文通信,不加密 | SSL/TLS 加密通信 |
| 连接方式 | 使用端口号 80 | 使用端口号 443 |
| 连接状态 | 无状态 | 基于 SSL+HTTP 协议构建,可进行加密通信和身份认证的网络协议 |
1.1.2 请求与响应
典型的 HTTP/HTTPS 通信发生在浏览器与 Web 服务器之间。浏览器作为“客户端”,负责发送请求、接收消息以及渲染显示;Web 服务器作为“服务器”,负责响应请求、管理服务器文件以及处理具体业务。在特定情况下,HTTP 也可以作为信息交换的通信规则。
客户端在需要资源时发送的内容称为“请求消息(Request)”,服务器接收到请求后返回给客户端的内容称为“响应消息(Response)”。HTTP 规定了这些请求消息和响应消息的格式。

请求消息格式
请求消息的格式如下。
| 消息元素 | 说明 |
|---|---|
| 请求行 (Request Line) | 请求方法:常见方法如下。 ・GET:用于查询和获取服务器资源。请求内容会显示在 URL 上。 ・POST:用于在服务器上创建或更新内容。请求内容不会显示在 URL 上,而是包含在消息体中。 URL:服务器的虚拟文件路径。详细内容将在下一节说明。 |
响应消息的主要组成部分如下。
-
状态行:包含协议版本、状态码和状态短语。
-
响应头:以键值对形式存在(例如日期时间、数据类型、数据长度等)。
-
空行:用于区分头部和正文的空行。
-
响应正文:如果是网页,通常为 HTML 代码,也可能是音频、视频文件或图片。通常由头部中的 Content-Type 指定。

响应消息格式
1.1.3 URL
假设构建了一个环境监测服务器系统,用于采集并记录市内各观测点的环境数据。假设公共网络上的域名为 www.huanjin.com,服务器内有两台设备,每台设备都保存了不同日期的气温数据。当客户端调用某台设备的某条特定数据时,需要向服务器发送一个唯一路径以便服务器检索。这个唯一路径就是 URL。

环境监测服务器的 URL 结构示例
URL(Uniform Resource Locator,统一资源定位符)位于请求消息的请求行中,起到路标作用。浏览网页并点击各种组件时,浏览器会自动补全不同的 URL。完整 URL 由协议部分、URL(域名)部分和文件地址组成。

URL 组成元素示例
具体内容如下。
| 组成部分 | 说明 |
|---|---|
| 协议 | 由 // 分隔。常见协议有 http 和 https。 |
| URL(域名) | 公共网络上的唯一域名。 |
| 文件地址 | 域名后第一个 / 到最后一个 / 之前的内容称为“虚拟目录”。 最后一个 / 之后到 # 之前的内容为“文件名”。 |
例如,如果想获取环境监测服务器中“设备 1”的“12 月 25 日”数据,则使用 GET 请求,URL 类似于 http://www.huanjin.com/Device-1/日付データ/20211225。
URL 中也可以包含变量信息(URL 查询字符串)。格式如下。
https://www.Adblock.com/s?a=123&b=234变量位于 URL 末尾,以 ? 开头,并以 参数名=值 的键值对形式保存。多个参数之间用 & 分隔。
1.1.4 HTTPS 使用要点
根据上述说明,使用 HTTPS 时需要始终注意以下几点。
-
CA 证书:HTTPS 协议使用 SSL/TLS。
-
数据发送:使用 POST 请求,服务器地址以 URL 形式表示。
-
数据/文件接收:使用 GET 请求,服务器地址以 URL 形式表示。
我们的 easyeai-api 软件开源库统一封装了复杂的消息格式和收发操作,使用户可以通过简单便捷的调用方式实现 HTTP/HTTPS 通信功能。
1.2. 快速开始
1.2.1 开发环境准备
如果是第一次阅读本文档,请先阅读《入门指南/开发环境准备/Easy-Eai 编译环境的准备与更新》,并按照其中步骤搭建编译环境。
在 PC 上的 Ubuntu 系统中运行 run 脚本,进入 EASY-EAI 编译环境。具体如下。
cd ~/develop_environment./run.sh 22041.2.2 下载源码并编译示例
在 EASY-EAI 编译环境下,创建用于管理源码仓库的目录。
cd /optmkdir EASY-EAI-Toolkitcd EASY-EAI-Toolkithttps://dl.dragonwake.com/download/rv1126b/embedded/EASY-EAI-Toolkit-1126B/Demos.zip
从上述链接下载 Demos.zip 压缩文件,上传到虚拟机的 /opt/EASY-EAI-Toolkit 目录并解压。 进入对应示例目录并执行编译操作。具体命令如下。
cd EASY-EAI-Toolkit-1126B/Demos/netProtocol-http/./build.sh💡 注意:由于依赖库放置在板端,交叉编译过程中必须保持 /mnt 挂载。

HTTP 示例构建结果
1.2.3 运行示例
通过串口调试或 SSH 调试进入板端后台,并切换到示例所在位置,如下所示。
cd /userdata/Demo/netProtocol-http
HTTP 示例目录
运行示例的命令如下。
./test-http_server & ./test-http_client1.2.4 运行结果
运行结果如下。

HTTP 示例运行结果
1.3. HTTPS 库函数说明
本章说明 EASY EAI 的 HTTPS 库函数使用方法。
1.3.1 包含/引用方法
EASY EAI api 库位于本仓库的 easyeai-api 目录中。为了方便用户在本地项目中直接调用 EASY EAI api 库,下面列出了项目中需要链接的库、头文件等。
| 说明 | CMake 中的写法 | Makefile 中的写法 |
|---|---|---|
| api.cmake | ${netProtocol_root}/http/api.cmake | 无 |
| 头文件目录 | ${HTTP_INCLUDE_DIRS} | -I ../../easyeai-api/netProtocol/http |
| 源码文件目录 | ${HTTP_SOURCE_DIRS} | ../../easyeai-api/netProtocol/http |
| 库文件目录 | 无 | 无 |
| 库链接参数 | ${HTTP_LIBS} | 无 |
API 源码路径为 EASY-EAI-Toolkit-1126B/easyeai-api/netProtocol/http/。用户可以通过源码查看接口实现,也可以直接修改源码。
1.3.2 证书设置
设置证书的函数原型如下。
void set_customer_crt(const char \*crt_file);具体说明如下。
| 函数名:set_customer_crt() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/http/https_request.h |
| 输入参数 | crt_file:证书路径 |
| 返回值 | 无 |
| 注意事项 | 无 |
1.3.3 向 HTTP/HTTPS 服务器发送 form-data 数据
1.3.3.1 清除 form_data 缓存
清除缓存的函数原型如下。
void clear_multipart();具体说明如下。
| 函数名:clear_multipart() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/http/https_request.h |
| 输入参数 | 无 |
| 返回值 | 无 |
| 注意事项 | form-data 缓存只有一个,因此无论在哪里执行,form-data 缓存都会被清空。如果在多线程中执行该函数,请注意线程同步问题。 |
1.3.3.2 添加 form_data 缓存(填充)
添加缓存的函数原型如下。要发送的数据以键值对形式添加。
void add_multipart_form_data(const char \*key, const char \*value);具体说明如下。
| 函数名:add_multipart_form_data() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/http/https_request.h |
| 输入参数 | key:form-data 数据的键名 value:form-data 数据内容 |
| 返回值 | 无 |
| 注意事项 | form-data 缓存只有一个。执行 clear_multipart() 后,此前添加到缓存中的所有 form-data 数据都会被清除。如果在多线程中执行,请注意线程同步问题。 |
1.3.3.3 发送 form-data 数据
HTTP 与 HTTPS 的加密方式不同,因此虽然参数相同,但定义了两个函数。函数原型如下。
int32_t send_data_to_Http(const char *server, const char *func, char *result, uint32_t result_lenth);int32_t send_data_to_Https(const char *server, const char *func, char *result, uint32_t result_lenth);具体说明如下。
| HTTP 发送函数:send_data_to_Http() HTTPS 发送函数:send_data_to_Https() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/https/https_request.h(按原文保留) |
| 输入参数 | server:服务器 URL func:方法(路径/API 名) result:用于保存 HTTP/HTTPS 返回正文数据的缓冲区 result_lenth:用户为正文数据分配的缓冲区最大大小 |
| 返回值 | -1:失败 0:成功。函数内部会自动调用 clear_multipart() 并清除缓存。 |
| 注意事项 | 该接口封装了 HTTP/HTTPS POST 请求,用于发送 form-data 缓存。调用成功后会清除缓存。在多线程环境中请注意同步。 |
1.3.4 向 HTTP/HTTPS 服务器发送 JSON 数据
由于 HTTP 与 HTTPS 的加密方式不同,因此分别定义了函数(参数相同)。
int32_t send_json_to_Http(const char *server, const char *func, const char *json, char *result, uint32_t result_length);int32_t send_json_to_Https(const char *server, const char *func, const char *json, char *result, uint32_t result_length);具体说明如下。
| HTTP 发送函数:send_json_to_Http() HTTPS 发送函数:send_json_to_Https() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/https/https_request.h |
| 输入参数 | server:服务器 URL func:方法(路径/API 名) json:要发送的 JSON 字符串 result:用于保存返回正文数据的缓冲区 result_lenth:用户为正文数据分配的缓冲区最大大小 |
| 返回值 | -1:失败 0:成功 |
| 注意事项 | 无 |
1.3.5 从 HTTP/HTTPS 服务器获取文件
由于 HTTP 与 HTTPS 的加密方式不同,因此分别定义了函数(参数相同)。
int32_t get_file_from_https(const char *url, const char *func, const char *filePath);int32_t get_file_from_http(const char *url, const char *func, const char *filePath);具体说明如下。
| HTTP 文件获取函数:get_file_from_https()(按原文保留) HTTPS 文件获取函数:get_file_from_http()(按原文保留) | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/https/https_request.h |
| 输入参数 | url:服务器 URL 路径 func:方法(路径) filePath:保存所获取文件的本地路径 |
| 返回值 | Success、Unknown、Connection、BindIPAddress、ReadWrite、ExceedRedirectCount、Canceled、SSLConnection、SSLLoadingCerts、SSLServerVerification、UnsupportedMultipartBoundaryChars、Compression 等 |
| 注意事项 | 该接口封装了 HTTP/HTTPS GET 请求。 |
1.4. 使用示例
示例代码路径为 EASY-EAI-Toolkit-1126B/Demos/netProtocol-http/test-http_client.c。
1.4.1 设置 CRT 密钥文件(证书)
密钥文件设置操作如下。
set_customer_crt("/userdata/customer.crt");1.4.2 发送 form_data 数据
1.4.2.1 添加数据
清除 form_data 数据缓冲区,然后向 form_data 缓冲区中添加数据。
clear_multipart();add_multipart_form_data("name", "gzlmo");add_multipart_form_data("id", "888888");add_multipart_form_data("pwd", "123456");1.4.2.2 发送数据
发送操作如下。
send_data_to_Http("192.168.3.191:50000", "/add", res, sizeof(res));1.4.3 发送 JSON 数据
JSON 数据发送操作如下。
send_json_to_Http("192.168.3.191:50000", "/add", "{name : \\gzlmo\\}", res, sizeof(res));2.MQTT
2.1. MQTT 概述
MQTT(Message Queuing Telemetry Transport)是一种基于发布/订阅(publish/subscribe)模型的“轻量级”通信协议。MQTT 最大的优点是能够以极少的代码和有限的带宽,为远程设备连接提供实时且可靠的消息服务。作为开销小、带宽消耗低的即时通信协议,它被广泛应用于 IoT(物联网)、小型设备、移动应用等领域。
2.1.1 发布与订阅
MQTT 采用的发布/订阅消息模型提供了一对多的消息分发机制,实现了应用之间的松耦合。在这种消息传递模型中,消息并不是从发送方直接发送到接收方(点对点),而是通过 MQTT 服务器(也称 MQTT Broker)进行分发。
-
MQTT 通信过程中存在三个角色:发布者(Publisher)、代理/服务器(Broker)、订阅者(Subscriber)。
-
MQTT 传输的消息分为主题(Topic)和载荷(Payload)两个部分。

MQTT 发布/订阅模型
发布者向 Broker(MQTT Broker)发布特定主题的消息,Broker 会将该消息推送给所有订阅该主题的订阅者。
- 发布消息的客户端是发布者,订阅主题消息的客户端是订阅者。
2.1.2 服务质量(QoS: Quality of Service levels)
服务质量是 MQTT 的重要特性之一。使用 TCP/IP 时,连接在一定程度上受到保护。但在无线网络中,断开和干扰经常发生,因此 MQTT 的 QoS 等级有助于防止信息丢失。
2.1.2.1 QoS 0
“最多一次(At most once)”。消息投递完全依赖底层 TCP/IP 网络。消息可能丢失或重复。该等级适用于环境传感器数据等场景,因为后续数据会很快发送,偶尔漏掉一条记录不会造成问题。

MQTT QoS 0 通信流程
2.1.2.2 QoS 1
“至少一次(At least once)”。保证消息到达,但消息可能重复到达。

MQTT QoS 1 通信流程
2.1.2.3 QoS 2
“恰好一次(Exactly once)”。保证消息只到达一次。该等级适用于计费系统等场景,因为消息重复或丢失都会导致错误结果。

MQTT QoS 2 通信流程
- 带有唯一消息 ID 的消息会同时保存在发送方和接收方。QoS 2 级别在发送方和接收方都需要两个流程,因此网络开销最高。
2.1.3 开源项目介绍
Eclipse Paho C 库使用广泛且社区活跃,是目前最常见、最受欢迎的 C 语言 MQTT 客户端开源库。它适用于 Windows、macOS、Linux、FreeRTOS、ESP32 等平台。
在 Ubuntu 系统中,可以直接编译 GitHub 仓库以获取所需库和头文件,也可以使用以下命令直接安装(此步骤可选):
sudo apt-get updatesudo apt-get install libpaho-mqtt-devPaho C 的优点是支持平台丰富、兼容性高,但也存在 API 偏底层、使用不便、还需要自行进行封装处理的限制。
因此,本文档采用 Eclipse 旗下的另一个 MQTT 项目 mosquitto。该项目更适合资源受限的嵌入式设备,虽然不如 Paho C 全面,但能满足基本需求,并且非常易用。
在 Ubuntu 系统中,可使用以下命令直接安装相关开发库:
sudo apt-get updatesudo apt-get install libmosquitto-dev2.1.4 常见通信模型
常见用法是将 MQTT Server(Broker)程序部署到云端,板卡(开发板)作为发布者和订阅者进行数据收发通信。

MQTT 通信模型
如果要在局域网(LAN)内使用,也可以在 PC 平台(Windows/Linux)上部署 MQTT Server,让板卡作为发布者和订阅者进行通信。
例如,可在 Ubuntu 系统中执行以下命令部署 MQTT Server:
sudo apt-get updatesudo apt-get install mosquittosudo systemctl start mosquitto如果希望 MQTT Server 在 Ubuntu 启动时自动启动,请额外执行以下命令:
sudo systemctl enable mosquitto2.1.5 Demo 说明
为了便于说明 Demo,本文档将 Broker、Subscriber、Publisher 全部部署在同一块板卡设备上。
首先给板卡上电并连接网络。然后通过串口或 SSH 进入板端后台,安装 MQTT 相关资源:
sudo apt-get updatesudo apt-get install libmosquitto-dev mosquittosudo systemctl enable mosquitto && sudo systemctl start mosquitto2.2. 快速开始
2.2.1 开发环境准备
如果是第一次阅读本文档,请先阅读《入门指南/开发环境准备/Easy-Eai 编译环境的准备与更新》,并按照其中步骤搭建编译环境。
在 PC 上的 Ubuntu 系统中运行 run 脚本,进入 EASY-EAI 编译环境。
cd ~/develop_environment./run.sh 22042.2.2 下载源码并编译示例
在 EASY-EAI 编译环境下,创建用于管理源码仓库的目录。
cd /optmkdir EASY-EAI-Toolkitcd EASY-EAI-Toolkithttps://dl.dragonwake.com/download/rv1126b/embedded/EASY-EAI-Toolkit-1126B/Demos.zip 从上述链接下载 Demos.zip 压缩文件,上传到虚拟机的 /opt/EASY-EAI-Toolkit 目录并解压。 进入对应示例目录并执行编译操作。
cd EASY-EAI-Toolkit-1126B/Demos/netProtocol-mqtt/./build.sh
MQTT 示例构建结果
💡 注意:由于依赖库放置在板端,交叉编译过程中必须保持 /mnt 挂载。如果编译失败并出现找不到 mosquitto.h 的错误,请查看 1.5 Demo 说明 小节。
2.2.3 运行示例
通过串口调试或 SSH 进入板端后台,切换到示例程序所在位置。
cd /userdata/Demo/netProtocol-mqtt
MQTT 示例目录
首先运行 Subscriber 程序:
./test-mqtt_subscription &
MQTT Subscriber 启动结果
💡 注意:如果执行失败并显示“Connection refused(连接被拒绝)”,请查看 1.5 Demo 说明 小节。Subscriber 会在后台运行,因此再次运行前请先使用 kill 命令结束进程。
接着运行 Publisher 程序:
./test-mqtt_publish2.2.4 実行結果
从运行结果可以看到,Subscriber 正常接收并输出了 Publisher 发送的消息。

MQTT 收发运行结果
2.3. Mosquitto 库函数说明
本章说明本示例使用的 mosquitto 库接口。更详细的 API 说明请参阅官方网站 API 文档(https://mosquitto.org/api/files/mosquitto-h.html)。
2.3.1 引用方法
EASY EAI api 库位于本仓库的 easyeai-api 目录中。项目中需要链接的库、头文件等如下。
| 说明 | CMake 中的写法 | Makefile 中的写法 |
|---|---|---|
| api.cmake | ${netProtocol_root}/mqtt/api.cmake | 无 |
| 头文件目录 | ${MQTT_INCLUDE_DIRS} | $SYSROOT/usr/include/ |
| 源码文件目录 | ${MQTT_SOURCE_DIRS} | 无 |
| 库文件目录 | 无 | $SYSROOT/usr/lib/aarch64-linux-gnu/ |
| 库链接参数 | ${MQTT_LIBS} | -lmosquitto -lcrypto -lssl -lstdc++ |
API 源码路径为 EASY-EAI-Toolkit-1126B/easyeai-api/netProtocol/mqtt/。
2.3.2 初始化 Mosquitto 库
int mosquitto_lib_init(void);| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_lib_init() |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | 无 |
| 返回值 | MOSQ_ERR_SUCCESS (0):初始化成功 |
2.3.3 新建 Mosquitto 客户端实例
struct mosquitto \*mosquitto_new(const char \*id, bool clean_session, void \*userdata);| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_new(const char *id, bool clean_session, void *userdata) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | id:客户端标识符(字符串) clean_session:为 true 时,断开连接时清除会话信息;为 false 时,保留会话信息。 userdata:指向用户自定义数据的指针 |
| 返回值 | struct mosquitto *(指向实例的指针) |
2.3.4 设置连接回调函数
处理与 Broker 的连接状态变化。
void mosquitto_connect_callback_set(struct mosquitto \*mosq, void (\*on_connect)(struct mosquitto \*, void \*,int));| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 on_connect:指向连接回调函数的指针(返回值 void,参数:客户端实例、用户数据、连接返回码) |
2.3.5 设置断开连接回调函数
处理与 Broker 断开连接的事件。
void mosquitto_disconnect_callback_set(struct mosquitto \*mosq, void (\*on_disconnect)(struct mosquitto \*, void \*, int));| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 on_disconnect:指向断开连接回调函数的指针(返回值 void,参数:客户端实例、用户数据、断开连接返回码) |
2.3.6 设置消息发布回调函数
处理消息成功发送到 Broker 时的事件。
void mosquitto_publish_callback_set(struct mosquitto \*mosq, void (\*on_publish)(struct mosquitto \*, void \*, int));| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)) |
| 头文件 | netProtocol-mqtt/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 on_publish:指向消息发布回调函数的指针(参数:客户端实例、用户数据、消息 ID) |
2.3.7 设置订阅回调函数
处理主题订阅成功时的事件。
void mosquitto_subscribe_callback_set(struct mosquitto \*mosq, void (\*on_subscribe)(struct mosquitto \*, void \*, int, int, const int \*));| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 on_subscribe:回调函数指针。参数依次为:客户端实例、用户数据指针、消息 ID、成功的主题数量、QoS 等级数组。 |
2.3.8 连接到指定 Broker
int mosquitto_connect(struct mosquitto \*mosq, const char \*host, int port, int keepalive);| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 host:Broker 地址 port:Broker 端口号 keepalive:保活时间(秒) |
2.3.9 订阅主题
int mosquitto_subscribe(struct mosquitto \*mosq, int \*mid, const char \*sub, int qos);| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 mid:指向存储消息 ID 的变量的指针(也可为 NULL) sub:要订阅的主题字符串 qos:订阅的 QoS 等级 |
| 返回值 | MOSQ_ERR_SUCCESS 表示成功,其他值表示失败 |
2.3.10 向指定主题发布消息
int mosquitto_publish(struct mosquitto \*mosq, int \*mid, const char \*topic, int payloadlen, const void \*payload, int qos, bool retain);| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 mid:用于存储消息 ID 的指针(也可为 NULL) topic:发布目标主题字符串 payloadlen:消息载荷长度 payload:指向消息载荷数据的指针 qos:QoS 等级 retain:为 true 时保留(Retain)消息 |
| 返回值 | MOSQ_ERR_SUCCESS 表示成功,其他值表示失败 |
2.3.11 从 Broker 断开连接
void mosquitto_destroy(struct mosquitto \*mosq);| 項目 | 詳細 |
|---|---|
| 函数名 | mosquitto_disconnect(struct mosquitto *mosq) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 |
2.3.12 销毁 Mosquitto 客户端实例
void mosquitto_destroy(struct mosquitto \*mosq);| 項目 | 詳細 |
|---|---|
| 函数名 | mosquitto_destroy(struct mosquitto *mosq) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | mosq:客户端实例 |
2.3.13 清理 Mosquitto 库使用的资源
void mosquitto_lib_cleanup(void);| 项目 | 详细信息 |
|---|---|
| 函数名 | mosquitto_lib_cleanup(void) |
| 头文件 | $SYSROOT/usr/include/mosquitto.h |
| 输入参数 | 无 |
2.4. 使用示例
- 发送(Publish)示例代码路径:
EASY-EAI-Toolkit-1126B/Demos/netProtocol-mqtt/test-mqtt_publish.c
- 订阅(Subscribe)示例代码路径:
EASY-EAI-Toolkit-1126B/Demos/netProtocol-mqtt/test-mqtt_subscription.c
3.MODBUS
3.1. MODBUS 概述
MODBUS 是一种应用层消息传输协议,用于在通过不同类型总线或网络连接的设备之间进行客户端/服务器通信。Modbus 比其他通信协议使用更广泛的主要原因是:它公开发布且没有版权要求,易于部署和维护,并且对于供应商在移动本地位和字节时限制较少。
3.1.1 协议版本与内容
目前,Modbus 协议存在支持串口、以太网以及其他互联网协议的网络版本。
大多数 Modbus 设备通过串行 EIA-485 物理层进行通信。串行连接有两种变体,它们在数值数据表示方式和协议细节上略有不同。MODBUS 模式分为三类:RTU(远程终端单元)模式、ASCII(美国信息交换标准代码)模式,以及 TCP(运行在以太网上的协议)模式。
-
Modbus RTU:一种以二进制表示数据的紧凑方式(Modbus 协议规定默认模式必须为 RTU,ASCII 为可选)。
-
Modbus ASCII:一种人类可读的冗长表示方式。
这两种变体都使用串行通信(serial communication)。通过 TCP/IP(如以太网)连接时,存在多种 Modbus/TCP 变体,该方式不需要计算校验和。这三种通信协议在数据模型和函数调用方面完全相同,仅封装方式不同。
3.1.2 通信与设备
Modbus 协议采用主/从(master/slave)架构。一个节点为主节点,其他使用 Modbus 协议参与通信的节点为从节点。每个从设备都有唯一地址。在串行网络和 MB+ 网络中,只有被指定为主节点的节点才能发起命令(在以太网上,任何设备都可以发送 Modbus 命令,但通常只有一个主节点设备发起命令)。
Modbus 命令中包含要执行该命令的设备的 Modbus 地址。所有设备都会接收命令,但只有指定地址的设备会执行命令并响应(地址 0 是例外,指定地址 0 的命令为广播命令,所有接收到命令的设备都会执行,但不会响应)。所有 Modbus 命令都包含校验码,用于确认到达的命令没有损坏。基本 Modbus 命令可以让 RTU 修改其寄存器中的某个特定值、控制或读取 I/O 端口,或指示设备返回其寄存器中的一个或多个数据。
3.1.3 MODBUS 使用要点
根据以上说明,使用 Modbus 时需要始终注意以下几点。
-
Modbus 是主从方式通信,不能异步通信。
-
如果主站不发送,总线上就不会发生数据通信。
-
设备必须支持 RTU 协议(这是 Modbus 协议中的规定)。
-
基本流程如下。
-
发送:从站地址 + 功能码 + 寄存器地址 + 寄存器地址数量 + 校验码
-
响应:从站地址 + 功能码 + 发送给主站的数据字节数 + 数据 + 校验码
-
我们的 easyeai-api 软件开源库对复杂的消息格式和收发操作进行了统一封装,使用户能够通过简单便捷的调用方式实现 Modbus 通信功能。
3.2. 快速开始
3.2.1 开发环境准备
在 PC 端 Ubuntu 系统中运行 run 脚本,进入 EASY-EAI 编译环境。具体步骤如下。
cd ~/develop_environment./run.sh 22043.2.2 下载源码并编译示例程序
在 EASY-EAI 编译环境中,创建用于保存源码仓库的管理目录。
cd /optmkdir EASY-EAI-Toolkitcd EASY-EAI-Toolkithttps://dl.dragonwake.com/download/rv1126b/embedded/EASY-EAI-Toolkit-1126B/Demos.zip
从上述链接下载 Demos.zip 压缩文件,上传到虚拟机的 /opt/EASY-EAI-Toolkit 目录并解压。
进入对应示例程序目录并执行编译操作。具体命令如下。
cd EASY-EAI-Toolkit-1126B/Demos/netProtocol-modbus/./build.sh💡 注意:由于依赖库放置在板端,交叉编译过程中必须保持 /mnt 挂载。

MODBUS 示例构建结果
3.2.3 运行示例程序
通过串口调试或 SSH 调试进入板端后台,并切换到示例程序部署所在位置,如下所示。
cd /userdata/Demo/netProtocol-modbus
MODBUS 示例目录
运行示例程序的命令如下。
./test-modbus_tcp_master & ./test-modbus_tcp_slave3.2.4 実行結果
TCP 模式的运行结果如下。

MODBUS TCP 运行结果
如果需要改为 RTU 模式,可以自行修改代码。
- 代码路径:EASY-EAI-Toolkit-1126B/Demos/netProtocol-modbus/test-modbus_tcp_slave.c

MODBUS RTU 代码修改位置
有关 API 的详细说明以及 API 调用(该示例程序的源代码),请参阅以下说明。
3.3. MODBUS 库函数说明
本章介绍 EASY EAI 的 MODBUS 库函数使用方法。
3.3.1 引用(包含)方法
EASY EAI api 库位于本仓库的 easyeai-api 目录中。为了便于用户在本地项目中直接调用我们的 EASY EAI api 库,下面列出了项目中需要链接的库和头文件等。
| 说明 | CMake 中的写法 | Makefile 中的写法 |
|---|---|---|
| api.cmake | ${netProtocol_root}/modbus/api.cmake | 无 |
| 头文件目录 | ${MODBUS_INCLUDE_DIRS} | -I ../../easyeai-api/netProtocol/modbus |
| 源码目录 | ${MODBUS_SOURCE_DIRS} | ../../easyeai-api/netProtocol/modbus |
| 库目录 | 无 | 无 |
| 链接参数 | ${MODBUS_LIBS} | 无 |
API 源码路径为 EASY-EAI-Toolkit-1126B/easyeai-api/netProtocol/modbus/。可以通过源码查看接口实现,也可以修改源码。
3.3.2 创建实例
通过 TCP 方式创建并初始化 Modbus 实例。
modbus_t\* modbus_new_tcp(const char \*ip, int port);| 函数名:modbus_new_tcp() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ip:IP 地址 port:端口号 |
| 返回值 | 成功:Modbus 实例 失败:NULL |
| 注意事项 | 无 |
3.3.3 设置从站 ID
设置从站 ID 的函数原型如下。
void modbus_set_slave(modbus_t \*ctx, int slave);| 函数名:modbus_set_slave() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 slave:从站 ID |
| 返回值 | 成功:0 失败:-1 |
| 注意事项 | 无 |
3.4 与从站连接
与从站建立连接的函数原型如下。
void modbus_connect(modbus_t \*ctx);| 函数名:modbus_connect() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 |
| 返回值 | 成功:0 失败:-1 |
| 注意事项 | 无 |
3.4.1 寄存器操作(功能码对应函数)
3.4.1.1 读取线圈状态(功能码:0x01)
可以读取连续多个线圈状态。
int modbus_read_bits(modbus_t \*ctx, int addr, int nb, uint8_t \*dest);| 函数名:modbus_read_bits() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:寄存器起始地址 nb:寄存器数量 dest:获取到的状态值 |
| 返回值 | 读取到的寄存器数量(失败时为 -1) |
| 注意事项 | 无 |
3.4.1.2 读取输入状态(功能码:0x02)
可以读取连续多个输入状态。
int modbus_read_input_bits(modbus_t \*ctx, int addr, int nb, uint8_t \*dest);| 函数名:modbus_read_input_bits() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:寄存器起始地址 nb:寄存器数量 dest:获取到的状态值 |
| 返回值 | 成功:返回 nb 的值(失败时为 -1) |
| 注意事项 | 无 |
3.4.1.3 读取保持寄存器(功能码:0x03)
可以读取连续多个保持寄存器的值。
int modbus_read_registers(modbus_t \*ctx, int addr, int nb, uint16_t \*dest);| 函数名:modbus_read_registers() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:寄存器起始地址 nb:寄存器数量 dest:获取到的寄存器值 |
| 返回值 | 成功:读取到的寄存器数量 失败:-1 |
| 注意事项 | 无 |
3.4.1.4 读取输入寄存器(功能码:0x04)
可以读取连续多个输入寄存器的值。
int modbus_read_input_registers(modbus_t \*ctx, int addr, int nb, uint16_t \*dest);| 函数名:modbus_read_input_registers() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:寄存器起始地址 nb:寄存器数量 dest:获取到的寄存器值 |
| 返回值 | 成功:读取到的寄存器数量 失败:-1 |
| 注意事项 | 无 |
3.4.1.5 写入单个线圈状态(功能码:0x05)
int modbus_write_bit(modbus_t \*ctx, int addr, int status);| 函数名:modbus_write_bit() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:线圈地址 status:线圈状态 |
| 返回值 | 成功:0 失败:-1 |
| 注意事项 | 无 |
3.4.1.6 写入多个连续线圈状态(功能码:15)
int modbus_write_bits(modbus_t \*ctx, int addr, int nb, const uint8_t \*src);| 函数名:modbus_write_bits() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:线圈起始地址 nb:线圈数量 src:要写入的多个线圈状态 |
| 返回值 | 成功:0 失败:-1 |
| 注意事项 | 无 |
3.4.1.7 写入单个寄存器(功能码:0x06)
int modbus_write_register(modbus_t \*ctx, int addr, int value);| 函数名:modbus_write_register() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:寄存器地址 value:寄存器值 |
| 返回值 | 成功:0 失败:-1 |
| 注意事项 | 无 |
3.4.1.8 写入多个连续寄存器(功能码:16)
int modbus_write_registers(modbus_t \*ctx, int addr, int nb, const uint16_t \*src);| 函数名:modbus_write_registers() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 addr:寄存器起始地址 nb:寄存器数量 src:要写入的多个寄存器值 |
| 返回值 | 成功:0 失败:-1 |
| 注意事项 | 无 |
3.4.2 关闭套接字
关闭套接字的函数原型如下。
void modbus_close(modbus_t \*ctx);| 函数名:modbus_close() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 |
| 返回值 | 无 |
| 注意事项 | 无 |
3.4.3 释放实例
释放实例的函数原型如下。
void modbus_free(modbus_t \*ctx);| 函数名:modbus_free() | 详细信息 |
|---|---|
| 头文件 | easyeai-api/netProtocol/modbus/modbus.h |
| 输入参数 | ctx:Modbus 实例 |
| 返回值 | 无 |
| 注意事项 | 无 |