├── .gitignore ├── LICENSE ├── README.md ├── SConscript ├── docs └── pictures │ └── umqtt_分层图.jpg ├── inc ├── umqtt.h ├── umqtt_cfg.h └── umqtt_internal.h ├── samples └── umqtt_sample.c └── src ├── pkgs ├── umqtt_internal.c ├── umqtt_pkgs_decode.c └── umqtt_pkgs_encode.c ├── trans └── umqtt_transport.c └── umqtt.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uMQTT 2 | 3 | ## 1、介绍 4 | 5 | uMQTT 软件包是 RT-Thread 自主研发的,基于 MQTT 3.1.1 协议的客户端实现,它提供了设备与 MQTT Broker 通讯的基本功能 6 | 7 | uMQTT 软件包功能如下: 8 | 9 | * 实现基础的连接、订阅、发布功能; 10 | * 具备多重心跳保活,设备重连机制,保证 mqtt 在线状态,适应复杂情况; 11 | * 支持 QoS=0, QoS=1, QoS=2 三种发送信息质量; 12 | * 支持多客户端使用; 13 | * 用户端接口简便,留有多种对外回调函数; 14 | * 支持多种技术参数可配置,易上手,便于产品化开发; 15 | * 功能强大,资源占用率低,支持功能可裁剪。 16 | 17 | 资源占用(测试环境 W60X ): 18 | 19 | | ROM | RAM | 动态RAM | 20 | | :-: | :-: | :-: | 21 | | 12.97KByte | 0.01KByte | 4.9KByte | 22 | 23 | 24 | ### 1.1 目录结构 25 | 26 | ``` 27 | umqtt 28 | ├───docs // 说明文档 29 | ├───inc 30 | │ ├───umqtt_internal.h // 内部打包及发送相关头文件 31 | │ ├───umqtt_cfg.h // 结构体配置头文件 32 | │ └───umqtt.h // 对外提供接口头文件 33 | ├───src 34 | │ ├───pkgs // 完成对 paho_mbedded 软件库的功能裁剪移植 35 | │ │ ├───umqtt_pkgs_decode.c // 打包实现源文件 36 | │ │ ├───umqtt_pkgs_encode.c // 解包实现源文件 37 | │ ├───trans 38 | │ │ └───umqtt_trans.c // 传输层相关源文件 39 | │ └───umqtt_utils.c // 通用接口实现文件 40 | ├───samples // finsh 调试接口示例 41 | ├───tests // 测试用例 42 | ├───LICENSE // 软件包许可证 43 | ├───README.md // 软件包使用说明 44 | └───SConscript // RT-Thread 默认的构建脚本 45 | ``` 46 | 47 | ### 1.2 许可证 48 | uMQTT 软件包遵循 Apache-2.0 许可,详见 LICENSE 文件。 49 | 50 | ### 1.3 依赖 51 | 52 | - RT_Thread 3.0+ 53 | - SAL 层组件 54 | 55 | ## 2、获取软件包 56 | 57 | **uMQTT 软件包相关配置选项介绍** 58 | ``` 59 | --- umqtt: A MQTT Client for RT-Thread 60 | [ ] Enable MQTT example 61 | [ ] Enable MQTT test 62 | (4) subtopic name list numbers 63 | (1024) send buffer size 64 | (1024) receive buffer size 65 | (1000) uplink timer def cycle, uint:mSec 66 | (5) reconnect max count 67 | (60) reconnect time interval, uint:Sec 68 | (5) keepalive func, max count 69 | (30) heartbeat interval, uint:Sec 70 | (4) connect timeout, uint:Sec 71 | (100) receive timeout, uint:mSec 72 | (4) send timeout, uint:Sec 73 | (4096) receive thread stack size 74 | (8) thread priority 75 | (4) async message ack queue count 76 | (0xFFFF) connect information, keepalive interval, uint:Sec 77 | [ ] Enable change connect keepalive time, uint:Sec 78 | Version (latest) ---> 79 | ``` 80 | 81 | * subtopic name list numbers: 内部允许最大同时订阅数量 82 | * send buffer size: 发送数据缓存大小 83 | * receive buffer size: 接收数据缓存大小 84 | * uplink timer def cycle, uint:mSec: 定时器运行周期, 单位: mSec 85 | * reconnect max count: 最大重连次数 86 | * reconnect time interval, uint:Sec: 重连间隔时间, 单位: Sec 87 | * keepalive func, max count: 保活机制中心跳重连次数 88 | * heartbeat interval, uint:Sec: 心跳发送间隔, 单位: Sec 89 | * connect timeout, uint:Sec: 连接超时时间, 单位: Sec 90 | * receive timeout, uint:mSec: 接收超时时间, 单位: mSec 91 | * send timeout, uint:Sec: 发送超时时间, 单位: Sec 92 | * receive thread stack size: 内部接收线程堆栈 93 | * thread priority: 内部线程优先级 94 | * async message ack queue size: 接收线程处理完接收数据向外部传送处理结果消息队列大小 95 | * connect information, keepalive interval, uint:Sec:MQTT 协议中 CONNECT 命令下 KEEPALIVE 值,默认最大 0xFF, 单位: Sec 96 | * Enable change connect keepalive time, uint:Sec: 允许修改 MQTT 连接信息中的 keepalive 时间, 单位: Sec 97 | * Version: 软件版本号 98 | 99 | ## 3、使用 uMQTT 软件包 100 | 101 | ### 3.1 软件包工作原理 102 | 103 | uMQTT 软件包主要用于在嵌入式设备上实现 MQTT 协议,软件包的主要工作基于 MQTT 协议实现。软件包分层图如下: 104 | 105 | ![umqtt_分层图](./docs/pictures/umqtt_分层图.jpg) 106 | 107 | 软件包实现过程中主要做了: 108 | 109 | 1. 根据 MQTT 3.1.1 协议规定,进行软件包数据协议的封包解包; 110 | 111 | 2. 传输层函数适配对接 SAL 层; 112 | 113 | 3. umqtt 客户端层,根据协议包层和传输层编写符合应用层的接口。实现基础连接、断连、订阅、取消订阅、发布消息等功能。支持 QoS0/1/2 三种发送信息质量。利用 uplink timer 定时器,实现多重心跳保活机制和设备重连机制,增加设备在线稳定性,适应复杂情况。 114 | 115 | ### 3.2 用户 API 介绍 116 | 117 | #### 3.2.1 创建对象 118 | ```c 119 | umqtt_client_t umqtt_create(const struct umqtt_info *info); 120 | ``` 121 | 创建客户端结构体对象。 122 | 123 | | 参数 | 描述 | 124 | |:------------------|:-----------------------------------| 125 | | info | 用户信息配置 | 126 | | **返回值** | **描述** | 127 | | != RT_NULL | umqtt 客户端结构体指针 | 128 | | == RT_NULL | 创建失败 | 129 | 130 | #### 3.2.2 删除对象 131 | ```c 132 | int umqtt_delete(struct umqtt_client *client); 133 | ``` 134 | 删除客户端结构体对象,并释放内存。 135 | 136 | | 参数 | 描述 | 137 | |:----|:----| 138 | | client | umqtt 客户端结构体指针 | 139 | | **返回值** | **描述** | 140 | | UMQTT_OK | 成功 | 141 | 142 | #### 3.2.3 启动客户端 143 | ```c 144 | int umqtt_start(struct umqtt_client *client); 145 | ``` 146 | 启动客户端会话,进行网络连接,和 MQTT 协议连接。 147 | 148 | | 参数 | 描述 | 149 | |:----|:----| 150 | | client | umqtt 客户端结构体指针 | 151 | | **返回值** | **描述** | 152 | | >=0 | 成功 | 153 | | <0 | 失败 | 154 | 155 | #### 3.2.4 停止客户端 156 | ```c 157 | void umqtt_stop(struct umqtt_client *client); 158 | ``` 159 | 停止客户端会话,关闭接收线程,暂停 uplink 定时器,发送 MQTT 断开连接命令,关闭 socket 套接字。 160 | 161 | | 参数 | 描述 | 162 | |:----|:----| 163 | | client | umqtt 客户端结构体指针 | 164 | | **返回值** | **描述** | 165 | | 无 | 无 | 166 | 167 | #### 3.2.5 发布消息 168 | ```c 169 | int umqtt_publish(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length, int timeout); 170 | ``` 171 | 针对订阅主题发布相关质量的消息。 172 | 173 | | 参数 | 描述 | 174 | |:----|:----| 175 | | client | umqtt 客户端结构体指针 | 176 | | qos | 消息发送质量 | 177 | | topic | 发布主题 | 178 | | payload | 发布消息 | 179 | | length | 发布消息的长度 | 180 | | timeout | 发布消息超时时间, 单位:mSec | 181 | | **返回值** | **描述** | 182 | | >=0 | 成功 | 183 | | <0 | 失败 | 184 | 185 | #### 3.2.6 订阅主题 186 | ```c 187 | int umqtt_subscribe(struct umqtt_client *client, const char *topic, enum umqtt_qos qos, umqtt_subscribe_cb callback); 188 | ``` 189 | 订阅主题,并设置对应主题接收 publish 消息时的回调函数。 190 | 191 | | 参数 | 描述 | 192 | |:----|:----| 193 | | client | umqtt 客户端结构体指针 | 194 | | topic | 订阅主题 | 195 | | qos | 订阅质量 | 196 | | callback | 对应主题接收 publish 消息时的回调函数 | 197 | | **返回值** | **描述** | 198 | | >=0 | 成功 | 199 | | <0 | 失败 | 200 | 201 | #### 3.2.7 取消订阅主题 202 | ```c 203 | int umqtt_unsubscribe(struct umqtt_client *client, const char *topic); 204 | ``` 205 | 取消相关主题的订阅,并释放相关的资源。 206 | 207 | | 参数 | 描述 | 208 | |:----|:----| 209 | | client | umqtt 客户端结构体指针 | 210 | | topic | 取消订阅主题 | 211 | | **返回值** | **描述** | 212 | | >=0 | 成功 | 213 | | <0 | 失败 | 214 | 215 | #### 3.2.8 异步发送消息 216 | ```c 217 | int umqtt_publish_async(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length); 218 | ``` 219 | 异步发送消息,只负责将信息发送出去,不负责阻塞接收。 220 | 221 | | 参数 | 描述 | 222 | |:----|:----| 223 | | client | umqtt 客户端结构体指针 | 224 | | qos | 发送消息质量 | 225 | | topic | 发送消息对应主题 | 226 | | payload | 发布的消息 | 227 | | length | 发布的消息长度 | 228 | | **返回值** | **描述** | 229 | | >=0 | 成功 | 230 | | <0 | 失败 | 231 | 232 | #### 3.2.9 设定获取参数 233 | ```c 234 | int umqtt_control(struct umqtt_client *client, enum umqtt_cmd cmd, void *params); 235 | ``` 236 | 根据相关命令设定或者读取内部相关参数。 237 | 238 | | 参数 | 描述 | 239 | |:----|:----| 240 | | client | umqtt 客户端结构体指针 | 241 | | cmd | 设定或者读取内部相关参数 | 242 | | params | 设定数据时,为返回值结构体;读取数据时,为 RT_NULL | 243 | | **返回值** | **描述** | 244 | | >=0 | 设定数据时, 成功; 读取数据时, 为具体返回数据 | 245 | | >0 | 设定数据时, 失败; 读取数据时, 为具体返回数据 | 246 | 247 | ### 3.3 示例介绍 248 | 249 | #### 3.3.1 准备工作 250 | 251 | - menuconfig 配置获取软件包和示例代码 252 | 253 | 打开 RT-Thread 提供的 ENV 工具,使用 **menuconfig** 配置软件包, 254 | 启用 UMQTT 软件包,并配置使能测试例程 (`Enable MQTT example`), 如下所示: 255 | 256 | ``` shell 257 | RT-Thread online packages 258 | IoT - internet of things ---> 259 | [*] umqtt: A MQTT Client for RT-Thread. ---> 260 | [*] Enable MQTT example # 开启 UMQTT 例程 261 | ``` 262 | 263 | - 使用 `pkgs --update` 命令下载软件包; 264 | - 编译下载; 265 | - 使用 [emqx](https://www.emqx.io/cn/) 搭建 MQTT Broker 。 266 | 267 | #### 3.3.2 启动例程 268 | 269 | * 启动 umqtt 客户端 270 | 271 | 启动 umqtt 客户端流程: 272 | - 申明 `struct umqtt_info` 结构体变量作为 umqtt 客户端用户配置变量 273 | - 测试 MQTT Broker 的 URI 进行赋值 274 | - 创建 umqtt 客户端 275 | - 声明并设置连接、在线、离线、心跳回调函数 276 | - 调用 `umqtt_start()` 函数,启动 umqtt 客户端 277 | 278 | ```shell 279 | msh />umqtt_ex_start 280 | [D/umqtt.sample] umqtt example start! 281 | [I/umqtt] connect success! 282 | [I/umqtt.sample] umqtt start success! 283 | ``` 284 | 285 | * 订阅功能 286 | 287 | ```shell 288 | msh />umqtt_ex_subscribe "test0" 289 | [D/umqtt.sample] umqtt example subscribe! 290 | [D/umqtt] start assign datas ! 291 | [D/umqtt] subscribe ack ok! 292 | ``` 293 | 294 | * 发布消息 295 | 296 | ```shell 297 | msh />umqtt_ex_publish test 0 hello # 消息发送质量 qos0 298 | [D/umqtt.sample] umqtt example publish! 299 | [D/umqtt.sample] umqtt topic recv callback! name length: 4, name: testhello, packet id: 0, payload len: 6 300 | 301 | msh />umqtt_ex_publish test 1 hello_this # 消息发送质量 qos1 302 | [D/umqtt.sample] umqtt example publish! 303 | [D/umqtt.sample] umqtt topic recv callback! name length: 4, name: test, packet id: 1, payload len: 11 304 | [I/umqtt] publish qos1 ack success! 305 | 306 | msh />umqtt_ex_publish test 1 hello_this_world # 消息发送质量 qos2 307 | [D/umqtt.sample] umqtt example publish! 308 | [D/umqtt.sample] umqtt topic recv callback! name length: 4, name: test, packet id: 2, payload len: 17 309 | [I/umqtt] publish qos2 ack success! 310 | 311 | ``` 312 | 313 | * 取消订阅 314 | 315 | ```shell 316 | msh />umqtt_ex_unsubscribe test 317 | [D/umqtt.sample] umqtt example unsubscribe! 318 | [I/umqtt] unsubscribe ack ok! 319 | ``` 320 | 321 | * 停止 umqtt 客户端 322 | 323 | ```shell 324 | msh />umqtt_ex_stop 325 | [D/umqtt.sample] umqtt example stop! 326 | ``` 327 | 328 | ## 4、注意事项 329 | 330 | * 本版本暂不支持加密通信协议; 331 | * 使用 [emqx](https://www.emqx.io/cn/) 搭建 MQTT Broker 。 332 | 333 | 334 | ## 5、联系方式 & 感谢 335 | 联系人: springcity 336 | Email: caochunchen@rt-thread.com 337 | 338 | 339 | -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | Import('RTT_ROOT') 2 | from building import * 3 | 4 | # get current directory 5 | cwd = GetCurrentDir() 6 | 7 | # The set of source files associated with this SConscript file. 8 | src = Glob('src/*.c') 9 | src += Glob('src/pkgs/*.c') 10 | src += Glob('src/trans/*.c'); 11 | 12 | if GetDepend('PKG_USING_UMQTT_EXAMPLE'): 13 | src += Glob('samples/*.c'); 14 | 15 | path = [cwd + '/inc'] 16 | 17 | group = DefineGroup('umqtt', src, depend = ['PKG_USING_UMQTT'], CPPPATH = path) 18 | 19 | Return('group') 20 | 21 | -------------------------------------------------------------------------------- /docs/pictures/umqtt_分层图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/umqtt/52b78a7c22cb21b4edafb33bcaa4c836cbe98729/docs/pictures/umqtt_分层图.jpg -------------------------------------------------------------------------------- /inc/umqtt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-29 springcity the first version 9 | */ 10 | 11 | #ifndef _UMQTT_H__ 12 | #define _UMQTT_H__ 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #define UMQTT_SW_VERSION "1.0.1" 21 | #define UMQTT_SW_VERSION_NUM 0x10001 22 | 23 | enum umqtt_client_state 24 | { 25 | UMQTT_CS_IDLE = 0x00000000, /* idle state */ 26 | UMQTT_CS_LINKING = 0x00000001, /* connecting */ 27 | UMQTT_CS_LINKED = 0x00000002, /* connected */ 28 | UMQTT_CS_UNLINK = 0x00000004, /* offline */ 29 | UMQTT_CS_UNLINK_LINKING = 0x00000008, /* offline linking */ 30 | UMQTT_CS_DISCONNECT = 0x00000010, /* reconnect failed */ 31 | }; 32 | 33 | enum umqtt_err_code 34 | { 35 | UMQTT_OK = 0, /* no error code */ 36 | UMQTT_FAILED = -1, /* function failed */ 37 | UMQTT_MEM_FULL = -2, /* out of memory */ 38 | UMQTT_TIMEOUT = -3, /* function timeout */ 39 | UMQTT_ENCODE_ERROR = -4, /* encode error */ 40 | UMQTT_DECODE_ERROR = -5, /* decode error */ 41 | UMQTT_SEND_TIMEOUT = -6, /* send timeout */ 42 | UMQTT_SEND_FAILED = -7, /* send failed */ 43 | UMQTT_INPARAMS_NULL = -8, /* input params is null */ 44 | UMQTT_BUFFER_TOO_SHORT = -9, /* buff too short */ 45 | UMQTT_READ_ERROR = -10, /* read error */ 46 | UMQTT_READ_FAILED = -11, /* read failed */ 47 | UMQTT_READ_TIMEOUT = -12, 48 | UMQTT_FIN_ACK = -13, /* server send fin ack to client */ 49 | UMQTT_RECONNECT_FAILED = -14, /* reconnect failed */ 50 | UMQTT_SOCK_CONNECT_FAILED = -15, 51 | UMQTT_DISCONNECT = -16, 52 | }; 53 | 54 | enum umqtt_evt 55 | { 56 | UMQTT_EVT_LINK = 0x00, /* link event */ 57 | UMQTT_EVT_ONLINE = 0x01, /* online event */ 58 | UMQTT_EVT_OFFLINE = 0x02, /* offline event */ 59 | UMQTT_EVT_HEARTBEAT = 0x03, /* heartbeat event */ 60 | }; 61 | 62 | enum umqtt_cmd 63 | { 64 | UMQTT_CMD_SUB_CB = 0x00, 65 | UMQTT_CMD_EVT_CB = 0x01, 66 | UMQTT_CMD_SET_HB = 0x02, /* set heartbeat time interval */ 67 | UMQTT_CMD_GET_CLIENT_STA = 0x03, /* get client status*/ 68 | 69 | UMQTT_CMD_DISCONNECT = 0x7E, /* close socket & mqtt disconnect */ 70 | UMQTT_CMD_DEL_HANDLE = 0x7F, 71 | #ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 72 | UMQTT_CMD_SET_CON_KP = 0x80, 73 | #endif 74 | }; 75 | 76 | enum umqtt_qos { UMQTT_QOS0 = 0, UMQTT_QOS1 = 1, UMQTT_QOS2 = 2, UMQTT_SUBFAIL = 0x80 }; 77 | 78 | struct umqtt_client; 79 | typedef struct umqtt_client *umqtt_client_t; 80 | typedef int (*umqtt_user_callback)(struct umqtt_client *client, enum umqtt_evt event); 81 | typedef void (*umqtt_subscribe_cb)(struct umqtt_client *client, void *msg); 82 | 83 | struct subtop_recv_handler 84 | { 85 | char *topicfilter; 86 | void (*callback)(void *client, void *message); 87 | enum umqtt_qos qos; 88 | rt_list_t next_list; 89 | }; 90 | 91 | struct umqtt_info 92 | { 93 | rt_size_t send_size, recv_size; /* send/receive buffer size */ 94 | const char *uri; /* complete URI (include: URI + URN) */ 95 | const char *client_id; /* client id */ 96 | const char *lwt_topic; /* will topic */ 97 | const char *lwt_message; /* will message */ 98 | const char *user_name; /* user_name */ 99 | const char *password; /* password */ 100 | enum umqtt_qos lwt_qos; /* will qos */ 101 | umqtt_subscribe_cb lwt_cb; /* will callback */ 102 | rt_uint8_t reconnect_max_num; /* reconnect max count */ 103 | rt_uint32_t reconnect_interval; /* reconnect interval time */ 104 | rt_uint8_t keepalive_max_num; /* keepalive max count */ 105 | rt_uint32_t keepalive_interval; /* keepalive interval */ 106 | rt_uint32_t recv_time_ms; /* receive timeout */ 107 | rt_uint32_t connect_time; /* connect timeout */ 108 | rt_uint32_t send_timeout; /* uplink_timeout publish/subscribe/unsubscribe */ 109 | rt_uint32_t thread_stack_size; /* thread task stack size */ 110 | rt_uint8_t thread_priority; /* thread priority */ 111 | #ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 112 | rt_uint16_t connect_keepalive_sec; /* connect information, keepalive second */ 113 | #endif 114 | }; 115 | 116 | 117 | 118 | /* create umqtt client according to user information */ 119 | umqtt_client_t umqtt_create(const struct umqtt_info *info); 120 | 121 | /* delete umqtt client */ 122 | int umqtt_delete(struct umqtt_client *client); 123 | 124 | /* start the umqtt client to work */ 125 | int umqtt_start(struct umqtt_client *client); 126 | 127 | /* stop the umqtt client work */ 128 | void umqtt_stop(struct umqtt_client *client); 129 | 130 | /* umqtt client publish datas to specified topic */ 131 | int umqtt_publish(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length, int timeout); 132 | 133 | /* subscribe the client to defined topic with defined qos */ 134 | int umqtt_subscribe(struct umqtt_client *client, const char *topic, enum umqtt_qos qos, umqtt_subscribe_cb callback); 135 | 136 | /* unsubscribe the client from defined topic */ 137 | int umqtt_unsubscribe(struct umqtt_client *client, const char *topic); 138 | 139 | /* umqtt client publish nonblocking datas */ 140 | int umqtt_publish_async(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length); 141 | 142 | /* set some config datas in umqtt client */ 143 | int umqtt_control(struct umqtt_client *client, enum umqtt_cmd cmd, void *params); 144 | 145 | #ifdef __cplusplus 146 | } 147 | #endif 148 | 149 | #endif 150 | 151 | 152 | -------------------------------------------------------------------------------- /inc/umqtt_cfg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-29 springcity the first version 9 | */ 10 | 11 | #ifndef _UMQTT_CFG_H__ 12 | #define _UMQTT_CFG_H__ 13 | 14 | #define PKG_UMQTT_PROTOCOL_NAME ("MQTC") 15 | #define PKG_UMQTT_PROTOCOL_NAME_LEN (rt_strlen(PKG_UMQTT_PROTOCOL_NAME)) 16 | #define PKG_UMQTT_PROTOCOL_LEVEL (4) /* MQTT3.1.1 ver_lvl:4; MQTT3.1 ver_lvl:3 */ 17 | 18 | #ifdef PKG_UMQTT_WILL_TOPIC_STRING 19 | #define UMQTT_WILL_TOPIC PKG_UMQTT_WILL_TOPIC_STRING 20 | #else 21 | #define UMQTT_WILL_TOPIC ("/umqtt/test") 22 | #endif 23 | 24 | #ifdef PKG_UMQTT_WILL_MESSAGE_STRING 25 | #define UMQTT_WILL_MWSSAGE PKG_UMQTT_WILL_MESSAGE_STRING 26 | #else 27 | #define UMQTT_WILL_MESSAGE ("Goodbye!") 28 | #endif 29 | 30 | #define UMQTT_INFO_DEF_THREAD_TICK 50 31 | #define UMQTT_MAX_PACKET_ID 65535 32 | #define UMQTT_INFO_DEF_UPLINK_TIMER_TICK 1000 33 | 34 | #ifndef PKG_UMQTT_PUBLISH_RECON_MAX 35 | #define PKG_UMQTT_PUBLISH_RECON_MAX 3 36 | #endif 37 | #ifndef PKG_UMQTT_QOS2_QUE_MAX 38 | #define PKG_UMQTT_QOS2_QUE_MAX 1 39 | #endif 40 | #define PKG_UMQTT_RECPUBREC_INTERVAL_TIME (2 * UMQTT_INFO_DEF_UPLINK_TIMER_TICK) 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /inc/umqtt_internal.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-29 springcity the first version 9 | */ 10 | 11 | #ifndef _UMQTT_INTERNAL_H__ 12 | #define _UMQTT_INTERNAL_H__ 13 | 14 | #include 15 | #include 16 | 17 | #include "umqtt_cfg.h" 18 | #include "umqtt.h" 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | enum umqtt_type 25 | { 26 | UMQTT_TYPE_RESERVED = 0, 27 | UMQTT_TYPE_CONNECT = 1, 28 | UMQTT_TYPE_CONNACK = 2, 29 | UMQTT_TYPE_PUBLISH = 3, 30 | UMQTT_TYPE_PUBACK = 4, 31 | UMQTT_TYPE_PUBREC = 5, 32 | UMQTT_TYPE_PUBREL = 6, 33 | UMQTT_TYPE_PUBCOMP = 7, 34 | UMQTT_TYPE_SUBSCRIBE = 8, 35 | UMQTT_TYPE_SUBACK = 9, 36 | UMQTT_TYPE_UNSUBSCRIBE = 10, 37 | UMQTT_TYPE_UNSUBACK = 11, 38 | UMQTT_TYPE_PINGREQ = 12, 39 | UMQTT_TYPE_PINGRESP = 13, 40 | UMQTT_TYPE_DISCONNECT = 14, 41 | }; 42 | 43 | enum umqtt_connack_retcode 44 | { 45 | UMQTT_CONNECTION_ACCEPTED = 0, 46 | UMQTT_UNNACCEPTABLE_PROTOCOL = 1, 47 | UMQTT_CLIENTID_REJECTED = 2, 48 | UMQTT_SERVER_UNAVAILABLE = 3, 49 | UMQTT_BAD_USERNAME_OR_PASSWORD = 4, 50 | UMQTT_NOT_AUTHORIZED = 5, 51 | 52 | }; 53 | 54 | #define UMQTT_MESSAGES_FIX_HEADER(TYPE, DUP, QOS, REATAIN) \ 55 | ((((TYPE) << 4) & 0xF0) | \ 56 | (((DUP) << 3) & 0x80) | \ 57 | (((QOS) << 1) % 0x06) | \ 58 | ((RETAIN) & 0x01)) 59 | 60 | union umqtt_pkgs_fix_header 61 | { 62 | rt_uint8_t byte; /* header */ 63 | struct { 64 | rt_uint8_t retain: 1; /* reserved bits */ 65 | rt_uint8_t qos: 2; /* QoS, 0-Almost once; 1-Alteast once; 2-Exactly once */ 66 | rt_uint8_t dup: 1; /* dup flag */ 67 | rt_uint8_t type: 4; /* MQTT packet type */ 68 | } bits; 69 | }; 70 | union umqtt_pkgs_connect_sign 71 | { 72 | rt_uint8_t connect_sign; 73 | struct { 74 | rt_uint8_t reserved: 1; /* reserved bits */ 75 | rt_uint8_t clean_session: 1; /* clean session bit */ 76 | rt_uint8_t will_flag: 1; /* will flag bit */ 77 | rt_uint8_t will_Qos: 2; /* will Qos bit */ 78 | rt_uint8_t will_retain: 1; /* will retain bit */ 79 | rt_uint8_t password_flag: 1; /* password flag bit */ 80 | rt_uint8_t username_flag: 1; /* user name flag bit */ 81 | } bits; 82 | }; 83 | union umqtt_pkgs_connack_sign 84 | { 85 | rt_uint8_t connack_sign; 86 | struct { 87 | rt_uint8_t sp: 1; /* current session bit */ 88 | rt_uint8_t reserved: 7; /* retain bit */ 89 | } bits; 90 | }; 91 | union pkgs_request_qos 92 | { 93 | rt_uint8_t request_qos; 94 | struct { 95 | rt_uint8_t qos: 2; /* QoS - 0/1/2 */ 96 | rt_uint8_t reserved: 6; /* retain bit */ 97 | } bits; 98 | }; 99 | struct sub_topic_filter 100 | { 101 | rt_uint16_t filter_len; /* topic filter length */ 102 | const char *topic_filter; /* topic name filter */ 103 | union pkgs_request_qos req_qos; /* request QoS */ 104 | }; 105 | struct unsub_topic_filter 106 | { 107 | rt_uint16_t filter_len; /* topic filter length */ 108 | const char *topic_filter; /* topic filter */ 109 | }; 110 | struct umqtt_pkgs_connect 111 | { 112 | /* variable header */ 113 | rt_uint16_t protocol_name_len; /* protocol name length */ 114 | const char *protocol_name; /* protocol name */ 115 | rt_uint8_t protocol_level; /* protocol level */ 116 | union umqtt_pkgs_connect_sign connect_flags; /* connect flags */ 117 | rt_uint16_t keepalive_interval_sec; /* keepalive interval second */ 118 | /* payload */ 119 | const char *client_id; /* client id */ 120 | const char *will_topic; /* will topic */ 121 | const char *will_message; /* will messagewill message */ 122 | const char *user_name; /* user name */ 123 | rt_uint16_t password_len; /* password length */ 124 | const char *password; /* password */ 125 | }; 126 | struct umqtt_pkgs_connack 127 | { 128 | /* variable header */ 129 | union umqtt_pkgs_connack_sign connack_flags; /* connect flags */ 130 | enum umqtt_connack_retcode ret_code; /* connect return code */ 131 | /* payload = NULL */ 132 | }; 133 | struct umqtt_pkgs_publish 134 | { 135 | /* variable header */ 136 | rt_uint16_t topic_name_len; /* topic name length */ 137 | const char *topic_name; /* topic name */ 138 | rt_uint16_t packet_id; /* packet id */ 139 | /* payload */ 140 | const char *payload; /* active payload */ 141 | /* not packet datas */ 142 | rt_uint32_t payload_len; /* retain payload length */ 143 | }; 144 | struct umqtt_pkgs_puback 145 | { 146 | /* variable header */ 147 | rt_uint16_t packet_id; /* packet id */ 148 | /* payload = NULL */ 149 | }; 150 | struct umqtt_pkgs_pubrec /* publish receive (QoS 2, step_1st) */ 151 | { 152 | /* variable header */ 153 | rt_uint16_t packet_id; /* packet id */ 154 | /* payload = NULL */ 155 | }; 156 | struct umqtt_pkgs_pubrel /* publish release (QoS 2, step_2nd) */ 157 | { 158 | /* variable header */ 159 | rt_uint16_t packet_id; /* packet id */ 160 | /* payload = NULL */ 161 | }; 162 | struct umqtt_pkgs_pubcomp /* publish complete (QoS 2, step_3rd) */ 163 | { 164 | /* variable header */ 165 | rt_uint16_t packet_id; /* packet id */ 166 | /* payload = NULL */ 167 | }; 168 | struct umqtt_pkgs_subscribe /* subscribe topic */ 169 | { 170 | /* variable header */ 171 | rt_uint16_t packet_id; /* packet id */ 172 | /* payload */ 173 | struct sub_topic_filter topic_filter[PKG_UMQTT_SUBRECV_DEF_LENGTH]; /* topic name filter arrays */ 174 | /* not payload datas */ 175 | rt_uint8_t topic_count; /* topic filter count */ 176 | }; 177 | struct umqtt_pkgs_suback /* subscribe ack */ 178 | { 179 | /* variable header */ 180 | rt_uint16_t packet_id; /* packet id */ 181 | /* payload */ 182 | rt_uint8_t ret_qos[PKG_UMQTT_SUBRECV_DEF_LENGTH]; /* return code - enum Qos - 0/1/2 */ 183 | /* not payload datas */ 184 | rt_uint8_t topic_count; /* topic name count */ 185 | }; 186 | struct umqtt_pkgs_unsubscribe /* unsubscribe */ 187 | { 188 | /* variable header */ 189 | rt_uint16_t packet_id; /* packet id */ 190 | /* payload */ 191 | struct unsub_topic_filter topic_filter[PKG_UMQTT_SUBRECV_DEF_LENGTH]; /* topic name filter arrays */ 192 | /* not payload datas */ 193 | rt_uint8_t topic_count; /* topic name count */ 194 | }; 195 | struct umqtt_pkgs_unsuback /* unsubscribe ack */ 196 | { 197 | /* variable header */ 198 | rt_uint16_t packet_id; /* packet id */ 199 | /* payload = NULL */ 200 | }; 201 | // struct pkgs_pingreq { } /* ping request = NULL */ 202 | // struct pkgs_pingresp { } /* ping response = NULL */ 203 | // struct pkgs_disconnect { } /* disconnect = NULL */ 204 | 205 | union umqtt_pkgs_msg /* mqtt message packet type */ 206 | { 207 | struct umqtt_pkgs_connect connect; /* connect */ 208 | struct umqtt_pkgs_connack connack; /* connack */ 209 | struct umqtt_pkgs_publish publish; /* publish */ 210 | struct umqtt_pkgs_puback puback; /* puback */ 211 | struct umqtt_pkgs_pubrec pubrec; /* publish receive (QoS 2, step_1st) */ 212 | struct umqtt_pkgs_pubrel pubrel; /* publish release (QoS 2, step_2nd) */ 213 | struct umqtt_pkgs_pubcomp pubcomp; /* publish complete (QoS 2, step_3rd) */ 214 | struct umqtt_pkgs_subscribe subscribe; /* subscribe topic */ 215 | struct umqtt_pkgs_suback suback; /* subscribe ack */ 216 | struct umqtt_pkgs_unsubscribe unsubscribe; /* unsubscribe topic */ 217 | struct umqtt_pkgs_unsuback unsuback; /* unsubscribe ack */ 218 | }; 219 | 220 | struct umqtt_msg 221 | { 222 | union umqtt_pkgs_fix_header header; /* fix header */ 223 | rt_uint32_t msg_len; /* message length */ 224 | union umqtt_pkgs_msg msg; /* retain payload message */ 225 | }; 226 | 227 | /* umqtt package datas */ 228 | int umqtt_encode(enum umqtt_type type, rt_uint8_t *send_buf, size_t send_len, struct umqtt_msg *message); 229 | /* umqtt unpackage datas */ 230 | int umqtt_decode(rt_uint8_t *recv_buf, size_t recv_buf_len, struct umqtt_msg *message); 231 | 232 | /* tcp/tls connect/disconnect/send/recv functions */ 233 | int umqtt_trans_connect(const char *uri, int *sock); 234 | int umqtt_trans_disconnect(int sock); 235 | int umqtt_trans_send(int sock, const rt_uint8_t *send_buf, rt_uint32_t buf_len, int timeout); 236 | int umqtt_trans_recv(int sock, rt_uint8_t *recv_buf, rt_uint32_t buf_len); 237 | 238 | /* compatible with paho MQTT embedded c needed to do processing */ 239 | typedef union umqtt_pkgs_fix_header MQTTHeader; 240 | typedef struct umqtt_pkgs_connect MQTTPacket_connectData; 241 | 242 | #define MQTTStrlen(c) ((c == NULL) ? 0 : strlen(c)) 243 | 244 | void umqtt_writeChar(unsigned char** pptr, char c); 245 | char umqtt_readChar(unsigned char** pptr); 246 | void umqtt_writeInt(unsigned char** pptr, int anInt); 247 | int umqtt_readInt(unsigned char** pptr); 248 | void umqtt_writeCString(unsigned char** pptr, const char* string); 249 | void umqtt_writeMQTTString(unsigned char** pptr, const char* string); 250 | int umqtt_readlenstring(int *str_len, char **p_string, unsigned char **pptr, unsigned char *enddata); 251 | int umqtt_pkgs_encode(unsigned char* buf, int length); 252 | int umqtt_pkgs_decode(int (*getcharfn)(unsigned char*, int), int* value); 253 | int umqtt_pkgs_len(int rem_len); 254 | 255 | #ifdef __cplusplus 256 | } 257 | #endif 258 | 259 | 260 | #endif 261 | -------------------------------------------------------------------------------- /samples/umqtt_sample.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-05-11 springcity the first version 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | 15 | #define DBG_TAG "umqtt.sample" 16 | 17 | #ifdef PKG_UMQTT_USING_DEBUG 18 | #define DBG_LVL DBG_LOG 19 | #else 20 | #define DBG_LVL DBG_INFO 21 | #endif /* MQTT_DEBUG */ 22 | #include 23 | 24 | #include "umqtt.h" 25 | #include "umqtt_internal.h" 26 | 27 | // #define MQTT_URI "tcp://test.mosquitto.org:1883" 28 | // #define MQTT_URI "tcp://mq.tongxinmao.com:18831" 29 | #define MQTT_URI "tcp://192.168.12.83:1883" 30 | #define MQTT_SUBTOPIC "/umqtt/test" 31 | #define MQTT_PUBTOPIC "/umqtt/test" 32 | #define MQTT_WILLMSG "Goodbye!" 33 | 34 | static int is_started = 0; 35 | static umqtt_client_t m_umqtt_client = RT_NULL; 36 | 37 | static int user_callback(struct umqtt_client *client, enum umqtt_evt event) 38 | { 39 | RT_ASSERT(client); 40 | 41 | switch(event) 42 | { 43 | case UMQTT_EVT_LINK: 44 | LOG_D(" user callback, event - link!"); 45 | break; 46 | case UMQTT_EVT_ONLINE: 47 | LOG_D(" user callback, event - online!"); 48 | break; 49 | case UMQTT_EVT_OFFLINE: 50 | LOG_D(" user callback, event - offline!"); 51 | break; 52 | case UMQTT_EVT_HEARTBEAT: 53 | LOG_D(" user callback, event - heartbeat!"); 54 | break; 55 | default: 56 | LOG_D(" user callback, event:%d", event); 57 | break; 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | static void umqtt_topic_recv_callback(struct umqtt_client *client, void *msg_data) 64 | { 65 | RT_ASSERT(client); 66 | RT_ASSERT(msg_data); 67 | struct umqtt_pkgs_publish *msg = (struct umqtt_pkgs_publish *)msg_data; 68 | LOG_D(" umqtt topic recv callback! name length: %d, name: %s, packet id: %d, payload len: %d ", 69 | msg->topic_name_len, 70 | msg->topic_name, 71 | msg->packet_id, 72 | // msg->payload, 73 | msg->payload_len); 74 | } 75 | 76 | static int umqtt_ex_start(int argc, char **argv) 77 | { 78 | LOG_D(" umqtt example start!"); 79 | 80 | if (argc != 1) 81 | { 82 | LOG_E(" umqtt_start --start a umqtt worker thread."); 83 | return -1; 84 | } 85 | 86 | if (is_started) 87 | { 88 | LOG_E(" umqtt client is already connected."); 89 | return -1; 90 | } 91 | 92 | struct umqtt_info umqtt_info = { 0 }; 93 | umqtt_info.uri = MQTT_URI; 94 | 95 | m_umqtt_client = umqtt_create(&umqtt_info); 96 | if (m_umqtt_client == RT_NULL) 97 | { 98 | LOG_E(" umqtt client create failed!"); 99 | return -1; 100 | } 101 | umqtt_control(m_umqtt_client, UMQTT_CMD_EVT_CB, user_callback); 102 | 103 | if (umqtt_start(m_umqtt_client) >= 0) 104 | { 105 | LOG_I(" umqtt start success!"); 106 | is_started = 1; 107 | } 108 | else 109 | { 110 | m_umqtt_client = RT_NULL; 111 | LOG_E(" umqtt start failed!"); 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | 118 | static int umqtt_ex_stop(int argc, char **argv) 119 | { 120 | LOG_D(" umqtt example stop!"); 121 | 122 | if (argc != 1) 123 | { 124 | LOG_D("umqtt_stop --stop umqtt worker thread and free mqtt client object.\n"); 125 | } 126 | 127 | is_started = 0; 128 | 129 | umqtt_stop(m_umqtt_client); 130 | umqtt_delete(m_umqtt_client); 131 | m_umqtt_client = RT_NULL; 132 | 133 | return 0; 134 | } 135 | 136 | static int str_to_int(const char *str) 137 | { 138 | int _ret = 0, _cnt = 0; 139 | int _strlen = strlen(str); 140 | for (_cnt = 0; _cnt < _strlen; _cnt++) { 141 | if ((str[_cnt] >= '0') && (str[_cnt] <= '9')) { 142 | _ret = _ret * 10 + str[_cnt] - '0'; 143 | } 144 | } 145 | 146 | return _ret; 147 | } 148 | 149 | static int umqtt_ex_publish(int argc, char **argv) 150 | { 151 | LOG_D(" umqtt example publish!"); 152 | 153 | if (is_started == 0) 154 | { 155 | LOG_E("mqtt client is not connected."); 156 | return -1; 157 | } 158 | 159 | if (argc == 4) { 160 | int _len = str_to_int(argv[2]); 161 | // LOG_D(" *argv[0]: %s, *argv[1]: %s, *argv[2]: %d, *argv[3]: %s ", argv[0], argv[1], _len, argv[3]); 162 | 163 | umqtt_publish(m_umqtt_client, ((_len > UMQTT_QOS2) ? UMQTT_QOS1 : _len), argv[1], argv[3], strlen(argv[3]) + 1, 100); 164 | } else { 165 | LOG_E("mqtt_publish <0/1/2> [message] --mqtt publish message to specified topic.\n"); 166 | return -1; 167 | } 168 | 169 | return 0; 170 | } 171 | 172 | static int umqtt_ex_subscribe(int argc, char **argv) 173 | { 174 | LOG_D(" umqtt example subscribe!"); 175 | if (argc != 2) 176 | { 177 | LOG_E("umqtt_subscribe [topic] --send an umqtt subscribe packet and wait for suback before returning.\n"); 178 | return -1; 179 | } 180 | 181 | if (is_started == 0) 182 | { 183 | LOG_E("umqtt client is not connected."); 184 | return -1; 185 | } 186 | 187 | return umqtt_subscribe(m_umqtt_client, argv[1], UMQTT_QOS1, umqtt_topic_recv_callback); 188 | } 189 | 190 | 191 | static int umqtt_ex_unsubscribe(int argc, char **argv) 192 | { 193 | LOG_D(" umqtt example unsubscribe!"); 194 | 195 | if (argc != 2) 196 | { 197 | LOG_E("mqtt_unsubscribe [topic] --send an mqtt unsubscribe packet and wait for suback before returning.\n"); 198 | return -1; 199 | } 200 | 201 | if (is_started == 0) 202 | { 203 | LOG_E("mqtt client is not connected."); 204 | return -1; 205 | } 206 | 207 | return umqtt_unsubscribe(m_umqtt_client, argv[1]); 208 | } 209 | 210 | #ifdef FINSH_USING_MSH 211 | MSH_CMD_EXPORT(umqtt_ex_start, startup umqtt client); 212 | MSH_CMD_EXPORT(umqtt_ex_stop, stop umqtt client); 213 | MSH_CMD_EXPORT(umqtt_ex_publish, umqtt publish message to specified topic); 214 | MSH_CMD_EXPORT(umqtt_ex_subscribe, umqtt subscribe topic); 215 | MSH_CMD_EXPORT(umqtt_ex_unsubscribe, umqtt unsubscribe topic); 216 | #endif 217 | 218 | -------------------------------------------------------------------------------- /src/pkgs/umqtt_internal.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2021, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2022-05-24 Administrator the first version 9 | */ 10 | #include "umqtt_internal.h" 11 | 12 | void umqtt_writeChar(unsigned char** pptr, char c) 13 | { 14 | **pptr = c; 15 | (*pptr)++; 16 | } 17 | 18 | char umqtt_readChar(unsigned char** pptr) 19 | { 20 | char c = **pptr; 21 | (*pptr)++; 22 | return c; 23 | } 24 | 25 | void umqtt_writeInt(unsigned char** pptr, int anInt) 26 | { 27 | **pptr = (unsigned char)(anInt / 256); 28 | (*pptr)++; 29 | **pptr = (unsigned char)(anInt % 256); 30 | (*pptr)++; 31 | } 32 | 33 | int umqtt_readInt(unsigned char** pptr) 34 | { 35 | unsigned char* ptr = *pptr; 36 | int len = 256*(*ptr) + (*(ptr+1)); 37 | *pptr += 2; 38 | return len; 39 | } 40 | 41 | void umqtt_writeCString(unsigned char** pptr, const char* string) 42 | { 43 | int len = 0; 44 | if (string) 45 | { 46 | len = strlen(string); 47 | umqtt_writeInt(pptr, len); 48 | memcpy(*pptr, string, len); 49 | *pptr += len; 50 | } 51 | } 52 | 53 | void umqtt_writeMQTTString(unsigned char** pptr, const char* string) 54 | { 55 | int len = 0; 56 | if (string) 57 | { 58 | len = strlen(string); 59 | umqtt_writeInt(pptr, len); 60 | memcpy(*pptr, string, len); 61 | *pptr += len; 62 | } 63 | else 64 | { 65 | umqtt_writeInt(pptr, 0); 66 | } 67 | } 68 | 69 | int umqtt_readlenstring(int *str_len, char **p_string, unsigned char **pptr, unsigned char *enddata) 70 | { 71 | int rc = 0; 72 | 73 | if (enddata - (*pptr) > 1) 74 | { 75 | *str_len = umqtt_readInt(pptr); 76 | if (&(*pptr)[*str_len] <= enddata) 77 | { 78 | *p_string = (char *)*pptr; 79 | *pptr += *str_len; 80 | rc = 1; 81 | } 82 | } 83 | return rc; 84 | } 85 | 86 | int umqtt_pkgs_encode(unsigned char* buf, int length) 87 | { 88 | int rc = 0; 89 | do { 90 | char d = length % 128; 91 | length /= 128; 92 | /* if there are more digits to encode, set the top bit of this digit */ 93 | if (length > 0) 94 | d |= 0x80; 95 | buf[rc++] = d; 96 | } while (length > 0); 97 | return rc; 98 | } 99 | 100 | int umqtt_pkgs_decode(int (*getcharfn)(unsigned char*, int), int* value) 101 | { 102 | unsigned char c; 103 | int multiplier = 1; 104 | int len = 0; 105 | #define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 106 | 107 | *value = 0; 108 | do 109 | { 110 | int rc = UMQTT_READ_ERROR; 111 | 112 | if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) 113 | { 114 | rc = UMQTT_READ_ERROR; /* bad data */ 115 | goto exit; 116 | } 117 | rc = (*getcharfn)(&c, 1); 118 | if (rc != 1) 119 | goto exit; 120 | *value += (c & 127) * multiplier; 121 | multiplier *= 128; 122 | } while ((c & 128) != 0); 123 | exit: 124 | return len; 125 | } 126 | 127 | int umqtt_pkgs_len(int rem_len) 128 | { 129 | rem_len += 1; /* header byte */ 130 | 131 | /* now remaining_length field */ 132 | if (rem_len < 128) 133 | rem_len += 1; 134 | else if (rem_len < 16384) 135 | rem_len += 2; 136 | else if (rem_len < 2097151) 137 | rem_len += 3; 138 | else 139 | rem_len += 4; 140 | 141 | return rem_len; 142 | } 143 | -------------------------------------------------------------------------------- /src/pkgs/umqtt_pkgs_decode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-05-06 springcity the first version 9 | */ 10 | 11 | #include "umqtt_cfg.h" 12 | #include "umqtt_internal.h" 13 | #include "umqtt.h" 14 | 15 | #define DBG_TAG "umqtt.decode" 16 | 17 | #ifdef PKG_UMQTT_USING_DEBUG 18 | #define DBG_LVL DBG_LOG 19 | #else 20 | #define DBG_LVL DBG_INFO 21 | #endif /* MQTT_DEBUG */ 22 | #include 23 | 24 | static unsigned char* bufptr; 25 | 26 | static int bufchar(unsigned char* c, int count) 27 | { 28 | int i; 29 | for (i = 0; i < count; ++i) 30 | *c = *bufptr++; 31 | return count; 32 | } 33 | 34 | static int umqtt_pkgs_decodeBuf(unsigned char* buf, int* value) 35 | { 36 | bufptr = buf; 37 | return umqtt_pkgs_decode(bufchar, value); 38 | } 39 | 40 | static int MQTTDeserialize_publish(struct umqtt_msg *pub_msg, rt_uint8_t *buf, int buflen) 41 | { 42 | unsigned char *curdata = buf; 43 | unsigned char *enddata = NULL; 44 | int rc = 0; 45 | int mylen = 0; 46 | 47 | pub_msg->header.byte = umqtt_readChar(&curdata); 48 | if (pub_msg->header.bits.type != UMQTT_TYPE_PUBLISH) 49 | { 50 | LOG_E(" decode datas, is not publish type! type:%d", pub_msg->header.bits.type); 51 | rc = UMQTT_DECODE_ERROR; 52 | goto exit; 53 | } 54 | 55 | curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); 56 | enddata = curdata + mylen; 57 | 58 | if (!umqtt_readlenstring((int *)&(pub_msg->msg.publish.topic_name_len), (char **)(&(pub_msg->msg.publish.topic_name)), &curdata, enddata) 59 | || (enddata - curdata < 0)) 60 | { 61 | LOG_E(" decode publish, topic name error!"); 62 | rc = UMQTT_DECODE_ERROR; 63 | goto exit; 64 | } 65 | 66 | if (pub_msg->header.bits.qos > 0) 67 | pub_msg->msg.publish.packet_id = umqtt_readInt(&curdata); 68 | 69 | pub_msg->msg.publish.payload_len = enddata - curdata; 70 | pub_msg->msg.publish.payload = (const char *)curdata; 71 | exit: 72 | return rc; 73 | } 74 | 75 | static int MQTTDeserialize_ack(struct umqtt_msg *puback_msg, rt_uint8_t *buf, int buflen) 76 | { 77 | unsigned char *curdata = buf; 78 | unsigned char *enddata = NULL; 79 | int rc = 0; 80 | int mylen; 81 | 82 | puback_msg->header.byte = umqtt_readChar(&curdata); 83 | 84 | curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); 85 | enddata = curdata + mylen; 86 | 87 | if (enddata - curdata < 2) 88 | { 89 | rc = UMQTT_DECODE_ERROR; 90 | goto exit; 91 | } 92 | puback_msg->msg.puback.packet_id = umqtt_readInt(&curdata); 93 | 94 | exit: 95 | return rc; 96 | } 97 | 98 | static int umqtt_connack_decode(struct umqtt_pkgs_connack *connack_msg, rt_uint8_t* buf, int buflen) 99 | { 100 | MQTTHeader header = {0}; 101 | unsigned char* curdata = buf; 102 | unsigned char* enddata = NULL; 103 | int rc = 0; 104 | int mylen; 105 | 106 | header.byte = umqtt_readChar(&curdata); 107 | if (header.bits.type != UMQTT_TYPE_CONNACK) 108 | { 109 | rc = UMQTT_FAILED; 110 | LOG_E(" not connack type!"); 111 | goto exit; 112 | } 113 | 114 | curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); /* read remaining length */ 115 | enddata = curdata + mylen; 116 | if (enddata - curdata < 2) 117 | { 118 | LOG_D(" enddata:%d, curdata:%d, mylen:%d", enddata, curdata, mylen); 119 | goto exit; 120 | } 121 | 122 | connack_msg->connack_flags.connack_sign = umqtt_readChar(&curdata); 123 | connack_msg->ret_code = umqtt_readChar(&curdata); 124 | exit: 125 | return rc; 126 | } 127 | 128 | 129 | // static int umqtt_pingresp_decode(); 130 | /** 131 | * parse the publish datas 132 | * 133 | * @param publish_msg the output datas 134 | * @param buf the input datas need to parse 135 | * @param buflen the input buffer length 136 | * 137 | * @return <0: failed or other error 138 | * =0: success 139 | */ 140 | static int umqtt_publish_decode(struct umqtt_msg *publish_msg, rt_uint8_t *buf, int buflen) 141 | { 142 | return MQTTDeserialize_publish(publish_msg, buf, buflen); 143 | } 144 | /** 145 | * parse the puback datas 146 | * 147 | * @param puback_msg the output datas 148 | * @param buf the input datas need to parse 149 | * @param buflen the input buffer length 150 | * 151 | * @return <0: failed or other error 152 | * =0: success 153 | */ 154 | static int umqtt_puback_decode(struct umqtt_msg *puback_msg, rt_uint8_t *buf, int buflen) 155 | { 156 | return MQTTDeserialize_ack(puback_msg, buf, buflen); 157 | } 158 | // static int umqtt_pubrec_decode(); // 159 | // static int umqtt_pubrel_decode(); // 160 | // static int umqtt_pubcomp_decode(); // 161 | 162 | /** 163 | * parse the suback datas 164 | * 165 | * @param suback_msg the output datas 166 | * @param buf the input datas need to parse 167 | * @param buflen the input buffer length 168 | * 169 | * @return <0: failed or other error 170 | * =0: success 171 | */ 172 | static int umqtt_suback_decode(struct umqtt_pkgs_suback *suback_msg, rt_uint8_t *buf, int buflen) 173 | { 174 | MQTTHeader header = {0}; 175 | unsigned char* curdata = buf; 176 | unsigned char* enddata = NULL; 177 | int rc = 0; 178 | int mylen; 179 | 180 | header.byte = umqtt_readChar(&curdata); 181 | if (header.bits.type != UMQTT_TYPE_SUBACK) 182 | { 183 | rc = UMQTT_FAILED; 184 | goto exit; 185 | } 186 | 187 | curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); /* read remaining length */ 188 | enddata = curdata + mylen; 189 | if (enddata - curdata < 2) 190 | { 191 | rc = UMQTT_DECODE_ERROR; 192 | goto exit; 193 | } 194 | 195 | suback_msg->packet_id = umqtt_readInt(&curdata); 196 | 197 | suback_msg->topic_count = 0; 198 | while (curdata < enddata) 199 | { 200 | if (suback_msg->topic_count > PKG_UMQTT_SUBRECV_DEF_LENGTH) 201 | { 202 | rc = UMQTT_FAILED; 203 | goto exit; 204 | } 205 | suback_msg->ret_qos[(suback_msg->topic_count)++] = umqtt_readChar(&curdata); 206 | } 207 | 208 | exit: 209 | return rc; 210 | } 211 | 212 | /** 213 | * parse the unsuback datas 214 | * 215 | * @param unsuback_msg the output datas 216 | * @param buf the input datas need to parse 217 | * @param buflen the input buffer length 218 | * 219 | * @return <0: failed or other error 220 | * =0: success 221 | */ 222 | static int umqtt_unsuback_decode(struct umqtt_pkgs_unsuback *unsuback_msg, rt_uint8_t *buf, int buflen) 223 | { 224 | unsigned char* curdata = buf; 225 | unsigned char* enddata = NULL; 226 | int rc = 0; 227 | int mylen; 228 | 229 | curdata += (rc = umqtt_pkgs_decodeBuf(curdata, &mylen)); /* read remaining length */ 230 | enddata = curdata + mylen; 231 | 232 | if (enddata - curdata < 2) 233 | { 234 | rc = UMQTT_DECODE_ERROR; 235 | goto exit; 236 | } 237 | unsuback_msg->packet_id = umqtt_readInt(&curdata); 238 | 239 | exit: 240 | return rc; 241 | } 242 | 243 | /** 244 | * parse the data according to the format 245 | * 246 | * @param recv_buf the input, the raw buffer data, of the correct length determined by the remaining length field 247 | * @param recv_buf_len the input, the length in bytes of the data in the supplied buffer 248 | * @param message the output datas 249 | * 250 | * @return <0: failed or other error 251 | * =0: success 252 | */ 253 | int umqtt_decode(rt_uint8_t *recv_buf, size_t recv_buf_len, struct umqtt_msg *message) 254 | { 255 | int _ret = 0; 256 | rt_uint8_t* curdata = recv_buf; 257 | enum umqtt_type type; 258 | if (message == RT_NULL) 259 | { 260 | _ret = UMQTT_INPARAMS_NULL; 261 | LOG_E(" umqtt decode inparams null!"); 262 | goto exit; 263 | } 264 | 265 | message->header.byte = umqtt_readChar(&curdata); 266 | type = message->header.bits.type; 267 | 268 | switch (type) 269 | { 270 | case UMQTT_TYPE_CONNACK: 271 | _ret = umqtt_connack_decode(&(message->msg.connack), recv_buf, recv_buf_len); 272 | break; 273 | case UMQTT_TYPE_PUBLISH: 274 | _ret = umqtt_publish_decode(message, recv_buf, recv_buf_len); 275 | break; 276 | case UMQTT_TYPE_PUBACK: 277 | _ret = umqtt_puback_decode(message, recv_buf, recv_buf_len); 278 | break; 279 | case UMQTT_TYPE_PUBREC: 280 | // _ret = umqtt_pubrec_decode(); 281 | break; 282 | case UMQTT_TYPE_PUBREL: 283 | // _ret = umqtt_pubrel_decode(); 284 | break; 285 | case UMQTT_TYPE_PUBCOMP: 286 | // _ret = umqtt_pubcomp_decode(); 287 | break; 288 | case UMQTT_TYPE_SUBACK: 289 | _ret = umqtt_suback_decode(&(message->msg.suback), recv_buf, recv_buf_len); 290 | break; 291 | case UMQTT_TYPE_UNSUBACK: 292 | _ret = umqtt_unsuback_decode(&(message->msg.unsuback), recv_buf, recv_buf_len); 293 | break; 294 | case UMQTT_TYPE_PINGRESP: 295 | // _ret = umqtt_pingresp_encode(); 296 | break; 297 | default: 298 | break; 299 | } 300 | exit: 301 | return _ret; 302 | } 303 | -------------------------------------------------------------------------------- /src/pkgs/umqtt_pkgs_encode.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-30 springcity the first version 9 | */ 10 | 11 | #include "umqtt_cfg.h" 12 | #include "umqtt_internal.h" 13 | 14 | #define DBG_TAG "umqtt.encode" 15 | 16 | #ifdef PKG_UMQTT_USING_DEBUG 17 | #define DBG_LVL DBG_LOG 18 | #else 19 | #define DBG_LVL DBG_INFO 20 | #endif /* MQTT_DEBUG */ 21 | #include 22 | 23 | static int MQTTSerialize_connectLength(MQTTPacket_connectData* options) 24 | { 25 | int len = 0; 26 | if (options->protocol_level == 3) /* MQTT V3.1 */ 27 | len = 12; /* variable depending on MQTT or MQIsdp */ 28 | else if (options->protocol_level == 4) /* MQTT V3.1.1 */ 29 | len = 10; 30 | 31 | len += MQTTStrlen(options->client_id) + 2; 32 | if (options->connect_flags.bits.will_flag) 33 | len += MQTTStrlen(options->will_topic) + 2 + MQTTStrlen(options->will_message) + 2; 34 | 35 | if (options->connect_flags.bits.password_flag) 36 | { 37 | len += MQTTStrlen(options->password) + 2; 38 | if (options->connect_flags.bits.username_flag) 39 | len += MQTTStrlen(options->user_name) + 2; 40 | } 41 | return len; 42 | } 43 | 44 | static int MQTTSerialize_subscribeLength(struct umqtt_pkgs_subscribe *params) 45 | { 46 | int _cnt = 0; 47 | int len = 2; 48 | if (params && (params->topic_count > 0)) 49 | { 50 | for (_cnt = 0; _cnt < params->topic_count; _cnt++) 51 | len += 2 + MQTTStrlen(params->topic_filter[_cnt].topic_filter) + 1; 52 | } 53 | else 54 | len = 0; 55 | return len; 56 | } 57 | 58 | static int MQTTSerialize_unsubscribeLength(struct umqtt_pkgs_unsubscribe *params) 59 | { 60 | int i; 61 | int len = 2; /* packetid */ 62 | if (params && (params->topic_count > 0)) 63 | { 64 | for (i = 0; i < params->topic_count; ++i) 65 | len += 2 + MQTTStrlen(params->topic_filter[i].topic_filter);/* length + topic*/ 66 | } 67 | else 68 | len = 0; 69 | return len; 70 | } 71 | 72 | static int MQTTSerialize_publishLength(int qos, struct umqtt_pkgs_publish *params) 73 | { 74 | int len = 0; 75 | len += 2 + params->topic_name_len + params->payload_len; 76 | if (qos > 0) 77 | len += 2; 78 | 79 | return len; 80 | } 81 | 82 | static int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) 83 | { 84 | unsigned char *ptr = buf; 85 | MQTTHeader header = { 0 }; 86 | int len = 0; 87 | int rc = -1; 88 | 89 | if (umqtt_pkgs_len(len = MQTTSerialize_connectLength(options)) > buflen) 90 | { 91 | rc = UMQTT_BUFFER_TOO_SHORT; 92 | goto exit; 93 | } 94 | 95 | header.byte = 0; 96 | header.bits.type = UMQTT_TYPE_CONNECT; 97 | umqtt_writeChar(&ptr, header.byte); /* write header */ 98 | 99 | ptr += umqtt_pkgs_encode(ptr, len); /* write remaining length */ 100 | 101 | if (options->protocol_level == 4) 102 | { 103 | umqtt_writeCString(&ptr, "MQTT"); 104 | umqtt_writeChar(&ptr, (char) 4); 105 | } 106 | else 107 | { 108 | umqtt_writeCString(&ptr, "MQIsdp"); 109 | umqtt_writeChar(&ptr, (char) 3); 110 | } 111 | 112 | umqtt_writeChar(&ptr, options->connect_flags.connect_sign); 113 | umqtt_writeInt(&ptr, options->keepalive_interval_sec); 114 | // umqtt_writeInt(&ptr, PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME); /* ping interval max, 0xffff */ 115 | umqtt_writeMQTTString(&ptr, options->client_id); 116 | if (options->connect_flags.bits.will_flag) 117 | { 118 | umqtt_writeMQTTString(&ptr, options->will_topic); 119 | umqtt_writeMQTTString(&ptr, options->will_message); 120 | } 121 | 122 | if (options->connect_flags.bits.username_flag) 123 | umqtt_writeMQTTString(&ptr, options->user_name); 124 | if (options->connect_flags.bits.password_flag) 125 | umqtt_writeMQTTString(&ptr, options->password); 126 | 127 | rc = ptr - buf; 128 | 129 | exit: 130 | return rc; 131 | } 132 | 133 | static int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) 134 | { 135 | MQTTHeader header = { 0 }; 136 | int rc = -1; 137 | unsigned char *ptr = buf; 138 | 139 | if (buflen < 2) 140 | { 141 | rc = UMQTT_BUFFER_TOO_SHORT; 142 | goto exit; 143 | } 144 | header.byte = 0; 145 | header.bits.type = packettype; 146 | umqtt_writeChar(&ptr, header.byte); /* write header */ 147 | 148 | ptr += umqtt_pkgs_encode(ptr, 0); /* write remaining length */ 149 | rc = ptr - buf; 150 | exit: 151 | return rc; 152 | } 153 | 154 | static int MQTTSerialize_subscribe(unsigned char* buf, int buflen, struct umqtt_pkgs_subscribe *params) 155 | { 156 | unsigned char *ptr = buf; 157 | MQTTHeader header = { 0 }; 158 | int rem_len = 0; 159 | int rc = 0; 160 | int i = 0; 161 | 162 | if (umqtt_pkgs_len(rem_len = MQTTSerialize_subscribeLength(params)) > buflen) 163 | { 164 | rc = UMQTT_BUFFER_TOO_SHORT; 165 | goto exit; 166 | } 167 | 168 | header.byte = 0; 169 | header.bits.type = UMQTT_TYPE_SUBSCRIBE; 170 | header.bits.dup = 0; 171 | header.bits.qos = 1; 172 | umqtt_writeChar(&ptr, header.byte); /* write header */ 173 | 174 | ptr += umqtt_pkgs_encode(ptr, rem_len); /* write remaining length */; 175 | 176 | umqtt_writeInt(&ptr, params->packet_id); 177 | 178 | for (i = 0; i < params->topic_count; ++i) 179 | { 180 | umqtt_writeMQTTString(&ptr, params->topic_filter[i].topic_filter); 181 | umqtt_writeChar(&ptr, params->topic_filter[i].req_qos.request_qos); 182 | } 183 | 184 | rc = ptr - buf; 185 | 186 | exit: 187 | return rc; 188 | } 189 | 190 | static int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, struct umqtt_pkgs_unsubscribe *params) 191 | { 192 | unsigned char *ptr = buf; 193 | MQTTHeader header = { 0 }; 194 | int rem_len = 0; 195 | int rc = 0; 196 | int i = 0; 197 | 198 | if (umqtt_pkgs_len(rem_len = MQTTSerialize_unsubscribeLength(params)) > buflen) 199 | { 200 | rc = UMQTT_BUFFER_TOO_SHORT; 201 | goto exit; 202 | } 203 | 204 | header.byte = 0; 205 | header.bits.type = UMQTT_TYPE_UNSUBSCRIBE; 206 | header.bits.dup = 0; 207 | header.bits.qos = 1; 208 | umqtt_writeChar(&ptr, header.byte); /* write header */ 209 | 210 | ptr += umqtt_pkgs_encode(ptr, rem_len); /* write remaining length */; 211 | 212 | umqtt_writeInt(&ptr, params->packet_id); 213 | 214 | for (i = 0; i < params->topic_count; ++i) 215 | umqtt_writeCString(&ptr, params->topic_filter[i].topic_filter); 216 | 217 | rc = ptr - buf; 218 | exit: 219 | return rc; 220 | } 221 | 222 | static int MQTTSerialize_publish(unsigned char* buf, int buflen, int dup, int qos, struct umqtt_pkgs_publish *message) 223 | { 224 | unsigned char *ptr = buf; 225 | MQTTHeader header = { 0 }; 226 | int rem_len = 0; 227 | int rc = 0; 228 | 229 | if (umqtt_pkgs_len(rem_len = MQTTSerialize_publishLength(qos, message)) > buflen) 230 | { 231 | rc = UMQTT_BUFFER_TOO_SHORT; 232 | goto exit; 233 | } 234 | 235 | header.bits.type = UMQTT_TYPE_PUBLISH; 236 | header.bits.dup = dup; 237 | header.bits.qos = qos; 238 | umqtt_writeChar(&ptr, header.byte); 239 | ptr += umqtt_pkgs_encode(ptr, rem_len); 240 | 241 | umqtt_writeCString(&ptr, message->topic_name); 242 | 243 | if (qos > 0) 244 | umqtt_writeInt(&ptr, message->packet_id); 245 | 246 | memcpy(ptr, message->payload, message->payload_len); 247 | ptr += message->payload_len; 248 | 249 | rc = ptr - buf; 250 | exit: 251 | return rc; 252 | } 253 | 254 | static int MQTTSerialize_ack(unsigned char *buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) 255 | { 256 | MQTTHeader header = { 0 }; 257 | int rc = 0; 258 | unsigned char *ptr = buf; 259 | 260 | if (buflen < 4) 261 | { 262 | rc = UMQTT_BUFFER_TOO_SHORT; 263 | goto exit; 264 | } 265 | header.bits.type = packettype; 266 | header.bits.dup = dup; 267 | header.bits.qos = (packettype == UMQTT_TYPE_PUBREL) ? 1 : 0; 268 | umqtt_writeChar(&ptr, header.byte); 269 | 270 | ptr += umqtt_pkgs_encode(ptr, 2); 271 | umqtt_writeInt(&ptr, packetid); 272 | rc = ptr - buf; 273 | exit: 274 | return rc; 275 | } 276 | 277 | /** 278 | * packaging the connect data 279 | * 280 | * @param buf the output send buf, result of the package 281 | * @param buflen the output send buffer length 282 | * @param params the input, connect params 283 | * 284 | * @return <=0: failed or other error 285 | * >0: package data length 286 | */ 287 | static int umqtt_connect_encode(rt_uint8_t *send_buf, size_t send_len, struct umqtt_pkgs_connect *params) 288 | { 289 | return MQTTSerialize_connect(send_buf, send_len, params); 290 | } 291 | 292 | /** 293 | * packaging the disconnect data 294 | * 295 | * @param buf the output send buf, result of the package 296 | * @param buflen the output send buffer length 297 | * 298 | * @return <=0: failed or other error 299 | * >0: package data length 300 | */ 301 | static int umqtt_disconnect_encode(rt_uint8_t *send_buf, size_t send_len) 302 | { 303 | return MQTTSerialize_zero(send_buf, send_len, UMQTT_TYPE_DISCONNECT); 304 | } 305 | 306 | /** 307 | * packaging the pingreq data 308 | * 309 | * @param buf the output send buf, result of the package 310 | * @param buflen the output send buffer length 311 | * 312 | * @return <=0: failed or other error 313 | * >0: package data length 314 | */ 315 | static int umqtt_pingreq_encode(rt_uint8_t *send_buf, size_t send_len) 316 | { 317 | return MQTTSerialize_zero(send_buf, send_len, UMQTT_TYPE_PINGREQ); 318 | } 319 | 320 | /** 321 | * packaging the puback data 322 | * 323 | * @param buf the output send buf, result of the package 324 | * @param buflen the output send buffer length 325 | * @param message the input message 326 | * 327 | * @return <=0: failed or other error 328 | * >0: package data length 329 | */ 330 | static int umqtt_publish_encode(unsigned char *buf, int buflen, int dup, int qos, struct umqtt_pkgs_publish *message) 331 | { 332 | return MQTTSerialize_publish(buf, buflen, dup, qos, message); 333 | } 334 | 335 | /** 336 | * packaging the puback data 337 | * 338 | * @param buf the output send buf, result of the package 339 | * @param buflen the output send buffer length 340 | * @param packetid the input pakcet id in message 341 | * 342 | * @return <=0: failed or other error 343 | * >0: package data length 344 | */ 345 | static int umqtt_puback_encode(unsigned char *buf, int buflen, unsigned short packetid) 346 | { 347 | return MQTTSerialize_ack(buf, buflen, UMQTT_TYPE_PUBACK, 0, packetid); 348 | } 349 | // static int umqtt_pubrec_encode(); 350 | 351 | /** 352 | * packaging the pubcomp data 353 | * 354 | * @param buf the output send buf, result of the package 355 | * @param buflen the output send buffer length 356 | * @param dup the input Duplicate delivery of a PUBLISH packet 357 | * @param packetid the input pakcet id in message 358 | * 359 | * @return <=0: failed or other error 360 | * >0: package data length 361 | */ 362 | static int umqtt_pubrel_encode(unsigned char *buf, int buflen, unsigned char dup, unsigned short packetid) 363 | { 364 | return MQTTSerialize_ack(buf, buflen, UMQTT_TYPE_PUBREL, dup, packetid); 365 | } 366 | 367 | /** 368 | * packaging the pubcomp data 369 | * 370 | * @param buf the output send buf, result of the package 371 | * @param buf_len the output send buffer length 372 | * @param packetid the input pakcet id in message 373 | * 374 | * @return <=0: failed or other error 375 | * >0: package data length 376 | */ 377 | static int umqtt_pubcomp_encode(unsigned char *buf, int buflen, unsigned short packetid) 378 | { 379 | return MQTTSerialize_ack(buf, buflen, UMQTT_TYPE_PUBCOMP, 0, packetid); 380 | } 381 | 382 | /** 383 | * packaging the subscribe data 384 | * 385 | * @param send_buf the output send buf, result of the package 386 | * @param send_len the output send buffer length 387 | * @param params the input message 388 | * 389 | * @return <=0: failed or other error 390 | * >0: package data length 391 | */ 392 | static int umqtt_subscribe_encode(rt_uint8_t *send_buf, size_t send_len, struct umqtt_pkgs_subscribe *params) 393 | { 394 | return MQTTSerialize_subscribe(send_buf, send_len, params); 395 | } 396 | 397 | /** 398 | * packaging the unsubscribe data 399 | * 400 | * @param send_buf the output send buf, result of the package 401 | * @param send_len the output send buffer length 402 | * @param params the input message 403 | * 404 | * @return <=0: failed or other error 405 | * >0: package data length 406 | */ 407 | static int umqtt_unsubscribe_encode(rt_uint8_t *send_buf, size_t send_len, struct umqtt_pkgs_unsubscribe *params) 408 | { 409 | return MQTTSerialize_unsubscribe(send_buf, send_len, params); 410 | } 411 | 412 | /** 413 | * packaging the data according to the format 414 | * 415 | * @param type the input packaging type 416 | * @param send_buf the output send buf, result of the package 417 | * @param send_len the output send buffer length 418 | * @param message the input message 419 | * 420 | * @return <=0: failed or other error 421 | * >0: package data length 422 | */ 423 | int umqtt_encode(enum umqtt_type type, rt_uint8_t *send_buf, size_t send_len, struct umqtt_msg *message) 424 | { 425 | int _ret = 0; 426 | switch (type) 427 | { 428 | case UMQTT_TYPE_CONNECT: 429 | _ret = umqtt_connect_encode(send_buf, send_len, &(message->msg.connect)); 430 | break; 431 | case UMQTT_TYPE_PUBLISH: 432 | _ret = umqtt_publish_encode(send_buf, send_len, message->header.bits.dup, message->header.bits.qos, &(message->msg.publish)); 433 | break; 434 | case UMQTT_TYPE_PUBACK: 435 | _ret = umqtt_puback_encode(send_buf, send_len, message->msg.puback.packet_id); 436 | break; 437 | case UMQTT_TYPE_PUBREC: 438 | // _ret = umqtt_pubrec_encode(); 439 | break; 440 | case UMQTT_TYPE_PUBREL: 441 | _ret = umqtt_pubrel_encode(send_buf, send_len, message->header.bits.dup, message->msg.pubrel.packet_id); 442 | break; 443 | case UMQTT_TYPE_PUBCOMP: 444 | _ret = umqtt_pubcomp_encode(send_buf, send_len, message->msg.pubcomp.packet_id); 445 | break; 446 | case UMQTT_TYPE_SUBSCRIBE: 447 | _ret = umqtt_subscribe_encode(send_buf, send_len, &(message->msg.subscribe)); 448 | break; 449 | case UMQTT_TYPE_UNSUBSCRIBE: 450 | _ret = umqtt_unsubscribe_encode(send_buf, send_len, &(message->msg.unsubscribe)); 451 | break; 452 | case UMQTT_TYPE_PINGREQ: 453 | _ret = umqtt_pingreq_encode(send_buf, send_len); 454 | break; 455 | case UMQTT_TYPE_DISCONNECT: 456 | _ret = umqtt_disconnect_encode(send_buf, send_len); 457 | break; 458 | default: 459 | break; 460 | } 461 | return _ret; 462 | } 463 | 464 | -------------------------------------------------------------------------------- /src/trans/umqtt_transport.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-05-07 springcity the first version 9 | */ 10 | 11 | #include 12 | 13 | #include "umqtt_cfg.h" 14 | #include "umqtt_internal.h" 15 | #include "umqtt.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define DBG_TAG "umqtt.transport" 25 | 26 | #ifdef PKG_UMQTT_USING_DEBUG 27 | #define DBG_LVL DBG_LOG 28 | #else 29 | #define DBG_LVL DBG_INFO 30 | #endif /* MQTT_DEBUG */ 31 | #include 32 | 33 | #ifdef UMQTT_USING_TLS 34 | #define UMQTT_SOCKET_PROTOCOL PROTOCOL_TLS 35 | #else 36 | #define UMQTT_SOCKET_PROTOCOL 0 37 | #endif 38 | 39 | /* 40 | * resolve server address 41 | * @param server the server sockaddress 42 | * @param url the input URL address. 43 | * @param host_addr the buffer pointer to save server host address 44 | * @param request the pointer to point the request url, for example, /index.html 45 | * 46 | * @return 0 on resolve server address OK, others failed 47 | * 48 | * URL example: 49 | * tcp://192.168.10.151:1883 50 | * tls://192.168.10.151:61614 51 | * tcp://[fe80::20c:29ff:fe9a:a07e]:1883 52 | * tls://[fe80::20c:29ff:fe9a:a07e]:61614 53 | * ws://echo.websocket.org:80 websocket 54 | */ 55 | static int umqtt_resolve_uri(const char *umqtt_uri, struct addrinfo **res) 56 | { 57 | int rc = 0; 58 | int uri_len = 0, host_addr_len = 0, port_len = 0; 59 | char *ptr; 60 | char port_str[6] = { 0 }; /* default port of mqtt(http) */ 61 | 62 | struct addrinfo hint; 63 | int ret; 64 | 65 | const char *host_addr = 0; 66 | char *host_addr_new = RT_NULL; 67 | const char *uri = umqtt_uri; 68 | uri_len = strlen(uri); 69 | 70 | /* strip protocol(tcp or ssl) */ 71 | if (strncmp(uri, "tcp://", 6) == 0) 72 | { 73 | host_addr = uri + 6; 74 | } 75 | else if (strncmp(uri, "ssl://", 6) == 0) 76 | { 77 | host_addr = uri + 6; 78 | // } else if (strncmp(uri, "ws://", 5) == 0) { 79 | } 80 | else 81 | { 82 | rc = UMQTT_INPARAMS_NULL; 83 | goto exit; 84 | } 85 | 86 | if (host_addr[0] == '[') /* ipv6 address */ 87 | { 88 | host_addr += 1; 89 | ptr = strstr(host_addr, "]"); 90 | if (!ptr) { 91 | rc = UMQTT_INPARAMS_NULL; 92 | goto exit; 93 | } 94 | host_addr_len = ptr - host_addr; 95 | if ((host_addr_len < 1) || (host_addr_len > uri_len)) { 96 | rc = UMQTT_INPARAMS_NULL; 97 | goto exit; 98 | } 99 | 100 | port_len = uri_len - 6 - host_addr_len - 3; 101 | if ((port_len >= 6) || (port_len < 1)) { 102 | rc = UMQTT_INPARAMS_NULL; 103 | goto exit; 104 | } 105 | 106 | strncpy(port_str, host_addr + host_addr_len + 2, port_len); 107 | port_str[port_len] = '\0'; 108 | // LOG_D("ipv6 address port: %s", port_str); 109 | } 110 | else /* ipv4 or domain. */ 111 | { 112 | ptr = strstr(host_addr, ":"); 113 | if (!ptr) 114 | { 115 | rc = UMQTT_INPARAMS_NULL; 116 | goto exit; 117 | } 118 | host_addr_len = ptr - host_addr; 119 | if ((host_addr_len < 1) || (host_addr_len > uri_len)) 120 | { 121 | rc = UMQTT_INPARAMS_NULL; 122 | goto exit; 123 | } 124 | port_len = uri_len - 6 - host_addr_len - 1; 125 | if ((port_len >= 6) || (port_len < 1)) 126 | { 127 | rc = UMQTT_INPARAMS_NULL; 128 | goto exit; 129 | } 130 | 131 | strncpy(port_str, host_addr + host_addr_len + 1, port_len); 132 | port_str[port_len] = '\0'; 133 | // LOG_D("ipv4 address port: %s", port_str); 134 | } 135 | 136 | /* get host addr ok. */ 137 | { 138 | /* resolve the host name. */ 139 | host_addr_new = rt_malloc(host_addr_len + 1); 140 | 141 | if (!host_addr_new) 142 | { 143 | rc = UMQTT_MEM_FULL; 144 | goto exit; 145 | } 146 | 147 | rt_memcpy(host_addr_new, host_addr, host_addr_len); 148 | host_addr_new[host_addr_len] = '\0'; 149 | 150 | rt_memset(&hint, 0, sizeof(hint)); 151 | 152 | ret = getaddrinfo(host_addr_new, port_str, &hint, res); 153 | if (ret != 0) 154 | { 155 | LOG_E("getaddrinfo err: %d '%s'", ret, host_addr_new); 156 | rc = UMQTT_FAILED; 157 | goto exit; 158 | } 159 | } 160 | 161 | exit: 162 | if (host_addr_new != RT_NULL) 163 | { 164 | rt_free(host_addr_new); 165 | host_addr_new = RT_NULL; 166 | } 167 | return rc; 168 | } 169 | 170 | /** 171 | * TCP/TLS Connection Complete for configured transport 172 | * 173 | * @param uri the input server URI address 174 | * @param sock the output socket 175 | * 176 | * @return <0: failed or other error 177 | * =0: success 178 | */ 179 | int umqtt_trans_connect(const char *uri, int *sock) 180 | { 181 | int _ret = 0; 182 | struct addrinfo *addr_res = RT_NULL; 183 | 184 | *sock = -1; 185 | _ret = umqtt_resolve_uri(uri, &addr_res); 186 | if ((_ret < 0) || (addr_res == RT_NULL)) 187 | { 188 | LOG_E("resolve uri err"); 189 | _ret = UMQTT_FAILED; 190 | goto exit; 191 | } 192 | 193 | if ((*sock = socket(addr_res->ai_family, SOCK_STREAM, UMQTT_SOCKET_PROTOCOL)) < 0) 194 | { 195 | LOG_E("create socket error!"); 196 | _ret = UMQTT_FAILED; 197 | goto exit; 198 | } 199 | 200 | _ret = ioctlsocket(*sock, FIONBIO, 0); 201 | if (_ret < 0) 202 | { 203 | LOG_E(" iocontrol socket error!"); 204 | _ret = UMQTT_FAILED; 205 | goto exit; 206 | } 207 | 208 | if ((_ret = connect(*sock, addr_res->ai_addr, addr_res->ai_addrlen)) < 0) 209 | { 210 | LOG_E(" connect err!"); 211 | closesocket(*sock); 212 | *sock = -1; 213 | _ret = UMQTT_FAILED; 214 | goto exit; 215 | } 216 | 217 | exit: 218 | if (addr_res) { 219 | freeaddrinfo(addr_res); 220 | addr_res = RT_NULL; 221 | } 222 | return _ret; 223 | } 224 | 225 | /** 226 | * TCP/TLS transport disconnection requests on configured transport. 227 | * 228 | * @param sock the input socket 229 | * 230 | * @return <0: failed or other error 231 | * =0: success 232 | */ 233 | int umqtt_trans_disconnect(int sock) 234 | { 235 | int _ret = 0; 236 | _ret = closesocket(sock); 237 | if (_ret < 0) 238 | return -errno; 239 | return _ret; 240 | } 241 | 242 | /** 243 | * TCP/TLS send datas on configured transport. 244 | * 245 | * @param sock the input socket 246 | * @param send_buf the input, transport datas buffer 247 | * @param buf_len the input, transport datas buffer length 248 | * @param timeout the input, tcp/tls transport timeout 249 | * 250 | * @return <0: failed or other error 251 | * =0: success 252 | */ 253 | int umqtt_trans_send(int sock, const rt_uint8_t *send_buf, rt_uint32_t buf_len, int timeout) 254 | { 255 | int _ret = 0; 256 | rt_uint32_t offset = 0U; 257 | while (offset < buf_len) 258 | { 259 | _ret = send(sock, send_buf + offset, buf_len - offset, 0); 260 | if (_ret < 0) 261 | return -errno; 262 | offset += _ret; 263 | } 264 | 265 | return _ret; 266 | } 267 | 268 | /** 269 | * TCP/TLS receive datas on configured transport. 270 | * 271 | * @param sock the input socket 272 | * @param recv_buf the output, receive datas buffer 273 | * @param buf_len the input, receive datas buffer length 274 | * 275 | * @return <=0: failed or other error 276 | * >0: receive datas length 277 | */ 278 | int umqtt_trans_recv(int sock, rt_uint8_t *recv_buf, rt_uint32_t buf_len) 279 | { 280 | return recv(sock, recv_buf, buf_len, 0); 281 | // return read(sock, recv_buf, buf_len); 282 | } 283 | -------------------------------------------------------------------------------- /src/umqtt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2022, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-05-08 springcity the first version 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include "umqtt_cfg.h" 17 | #include "umqtt_internal.h" 18 | #include "umqtt.h" 19 | 20 | #define DBG_TAG "umqtt" 21 | 22 | #ifdef PKG_UMQTT_USING_DEBUG 23 | #define DBG_LVL DBG_LOG 24 | #else 25 | #define DBG_LVL DBG_INFO 26 | #endif /* MQTT_DEBUG */ 27 | #include 28 | 29 | #define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 30 | 31 | #define UMQTT_CLIENT_LOCK(CLIENT) rt_mutex_take(CLIENT->lock_client, RT_WAITING_FOREVER) 32 | #define UMQTT_CLIENT_UNLOCK(CLIENT) rt_mutex_release(CLIENT->lock_client) 33 | 34 | #define UMQTT_SET_CONNECT_FLAGS(user_name_flag, password_flag, will_retain, will_qos, will_flag, clean_session, reserved) \ 35 | (((user_name_flag & 0x01) << 7) | \ 36 | ((password_flag & 0x01) << 6) | \ 37 | ((will_retain & 0x01) << 5) | \ 38 | ((will_qos & 0x01) << 3) | \ 39 | ((will_flag & 0x01) << 2) | \ 40 | ((clean_session & 0x01) << 1) | \ 41 | (reserved & 0x01)) 42 | #define UMQTT_DEF_CONNECT_FLAGS (UMQTT_SET_CONNECT_FLAGS(0,0,0,0,0,1,0)) 43 | 44 | struct umqtt_msg_ack 45 | { 46 | rt_uint16_t packet_id; 47 | rt_uint8_t msg_type; 48 | }; 49 | 50 | struct umqtt_qos2_msg 51 | { 52 | rt_uint16_t topic_name_len; 53 | char *topic_name; 54 | rt_uint16_t packet_id; 55 | char *payload; 56 | rt_uint32_t payload_len; 57 | rt_list_t next_list; 58 | }; 59 | 60 | struct umqtt_pubrec_msg 61 | { 62 | int cnt; 63 | int packet_id; 64 | int next_tick; /* next tick*/ 65 | }; 66 | 67 | struct umqtt_client 68 | { 69 | int sock; /* socket sock */ 70 | enum umqtt_client_state connect_state; /* mqtt client status */ 71 | 72 | struct umqtt_info mqtt_info; /* user mqtt config information */ 73 | rt_uint8_t reconnect_count; /* mqtt client reconnect count */ 74 | rt_uint8_t keepalive_count; /* mqtt keepalive count */ 75 | rt_uint32_t pingreq_last_tick; /* mqtt ping request message */ 76 | rt_uint32_t uplink_next_tick; /* uplink (include: publish/subscribe/unsub/connect/ping/... client->broker) next tick(ping) */ 77 | rt_uint32_t uplink_last_tick; /* uplink (include: publish/subscribe/unsub/connect/ping/... client->broker) next tick(ping) */ 78 | rt_uint32_t reconnect_next_tick; /* client unlink, reconnect next tick */ 79 | rt_uint32_t reconnect_last_tick; /* client unlink, reconnect last tick */ 80 | 81 | rt_uint8_t *send_buf, *recv_buf; /* send data buffer, receive data buffer */ 82 | rt_size_t send_len, recv_len; /* send datas length, receive datas length */ 83 | 84 | rt_uint16_t packet_id; /* mqtt packages id */ 85 | 86 | rt_mutex_t lock_client; /* mqtt client lock */ 87 | rt_mq_t msg_queue; /* fro receive thread with other thread to communicate message */ 88 | 89 | rt_timer_t uplink_timer; /* client send message to broker manager timer */ 90 | 91 | int sub_recv_list_len; /* subscribe topic, receive topicname to deal datas */ 92 | rt_list_t sub_recv_list; /* subscribe information list header */ 93 | 94 | rt_list_t qos2_msg_list; /* qos2 message list */ 95 | struct umqtt_pubrec_msg pubrec_msg[PKG_UMQTT_QOS2_QUE_MAX]; /* pubrec message array */ 96 | 97 | umqtt_user_callback user_handler; /* user handler */ 98 | 99 | void *user_data; /* user data */ 100 | rt_thread_t task_handle; /* task thread */ 101 | 102 | rt_list_t list; /* list header */ 103 | }; 104 | 105 | enum tick_item 106 | { 107 | UPLINK_LAST_TICK = 0, 108 | UPLINK_NEXT_TICK = 1, 109 | RECON_LAST_TICK = 2, 110 | RECON_NEXT_TICK = 3, 111 | }; 112 | 113 | static int umqtt_handle_readpacket(struct umqtt_client *client); 114 | static void umqtt_deliver_message(struct umqtt_client *client, 115 | const char *topic_name, int len, 116 | struct umqtt_pkgs_publish *msg); 117 | 118 | static int get_next_packetID(struct umqtt_client *client) 119 | { 120 | return client->packet_id = (client->packet_id == UMQTT_MAX_PACKET_ID) ? 1 : (client->packet_id + 1); 121 | } 122 | 123 | static int add_one_qos2_msg(struct umqtt_client *client, struct umqtt_pkgs_publish *pdata) 124 | { 125 | int _ret = UMQTT_OK; 126 | struct umqtt_qos2_msg *msg = RT_NULL; 127 | if ((pdata) && (client)) 128 | { 129 | if (rt_list_len(&client->qos2_msg_list) >= PKG_UMQTT_QOS2_QUE_MAX) 130 | { 131 | LOG_W(" qos2 message list is over !"); 132 | } 133 | else 134 | { 135 | msg = (struct umqtt_qos2_msg *)rt_calloc(1, sizeof(struct umqtt_qos2_msg)); 136 | if (msg) 137 | { 138 | msg->topic_name_len = pdata->topic_name_len; 139 | msg->packet_id = pdata->packet_id; 140 | msg->payload_len = pdata->payload_len; 141 | if ((pdata->topic_name != RT_NULL) && (pdata->topic_name_len != 0)) 142 | { 143 | msg->topic_name = (char *)rt_calloc(1, sizeof(char) * pdata->topic_name_len); 144 | if (msg->topic_name) 145 | { 146 | rt_strncpy(msg->topic_name, pdata->topic_name, msg->topic_name_len); 147 | } 148 | else 149 | { 150 | _ret = UMQTT_MEM_FULL; 151 | LOG_E(" calloc umqtt qos2 message topic name failed! memory full! "); 152 | } 153 | } 154 | if ((pdata->payload != RT_NULL) && (pdata->payload_len != 0)) 155 | { 156 | msg->payload = (char *)rt_calloc(1, sizeof(char) * pdata->payload_len); 157 | if (msg->payload) 158 | { 159 | rt_strncpy(msg->payload, pdata->payload, msg->payload_len); 160 | } 161 | else 162 | { 163 | _ret = UMQTT_MEM_FULL; 164 | LOG_E(" calloc umqtt qos2 message payload failed! memory full! "); 165 | } 166 | } 167 | } 168 | else 169 | { 170 | _ret = UMQTT_MEM_FULL; 171 | LOG_E(" calloc umqtt qos2 message failed! "); 172 | } 173 | } 174 | } 175 | else 176 | { 177 | _ret = UMQTT_INPARAMS_NULL; 178 | LOG_E(" add qos2 message failed! input params is valid! "); 179 | } 180 | 181 | if (_ret == UMQTT_OK) 182 | { 183 | rt_list_insert_after(&client->qos2_msg_list, &msg->next_list); 184 | } 185 | 186 | //_exit: 187 | if (_ret == UMQTT_MEM_FULL) 188 | { 189 | if (msg) 190 | { 191 | if (msg->topic_name) { rt_free(msg->topic_name); msg->topic_name = RT_NULL; } 192 | if (msg->payload) { rt_free(msg->payload); msg->payload = RT_NULL; } 193 | rt_free(msg); msg = RT_NULL; 194 | } 195 | } 196 | 197 | return _ret; 198 | } 199 | 200 | static int qos2_publish_delete(struct umqtt_client *client, int packet_id) 201 | { 202 | int _ret = UMQTT_OK, tmp_ret = 0; 203 | struct umqtt_qos2_msg *p_msg = RT_NULL; 204 | rt_list_t *node = RT_NULL; 205 | struct umqtt_pkgs_publish publish_msg = { 0 }; 206 | /* call publish, delete packet id delete */ 207 | 208 | if ((tmp_ret == rt_list_isempty(&client->qos2_msg_list)) == 0) 209 | { 210 | rt_list_for_each(node, &client->qos2_msg_list) 211 | { 212 | p_msg = rt_list_entry(node, struct umqtt_qos2_msg, next_list); 213 | if (p_msg->packet_id == packet_id) 214 | { 215 | LOG_D(" qos2, deliver message! topic nme: %s ", p_msg->topic_name); 216 | publish_msg.topic_name_len = p_msg->topic_name_len; 217 | publish_msg.payload_len = p_msg->payload_len; 218 | publish_msg.packet_id = p_msg->packet_id; 219 | publish_msg.topic_name = p_msg->topic_name; 220 | publish_msg.payload = p_msg->payload; 221 | umqtt_deliver_message(client, p_msg->topic_name, p_msg->topic_name_len, &publish_msg); 222 | if (p_msg->topic_name) { rt_free(p_msg->topic_name); p_msg->topic_name = RT_NULL; } 223 | if (p_msg->payload) { rt_free(p_msg->payload); p_msg->payload = RT_NULL; } 224 | rt_list_remove(&(p_msg->next_list)); 225 | rt_free(p_msg); p_msg = RT_NULL; 226 | goto _exit; 227 | } 228 | } 229 | } 230 | _exit: 231 | return _ret; 232 | } 233 | 234 | static int add_one_pubrec_msg(struct umqtt_client *client, int packet_id) 235 | { 236 | int _ret = UMQTT_OK, _cnt = 0; 237 | 238 | for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++) 239 | { 240 | if (client->pubrec_msg[_cnt].cnt == -1) 241 | { 242 | UMQTT_CLIENT_LOCK(client); 243 | client->pubrec_msg[_cnt].cnt = PKG_UMQTT_PUBLISH_RECON_MAX; 244 | client->pubrec_msg[_cnt].packet_id = packet_id; 245 | client->pubrec_msg[_cnt].next_tick = rt_tick_get() + PKG_UMQTT_RECPUBREC_INTERVAL_TIME; 246 | UMQTT_CLIENT_UNLOCK(client); 247 | break; 248 | } 249 | } 250 | if (_cnt >= PKG_UMQTT_QOS2_QUE_MAX) 251 | { 252 | LOG_W(" add one pubrec message is full! "); 253 | _ret = UMQTT_MEM_FULL; 254 | } 255 | return _ret; 256 | } 257 | 258 | static int clear_one_pubrec_msg(struct umqtt_client *client, int packet_id) 259 | { 260 | int _ret = UMQTT_OK, _cnt = 0; 261 | 262 | for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++) 263 | { 264 | if (client->pubrec_msg[_cnt].packet_id == packet_id) 265 | { 266 | UMQTT_CLIENT_LOCK(client); 267 | client->pubrec_msg[_cnt].cnt = -1; 268 | client->pubrec_msg[_cnt].packet_id = -1; 269 | client->pubrec_msg[_cnt].next_tick = -1; 270 | UMQTT_CLIENT_UNLOCK(client); 271 | break; 272 | } 273 | } 274 | 275 | return _ret; 276 | } 277 | 278 | static int pubrec_cycle_callback(struct umqtt_client *client) 279 | { 280 | int _ret = UMQTT_OK, _cnt = 0; 281 | struct umqtt_msg encode_msg = { 0 }; 282 | 283 | /* search pubrec packet id, encode, transport, change next tick time */ 284 | for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++) 285 | { 286 | if ((client->pubrec_msg[_cnt].cnt != -1) 287 | && (client->pubrec_msg[_cnt].packet_id != -1) 288 | && (client->pubrec_msg[_cnt].next_tick != -1)) 289 | { 290 | if (client->pubrec_msg[_cnt].next_tick <= rt_tick_get()) 291 | { 292 | UMQTT_CLIENT_LOCK(client); 293 | client->pubrec_msg[_cnt].cnt--; 294 | client->pubrec_msg[_cnt].next_tick = rt_tick_get() + PKG_UMQTT_RECPUBREC_INTERVAL_TIME; 295 | UMQTT_CLIENT_UNLOCK(client); 296 | 297 | if (client->pubrec_msg[_cnt].cnt < 0) 298 | { 299 | qos2_publish_delete(client, client->pubrec_msg[_cnt].packet_id); 300 | LOG_W(" pubrec failed!"); 301 | UMQTT_CLIENT_LOCK(client); 302 | client->pubrec_msg[_cnt].cnt = -1; 303 | client->pubrec_msg[_cnt].packet_id = -1; 304 | client->pubrec_msg[_cnt].next_tick = -1; 305 | UMQTT_CLIENT_UNLOCK(client); 306 | } 307 | else 308 | { 309 | rt_memset(&encode_msg, 0, sizeof(struct umqtt_msg)); 310 | encode_msg.header.bits.qos = UMQTT_QOS2; 311 | encode_msg.header.bits.dup = 0; 312 | encode_msg.header.bits.type = UMQTT_TYPE_PUBREC; 313 | encode_msg.msg.pubrec.packet_id = client->pubrec_msg[_cnt].packet_id; 314 | 315 | _ret = umqtt_encode(encode_msg.header.bits.type, client->send_buf, client->mqtt_info.send_size, 316 | &encode_msg); 317 | if (_ret < 0) 318 | { 319 | _ret = UMQTT_ENCODE_ERROR; 320 | LOG_E(" pubrec failed!"); 321 | goto _exit; 322 | } 323 | client->send_len = _ret; 324 | 325 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 326 | client->mqtt_info.send_timeout); 327 | if (_ret < 0) 328 | { 329 | _ret = UMQTT_SEND_FAILED; 330 | LOG_E(" trans send failed!"); 331 | goto _exit; 332 | } 333 | } 334 | } 335 | 336 | } 337 | } 338 | 339 | _exit: 340 | return _ret; 341 | } 342 | 343 | static void set_connect_status(struct umqtt_client *client, enum umqtt_client_state status) 344 | { 345 | UMQTT_CLIENT_LOCK(client); 346 | client->connect_state = status; 347 | UMQTT_CLIENT_UNLOCK(client); 348 | } 349 | 350 | static void set_uplink_recon_tick(struct umqtt_client *client, enum tick_item item) 351 | { 352 | RT_ASSERT(client); 353 | switch (item) 354 | { 355 | case UPLINK_LAST_TICK: 356 | UMQTT_CLIENT_LOCK(client); 357 | client->uplink_last_tick = rt_tick_get(); 358 | UMQTT_CLIENT_UNLOCK(client); 359 | break; 360 | case UPLINK_NEXT_TICK: 361 | UMQTT_CLIENT_LOCK(client); 362 | client->uplink_next_tick = client->mqtt_info.keepalive_interval * 1000 + rt_tick_get(); 363 | UMQTT_CLIENT_UNLOCK(client); 364 | break; 365 | case RECON_LAST_TICK: 366 | UMQTT_CLIENT_LOCK(client); 367 | client->reconnect_last_tick = rt_tick_get(); 368 | UMQTT_CLIENT_UNLOCK(client); 369 | break; 370 | case RECON_NEXT_TICK: 371 | UMQTT_CLIENT_LOCK(client); 372 | client->reconnect_next_tick = client->mqtt_info.reconnect_interval * 1000 + rt_tick_get(); 373 | UMQTT_CLIENT_UNLOCK(client); 374 | break; 375 | default: 376 | LOG_W(" set tick item outof set! value: %d", item); 377 | break; 378 | } 379 | }; 380 | 381 | static int umqtt_connect(struct umqtt_client *client, int block) 382 | { 383 | int _ret = 0, _length = 0, _cnt = 0; 384 | struct umqtt_msg encode_msg = { 0 }; 385 | //struct umqtt_msg_ack msg_ack = { 0 }; 386 | RT_ASSERT(client); 387 | 388 | _reconnect: 389 | client->reconnect_count++; 390 | if (client->reconnect_count > client->mqtt_info.reconnect_max_num) 391 | { 392 | _ret = UMQTT_RECONNECT_FAILED; 393 | client->reconnect_count = 0; 394 | LOG_E(" reconnect failed!"); 395 | goto exit; 396 | } 397 | _ret = umqtt_trans_connect(client->mqtt_info.uri, &(client->sock)); 398 | if (_ret < 0) 399 | { 400 | _ret = UMQTT_SOCK_CONNECT_FAILED; 401 | LOG_E(" umqtt connect, transport connect failed!"); 402 | goto disconnect; 403 | } 404 | 405 | encode_msg.msg.connect.protocol_name_len = PKG_UMQTT_PROTOCOL_NAME_LEN; 406 | encode_msg.msg.connect.protocol_name = PKG_UMQTT_PROTOCOL_NAME; 407 | encode_msg.msg.connect.protocol_level = PKG_UMQTT_PROTOCOL_LEVEL; 408 | encode_msg.msg.connect.connect_flags.connect_sign = UMQTT_DEF_CONNECT_FLAGS; 409 | #ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 410 | encode_msg.msg.connect.keepalive_interval_sec = ((client->mqtt_info.connect_keepalive_sec == 0) ? PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME : client->mqtt_info.connect_keepalive_sec); 411 | #else 412 | encode_msg.msg.connect.keepalive_interval_sec = PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME; 413 | #endif 414 | encode_msg.msg.connect.client_id = client->mqtt_info.client_id; 415 | encode_msg.msg.connect.will_topic = client->mqtt_info.lwt_topic; 416 | encode_msg.msg.connect.will_message = client->mqtt_info.lwt_message; 417 | encode_msg.msg.connect.user_name = client->mqtt_info.user_name; 418 | if (client->mqtt_info.user_name) 419 | { 420 | encode_msg.msg.connect.connect_flags.bits.username_flag = 1; 421 | } 422 | encode_msg.msg.connect.password = client->mqtt_info.password; 423 | if (client->mqtt_info.password) { 424 | encode_msg.msg.connect.connect_flags.bits.password_flag = 1; 425 | encode_msg.msg.connect.password_len = rt_strlen(client->mqtt_info.password); 426 | } 427 | 428 | _length = umqtt_encode(UMQTT_TYPE_CONNECT, client->send_buf, client->mqtt_info.send_size, &encode_msg); 429 | if (_length <= 0) 430 | { 431 | _ret = UMQTT_ENCODE_ERROR; 432 | LOG_E(" connect encode failed!"); 433 | goto exit; 434 | } 435 | client->send_len = _length; 436 | 437 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout); 438 | if (_ret < 0) 439 | { 440 | _ret = UMQTT_SEND_FAILED; 441 | LOG_E(" connect trans send failed! errno:0x%04x", errno); 442 | goto exit; 443 | } 444 | 445 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 446 | 447 | if (block != 0) 448 | { 449 | _ret = umqtt_handle_readpacket(client); 450 | if (_ret == UMQTT_FIN_ACK) 451 | { 452 | LOG_D(" server fin ack, connect failed!"); 453 | goto disconnect; 454 | } 455 | else if (_ret < 0) 456 | { 457 | _ret = UMQTT_READ_ERROR; 458 | LOG_E(" connect trans recv failed!"); 459 | goto exit; 460 | } 461 | 462 | while (1) 463 | { 464 | if (_cnt++ >= (client->mqtt_info.connect_time << 1)) 465 | { 466 | _ret = UMQTT_TIMEOUT; 467 | LOG_E(" connect recv message timeout!"); 468 | goto exit; 469 | } 470 | else 471 | { 472 | if (client->connect_state == UMQTT_CS_LINKED) 473 | { 474 | _cnt = 0; 475 | _ret = UMQTT_OK; 476 | LOG_I(" connect success!"); 477 | goto exit; 478 | } 479 | } 480 | rt_thread_mdelay(500); 481 | } 482 | } 483 | _ret = UMQTT_OK; 484 | LOG_D(" connect sucess!"); 485 | 486 | disconnect: 487 | if ((_ret == UMQTT_FIN_ACK) || (_ret == UMQTT_SOCK_CONNECT_FAILED)) 488 | { 489 | _ret = UMQTT_FIN_ACK; 490 | umqtt_trans_disconnect(client->sock); 491 | client->sock = -1; 492 | rt_thread_delay(RT_TICK_PER_SECOND * 5); 493 | LOG_E(" server send fin ack, need to reconnect!"); 494 | goto _reconnect; 495 | } 496 | 497 | exit: 498 | if (_ret < 0) 499 | { 500 | set_connect_status(client, UMQTT_CS_DISCONNECT); /* reconnect failed */ 501 | LOG_W(" set client status disconnect! "); 502 | } 503 | return _ret; 504 | } 505 | 506 | static int umqtt_disconnect(struct umqtt_client *client) 507 | { 508 | int _ret = 0, _length = 0; 509 | RT_ASSERT(client); 510 | 511 | _length = umqtt_encode(UMQTT_TYPE_DISCONNECT, client->send_buf, client->mqtt_info.send_size, RT_NULL); 512 | if (_length < 0) 513 | { 514 | _ret = UMQTT_ENCODE_ERROR; 515 | LOG_E(" disconnect encode failed!"); 516 | goto exit; 517 | } 518 | client->send_len = _length; 519 | 520 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 521 | client->mqtt_info.send_timeout); 522 | if (_ret < 0) 523 | { 524 | _ret = UMQTT_SEND_FAILED; 525 | LOG_E(" disconnect trans send failed!"); 526 | goto exit; 527 | } 528 | _ret = UMQTT_OK; 529 | exit: 530 | return _ret; 531 | } 532 | 533 | static rt_uint8_t topicname_is_matched(char *topic_filter, char *topic_name, int len) 534 | { 535 | RT_ASSERT(topic_filter); 536 | RT_ASSERT(topic_name); 537 | 538 | char *cur_f = topic_filter; 539 | char *cur_n = topic_name; 540 | char *cur_n_end = cur_n + len; 541 | 542 | while (*cur_f && (cur_n < cur_n_end)) 543 | { 544 | if ((*cur_n == '/') && (*cur_f != '/')) 545 | break; 546 | if ((*cur_f != '+') && (*cur_f != '#') && (*cur_f != *cur_n)) 547 | break; 548 | if (*cur_f == '+') 549 | { 550 | char *nextpos = cur_n + 1; 551 | while (nextpos < cur_n_end && *nextpos != '/') 552 | nextpos = ++cur_n + 1; 553 | } 554 | else if (*cur_f == '#') 555 | { 556 | cur_n = cur_n_end - 1; 557 | } 558 | cur_f++; 559 | cur_n++; 560 | }; 561 | 562 | return ((cur_n == cur_n_end) && (*cur_f == '\0')); 563 | } 564 | 565 | static void umqtt_deliver_message(struct umqtt_client *client, 566 | const char *topic_name, int len, 567 | struct umqtt_pkgs_publish *msg) 568 | { 569 | RT_ASSERT(client); 570 | RT_ASSERT(topic_name); 571 | RT_ASSERT(msg); 572 | 573 | struct subtop_recv_handler *p_subtop = RT_NULL; 574 | rt_list_t *node = RT_NULL; 575 | rt_list_for_each(node, &client->sub_recv_list) 576 | { 577 | p_subtop = rt_list_entry(node, struct subtop_recv_handler, next_list); 578 | if ((p_subtop->topicfilter) 579 | && (topicname_is_matched(p_subtop->topicfilter, (char *)topic_name, len))) 580 | { 581 | if (p_subtop->callback != RT_NULL) 582 | { 583 | p_subtop->callback(client, msg); 584 | } 585 | } 586 | } 587 | } 588 | 589 | static int umqtt_readpacket(struct umqtt_client *client, unsigned char *buf, int len, int timeout) 590 | { 591 | int bytes = 0, _ret = 0; 592 | rt_tick_t timeout_tick = (rt_tick_from_millisecond(timeout) & (RT_TICK_MAX >> 1)); 593 | rt_tick_t end_tick = rt_tick_get() + timeout_tick; 594 | 595 | RT_ASSERT(client); 596 | RT_ASSERT(buf); 597 | if (len == 0) 598 | { 599 | _ret = UMQTT_OK; 600 | return _ret; 601 | } 602 | 603 | while (bytes < len) 604 | { 605 | _ret = umqtt_trans_recv(client->sock, &buf[bytes], (size_t)(len - bytes)); 606 | if (_ret == -1) 607 | { 608 | if (!(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)) 609 | { 610 | _ret = UMQTT_READ_FAILED; 611 | LOG_E(" readpacket error! errno(%d)", errno); 612 | goto exit; 613 | } 614 | } 615 | else if (_ret == 0) 616 | { 617 | _ret = UMQTT_FIN_ACK; 618 | LOG_W(" readpacket return 0!"); 619 | goto exit; 620 | } 621 | else 622 | { 623 | bytes += _ret; 624 | } 625 | if ((rt_tick_get() - end_tick) < (RT_TICK_MAX >> 1)) 626 | { 627 | _ret = UMQTT_READ_TIMEOUT; 628 | LOG_W(" readpacket timeout! now_tick(%ld), endtick(%ld), timeout(%ld) ", 629 | rt_tick_get(), end_tick, timeout_tick); 630 | goto exit; 631 | } 632 | } 633 | _ret = UMQTT_OK; 634 | 635 | exit: 636 | return _ret; 637 | } 638 | 639 | static int umqtt_handle_readpacket(struct umqtt_client *client) 640 | { 641 | int _ret = 0, _onedata = 0, _cnt = 0, _loop_cnt = 0;// _remain_len = 0; 642 | int _temp_ret = 0; 643 | int _pkt_len = 0; 644 | int _multiplier = 1; 645 | int _pkt_type = 0; 646 | struct umqtt_msg decode_msg = { 0 }; 647 | struct umqtt_msg_ack msg_ack = { 0 }; 648 | struct umqtt_msg encode_msg = { 0 }; 649 | RT_ASSERT(client); 650 | 651 | /* 1. read the heade type */ 652 | _temp_ret = umqtt_trans_recv(client->sock, client->recv_buf, 1); 653 | if (_temp_ret <= 0) 654 | { 655 | _ret = UMQTT_FIN_ACK; 656 | LOG_W(" server fin ack! connect failed! need to reconnect!"); 657 | goto exit; 658 | } 659 | 660 | /* 2. read length */ 661 | do { 662 | if (++_cnt > MAX_NO_OF_REMAINING_LENGTH_BYTES) 663 | { 664 | _ret = UMQTT_FAILED; 665 | LOG_E(" umqtt packet length error!"); 666 | goto exit; 667 | } 668 | _ret = umqtt_readpacket(client, (unsigned char *)&_onedata, 1, client->mqtt_info.recv_time_ms); 669 | if (_ret == UMQTT_FIN_ACK) 670 | { 671 | LOG_W(" server fin ack! connect failed! need to reconnect!"); 672 | goto exit; 673 | } 674 | else if (_ret != UMQTT_OK) 675 | { 676 | _ret = UMQTT_READ_FAILED; 677 | goto exit; 678 | } 679 | *(client->recv_buf + _cnt) = _onedata; 680 | _pkt_len += (_onedata & 0x7F) * _multiplier; 681 | _multiplier *= 0x80; 682 | } while ((_onedata & 0x80) != 0); 683 | 684 | /* read and delete if the data length is greater than the cache buff */ 685 | if ((_pkt_len + 1 + _cnt) > client->mqtt_info.recv_size) 686 | { 687 | LOG_W(" socket read buffer too short! will read and delete socket buff! "); 688 | _loop_cnt = _pkt_len / client->mqtt_info.recv_size; 689 | 690 | do 691 | { 692 | if (_loop_cnt == 0) 693 | { 694 | umqtt_readpacket(client, client->recv_buf, _pkt_len, client->mqtt_info.recv_time_ms); 695 | _ret = UMQTT_BUFFER_TOO_SHORT; 696 | LOG_W(" finish read and delete socket buff!"); 697 | goto exit; 698 | } 699 | else 700 | { 701 | _loop_cnt--; 702 | umqtt_readpacket(client, client->recv_buf, client->mqtt_info.recv_size, client->mqtt_info.recv_time_ms); 703 | _pkt_len -= client->mqtt_info.recv_size; 704 | } 705 | }while(1); 706 | } 707 | 708 | /* 3. read the remain datas */ 709 | _ret = umqtt_readpacket(client, client->recv_buf + _cnt + 1, _pkt_len, client->mqtt_info.recv_time_ms); 710 | if (_ret == UMQTT_FIN_ACK) 711 | { 712 | LOG_W(" server fin ack! connect failed! need to reconnect!"); 713 | goto exit; 714 | } 715 | else if (_ret != UMQTT_OK) 716 | { 717 | _ret = UMQTT_READ_FAILED; 718 | LOG_E(" read remain datas error!"); 719 | goto exit; 720 | } 721 | 722 | /* 4. encode packet datas */ 723 | rt_memset(&decode_msg, 0, sizeof(decode_msg)); 724 | _ret = umqtt_decode(client->recv_buf, _pkt_len + _cnt + 1, &decode_msg); 725 | if (_ret < 0) 726 | { 727 | _ret = UMQTT_DECODE_ERROR; 728 | LOG_E(" decode error!"); 729 | goto exit; 730 | } 731 | _pkt_type = decode_msg.header.bits.type; 732 | switch (_pkt_type) 733 | { 734 | case UMQTT_TYPE_CONNACK: 735 | { 736 | LOG_D(" read connack cmd information!"); 737 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 738 | set_connect_status(client, UMQTT_CS_LINKED); 739 | } 740 | break; 741 | case UMQTT_TYPE_PUBLISH: 742 | { 743 | LOG_D(" read publish cmd information!"); 744 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 745 | 746 | if (decode_msg.header.bits.qos != UMQTT_QOS2) 747 | { 748 | LOG_D(" qos: %d, deliver message! topic nme: %s ", decode_msg.header.bits.qos, decode_msg.msg.publish.topic_name); 749 | umqtt_deliver_message(client, decode_msg.msg.publish.topic_name, decode_msg.msg.publish.topic_name_len, 750 | &(decode_msg.msg.publish)); 751 | } 752 | 753 | if (decode_msg.header.bits.qos != UMQTT_QOS0) 754 | { 755 | rt_memset(&encode_msg, 0, sizeof(encode_msg)); 756 | encode_msg.header.bits.qos = decode_msg.header.bits.qos; 757 | encode_msg.header.bits.dup = decode_msg.header.bits.dup; 758 | if (decode_msg.header.bits.qos == UMQTT_QOS1) 759 | { 760 | encode_msg.header.bits.type = UMQTT_TYPE_PUBACK; 761 | encode_msg.msg.puback.packet_id = decode_msg.msg.publish.packet_id; 762 | } 763 | else if (decode_msg.header.bits.qos == UMQTT_QOS2) 764 | { 765 | encode_msg.header.bits.type = UMQTT_TYPE_PUBREC; 766 | add_one_qos2_msg(client, &(decode_msg.msg.publish)); 767 | encode_msg.msg.pubrel.packet_id = decode_msg.msg.publish.packet_id; 768 | add_one_pubrec_msg(client, encode_msg.msg.pubrel.packet_id); /* add pubrec message */ 769 | } 770 | 771 | _ret = umqtt_encode(encode_msg.header.bits.type, client->send_buf, client->mqtt_info.send_size, 772 | &encode_msg); 773 | if (_ret < 0) 774 | { 775 | _ret = UMQTT_ENCODE_ERROR; 776 | LOG_E(" puback / pubrec failed!"); 777 | goto exit; 778 | } 779 | client->send_len = _ret; 780 | 781 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 782 | client->mqtt_info.send_timeout); 783 | if (_ret < 0) 784 | { 785 | _ret = UMQTT_SEND_FAILED; 786 | LOG_E(" trans send failed!"); 787 | goto exit; 788 | } 789 | 790 | } 791 | } 792 | break; 793 | case UMQTT_TYPE_PUBACK: 794 | { 795 | LOG_D(" read puback cmd information!"); 796 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 797 | msg_ack.msg_type = UMQTT_TYPE_PUBACK; 798 | msg_ack.packet_id = decode_msg.msg.puback.packet_id; 799 | _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack)); 800 | if (_ret != RT_EOK) 801 | { 802 | _ret = UMQTT_SEND_FAILED; 803 | LOG_E(" mq send failed!"); 804 | goto exit; 805 | } 806 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 807 | } 808 | break; 809 | case UMQTT_TYPE_PUBREC: 810 | { 811 | LOG_D(" read pubrec cmd information!"); 812 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 813 | msg_ack.msg_type = UMQTT_TYPE_PUBREC; 814 | msg_ack.packet_id = decode_msg.msg.puback.packet_id; 815 | _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack)); 816 | if (_ret != RT_EOK) 817 | { 818 | _ret = UMQTT_SEND_FAILED; 819 | LOG_E(" mq send failed!"); 820 | goto exit; 821 | } 822 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 823 | } 824 | break; 825 | case UMQTT_TYPE_PUBREL: 826 | { 827 | LOG_D(" read pubrel cmd information!"); 828 | 829 | rt_memset(&encode_msg, 0, sizeof(encode_msg)); 830 | encode_msg.header.bits.type = UMQTT_TYPE_PUBCOMP; 831 | encode_msg.header.bits.qos = decode_msg.header.bits.qos; 832 | encode_msg.header.bits.dup = decode_msg.header.bits.dup; 833 | encode_msg.msg.pubrel.packet_id = decode_msg.msg.pubrec.packet_id; 834 | 835 | /* publish callback, and delete callback */ 836 | qos2_publish_delete(client, encode_msg.msg.pubrel.packet_id); 837 | 838 | /* delete array numbers! */ 839 | clear_one_pubrec_msg(client, encode_msg.msg.pubrel.packet_id); 840 | 841 | _ret = umqtt_encode(UMQTT_TYPE_PUBCOMP, client->send_buf, client->mqtt_info.send_size, &encode_msg); 842 | if (_ret < 0) 843 | { 844 | _ret = UMQTT_ENCODE_ERROR; 845 | LOG_E(" pubcomp failed!"); 846 | goto exit; 847 | } 848 | client->send_len = _ret; 849 | 850 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 851 | client->mqtt_info.send_timeout); 852 | if (_ret < 0) 853 | { 854 | _ret = UMQTT_SEND_FAILED; 855 | LOG_E(" trans send failed!"); 856 | goto exit; 857 | } 858 | 859 | } 860 | break; 861 | case UMQTT_TYPE_PUBCOMP: 862 | { 863 | LOG_D(" read pubcomp cmd information!"); 864 | 865 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 866 | msg_ack.msg_type = UMQTT_TYPE_PUBCOMP; 867 | msg_ack.packet_id = decode_msg.msg.pubcomp.packet_id; 868 | _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack)); 869 | if (_ret != RT_EOK) 870 | { 871 | _ret = UMQTT_SEND_FAILED; 872 | goto exit; 873 | } 874 | } 875 | break; 876 | case UMQTT_TYPE_SUBACK: 877 | { 878 | LOG_D(" read suback cmd information!"); 879 | 880 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 881 | msg_ack.msg_type = UMQTT_TYPE_SUBACK; 882 | msg_ack.packet_id = decode_msg.msg.suback.packet_id; 883 | 884 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 885 | 886 | _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack)); 887 | if (_ret != RT_EOK) 888 | { 889 | _ret = UMQTT_SEND_FAILED; 890 | goto exit; 891 | } 892 | } 893 | break; 894 | case UMQTT_TYPE_UNSUBACK: 895 | { 896 | LOG_D(" read unsuback cmd information!"); 897 | 898 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 899 | msg_ack.msg_type = UMQTT_TYPE_UNSUBACK; 900 | msg_ack.packet_id = decode_msg.msg.unsuback.packet_id; 901 | 902 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 903 | 904 | _ret = rt_mq_send(client->msg_queue, &msg_ack, sizeof(struct umqtt_msg_ack)); 905 | if (_ret != RT_EOK) 906 | { 907 | _ret = UMQTT_SEND_FAILED; 908 | goto exit; 909 | } 910 | 911 | } 912 | break; 913 | case UMQTT_TYPE_PINGRESP: 914 | { 915 | LOG_I(" ping resp! broker -> client! now tick: %d ", rt_tick_get()); 916 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 917 | } 918 | break; 919 | default: 920 | { 921 | LOG_W(" not right type(0x%02x)!", _pkt_type); 922 | } 923 | break; 924 | } 925 | 926 | exit: 927 | return _ret; 928 | } 929 | 930 | 931 | static void umqtt_thread(void *params) 932 | { 933 | int _ret = 0; 934 | struct umqtt_client *client = (struct umqtt_client *)params; 935 | RT_ASSERT(client); 936 | 937 | while (1) { 938 | 939 | _ret = umqtt_handle_readpacket(client); 940 | if (_ret == UMQTT_FIN_ACK) 941 | { 942 | LOG_W("reconnect start! stop timer -> trans disconnect delay 5000ms -> umqtt connect! "); 943 | rt_timer_stop(client->uplink_timer); 944 | 945 | umqtt_trans_disconnect(client->sock); 946 | client->sock = -1; 947 | rt_thread_mdelay(5000); 948 | 949 | _ret = umqtt_connect(client, 1); 950 | if (_ret == UMQTT_RECONNECT_FAILED) 951 | { 952 | LOG_E(" umqtt reconnect failed!"); 953 | goto _exit; 954 | } 955 | } 956 | 957 | } 958 | 959 | _exit: 960 | LOG_I(" umqtt thread is quit! "); 961 | client->task_handle = RT_NULL; 962 | return; 963 | } 964 | 965 | static void umqtt_check_def_info(struct umqtt_info *info) 966 | { 967 | if (info) 968 | { 969 | if (info->send_size == 0) { info->send_size = PKG_UMQTT_INFO_DEF_SENDSIZE; } 970 | if (info->recv_size == 0) { info->recv_size = PKG_UMQTT_INFO_DEF_RECVSIZE; } 971 | if (info->reconnect_max_num == 0) { info->reconnect_max_num = PKG_UMQTT_INFO_DEF_RECONNECT_MAX_NUM; } 972 | if (info->reconnect_interval == 0) { info->reconnect_interval = PKG_UMQTT_INFO_DEF_RECONNECT_INTERVAL; } 973 | if (info->keepalive_max_num == 0) { info->keepalive_max_num = PKG_UMQTT_INFO_DEF_KEEPALIVE_MAX_NUM; } 974 | if (info->keepalive_interval == 0) { info->keepalive_interval = PKG_UMQTT_INFO_DEF_HEARTBEAT_INTERVAL; } 975 | if (info->connect_time == 0) { info->connect_time = PKG_UMQTT_INFO_DEF_CONNECT_TIMEOUT; } 976 | if (info->recv_time_ms == 0) { info->recv_time_ms = PKG_UMQTT_INFO_DEF_RECV_TIMEOUT_MS; } 977 | if (info->send_timeout == 0) { info->send_timeout = PKG_UMQTT_INFO_DEF_SEND_TIMEOUT; } 978 | if (info->thread_stack_size == 0) { info->thread_stack_size = PKG_UMQTT_INFO_DEF_THREAD_STACK_SIZE; } 979 | if (info->thread_priority == 0) { info->thread_priority = PKG_UMQTT_INFO_DEF_THREAD_PRIORITY; } 980 | } 981 | } 982 | 983 | static int umqtt_keepalive_callback(struct umqtt_client *client) 984 | { 985 | int _ret = 0, _length = 0; 986 | rt_uint32_t _connect_kp_time = 0; 987 | RT_ASSERT(client); 988 | 989 | #ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 990 | _connect_kp_time = (client->mqtt_info.connect_keepalive_sec == 0) ? (PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME >> 1) : (client->mqtt_info.connect_keepalive_sec >> 1); 991 | #else 992 | _connect_kp_time = PKG_UMQTT_CONNECT_KEEPALIVE_DEF_TIME >> 1; 993 | #endif 994 | 995 | if (client->connect_state == UMQTT_CS_LINKED) 996 | { 997 | if (((client->uplink_next_tick <= rt_tick_get()) 998 | && (client->uplink_next_tick > client->uplink_last_tick)) 999 | || ((client->pingreq_last_tick + _connect_kp_time) < rt_tick_get())) 1000 | { 1001 | _length = umqtt_encode(UMQTT_TYPE_PINGREQ, client->send_buf, 2, NULL); 1002 | if (_length == 2) 1003 | umqtt_trans_send(client->sock, client->send_buf, _length, 0); 1004 | 1005 | if (client->user_handler) 1006 | client->user_handler(client, UMQTT_EVT_HEARTBEAT); 1007 | 1008 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 1009 | client->pingreq_last_tick = rt_tick_get(); 1010 | } 1011 | else if ((client->uplink_last_tick >= client->uplink_next_tick) 1012 | && ((client->uplink_last_tick + client->mqtt_info.send_timeout * 1000) < rt_tick_get())) 1013 | { 1014 | if (++client->keepalive_count >= client->mqtt_info.keepalive_max_num) { 1015 | set_connect_status(client, UMQTT_CS_UNLINK); 1016 | } 1017 | else 1018 | { 1019 | _length = umqtt_encode(UMQTT_TYPE_PINGREQ, client->send_buf, 2, NULL); 1020 | if (_length == 2) 1021 | umqtt_trans_send(client->sock, client->send_buf, _length, 0); 1022 | 1023 | if (client->user_handler) 1024 | client->user_handler(client, UMQTT_EVT_HEARTBEAT); 1025 | 1026 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 1027 | } 1028 | } 1029 | } 1030 | return _ret; 1031 | } 1032 | 1033 | static int umqtt_reconnect_callback(struct umqtt_client *client) 1034 | { 1035 | int _ret = 0; 1036 | RT_ASSERT(client); 1037 | 1038 | if (client->connect_state == UMQTT_CS_UNLINK) 1039 | { 1040 | if (client->user_handler) 1041 | client->user_handler(client, UMQTT_EVT_OFFLINE); 1042 | 1043 | set_connect_status(client, UMQTT_CS_UNLINK_LINKING); 1044 | umqtt_trans_disconnect(client->sock); 1045 | client->sock = -1; 1046 | 1047 | } 1048 | else if (client->connect_state == UMQTT_CS_UNLINK_LINKING) 1049 | { 1050 | if (client->reconnect_next_tick <= rt_tick_get()) 1051 | { 1052 | if (client->sock < 0) 1053 | { 1054 | if (client->reconnect_count >= client->mqtt_info.reconnect_max_num) 1055 | { 1056 | set_connect_status(client, UMQTT_CS_DISCONNECT); 1057 | /* stop timer, delete task handle */ 1058 | if (client->task_handle) 1059 | { 1060 | rt_thread_delete(client->task_handle); 1061 | client->task_handle = RT_NULL; 1062 | } 1063 | if (client->uplink_timer) 1064 | { 1065 | rt_timer_stop(client->uplink_timer); 1066 | } 1067 | } 1068 | else 1069 | { 1070 | client->keepalive_count = 0; 1071 | set_uplink_recon_tick(client, RECON_NEXT_TICK); 1072 | umqtt_connect(client, 0); 1073 | if (client->user_handler) 1074 | client->user_handler(client, UMQTT_EVT_LINK); 1075 | } 1076 | } 1077 | else 1078 | { 1079 | umqtt_trans_disconnect(client->sock); 1080 | client->sock = -1; 1081 | } 1082 | } 1083 | 1084 | } 1085 | 1086 | return _ret; 1087 | } 1088 | 1089 | static void umqtt_uplink_timer_callback(void *params) 1090 | { 1091 | struct umqtt_client *client = (struct umqtt_client *)params; 1092 | umqtt_keepalive_callback(client); 1093 | umqtt_reconnect_callback(client); 1094 | pubrec_cycle_callback(client); 1095 | } 1096 | 1097 | /** 1098 | * delete the umqtt client, release resources 1099 | * 1100 | * @param client the input, umqtt client 1101 | * 1102 | * @return <0: failed or other error 1103 | * =0: success 1104 | */ 1105 | int umqtt_delete(struct umqtt_client *client) 1106 | { 1107 | int _ret = 0, _cnt = 0; 1108 | struct subtop_recv_handler *p_subtop = RT_NULL; 1109 | struct umqtt_qos2_msg *p_msg = RT_NULL; 1110 | rt_list_t *node = RT_NULL; 1111 | rt_list_t *node_tmp = RT_NULL; 1112 | if (client == RT_NULL) 1113 | return _ret; 1114 | if (client->task_handle) 1115 | { 1116 | rt_thread_delete(client->task_handle); 1117 | client->task_handle = RT_NULL; 1118 | client->user_handler = RT_NULL; 1119 | } 1120 | if (client->uplink_timer) 1121 | { 1122 | rt_timer_stop(client->uplink_timer); 1123 | rt_timer_delete(client->uplink_timer); 1124 | client->uplink_timer = RT_NULL; 1125 | } 1126 | if (client->msg_queue) 1127 | { 1128 | rt_mq_delete(client->msg_queue); 1129 | client->msg_queue = RT_NULL; 1130 | } 1131 | client->send_len = client->recv_len = 0; 1132 | if (client->lock_client) 1133 | { 1134 | rt_mutex_delete(client->lock_client); 1135 | client->lock_client = RT_NULL; 1136 | } 1137 | if (client->recv_buf) 1138 | { 1139 | rt_free(client->recv_buf); 1140 | client->recv_buf = RT_NULL; 1141 | } 1142 | if (client->send_buf) 1143 | { 1144 | rt_free(client->send_buf); 1145 | client->send_buf = RT_NULL; 1146 | } 1147 | if ((_ret = rt_list_isempty(&client->sub_recv_list)) == 0) 1148 | { 1149 | rt_list_for_each_safe(node, node_tmp, &client->sub_recv_list) 1150 | { 1151 | p_subtop = rt_list_entry(node, struct subtop_recv_handler, next_list); 1152 | if (p_subtop->topicfilter) { // const char *, cannot free! 1153 | rt_free(p_subtop->topicfilter); 1154 | p_subtop->topicfilter = RT_NULL; 1155 | p_subtop->callback = RT_NULL; 1156 | } 1157 | rt_list_remove(&(p_subtop->next_list)); 1158 | rt_free(p_subtop); p_subtop = RT_NULL; 1159 | } 1160 | } 1161 | 1162 | if ((_ret == rt_list_isempty(&client->qos2_msg_list)) == 0) 1163 | { 1164 | rt_list_for_each_safe(node, node_tmp, &client->qos2_msg_list) 1165 | { 1166 | p_msg = rt_list_entry(node, struct umqtt_qos2_msg, next_list); 1167 | if (p_msg->topic_name) { rt_free(p_msg->topic_name); p_msg->topic_name = RT_NULL; } 1168 | if (p_msg->payload) { rt_free(p_msg->payload); p_msg->payload = RT_NULL; } 1169 | rt_list_remove(&(p_msg->next_list)); 1170 | rt_free(p_msg); p_msg = RT_NULL; 1171 | } 1172 | } 1173 | 1174 | for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++) 1175 | { 1176 | client->pubrec_msg[_cnt].cnt = -1; 1177 | client->pubrec_msg[_cnt].packet_id = -1; 1178 | client->pubrec_msg[_cnt].next_tick = -1; 1179 | } 1180 | 1181 | rt_list_remove(&(client->list)); /* delete this list */ 1182 | rt_free(client); 1183 | _ret = UMQTT_OK; 1184 | return _ret; 1185 | } 1186 | /** 1187 | * create umqtt client according to user information 1188 | * 1189 | * @param info the input, user config information 1190 | * 1191 | * @return RT_NULL: create failed 1192 | * not RT_NULL: create success, client point 1193 | */ 1194 | umqtt_client_t umqtt_create(const struct umqtt_info *info) 1195 | { 1196 | RT_ASSERT(info); 1197 | static rt_uint8_t lock_cnt = 0; 1198 | int _ret = 0, _length = 0, _cnt = 0; 1199 | umqtt_client_t mqtt_client = RT_NULL; 1200 | struct subtop_recv_handler *p_subtop = RT_NULL; 1201 | char _name[RT_NAME_MAX]; 1202 | 1203 | mqtt_client = (umqtt_client_t)rt_calloc(1, sizeof(struct umqtt_client)); 1204 | if (mqtt_client == RT_NULL) 1205 | { 1206 | LOG_E(" umqtt create failed!"); 1207 | _ret = UMQTT_MEM_FULL; 1208 | goto exit; 1209 | } 1210 | rt_memcpy(&(mqtt_client->mqtt_info), info, sizeof(struct umqtt_info)); 1211 | umqtt_check_def_info(&(mqtt_client->mqtt_info)); 1212 | 1213 | /* will topic/message send/recv*/ 1214 | mqtt_client->sub_recv_list_len = PKG_UMQTT_SUBRECV_DEF_LENGTH; 1215 | rt_list_init(&mqtt_client->sub_recv_list); 1216 | if (mqtt_client->mqtt_info.lwt_topic != RT_NULL) 1217 | { 1218 | p_subtop = (struct subtop_recv_handler *)rt_calloc(1, sizeof(struct subtop_recv_handler)); 1219 | if (p_subtop != RT_NULL) 1220 | { 1221 | p_subtop->qos = mqtt_client->mqtt_info.lwt_qos; 1222 | _length = strlen(mqtt_client->mqtt_info.lwt_topic) + 1; 1223 | p_subtop->topicfilter = (char *)rt_calloc(1, sizeof(char) * _length); 1224 | rt_strncpy(p_subtop->topicfilter, mqtt_client->mqtt_info.lwt_topic, _length); 1225 | p_subtop->callback = (void (*)(void *, void *))(mqtt_client->mqtt_info.lwt_cb); 1226 | rt_list_insert_after(&mqtt_client->sub_recv_list, &p_subtop->next_list); 1227 | } 1228 | } 1229 | 1230 | /* qos2 msg list init */ 1231 | rt_list_init(&mqtt_client->qos2_msg_list); 1232 | 1233 | for (_cnt = 0; _cnt < PKG_UMQTT_QOS2_QUE_MAX; _cnt++) 1234 | { 1235 | mqtt_client->pubrec_msg[_cnt].cnt = -1; 1236 | mqtt_client->pubrec_msg[_cnt].packet_id = -1; 1237 | mqtt_client->pubrec_msg[_cnt].next_tick = -1; 1238 | } 1239 | 1240 | mqtt_client->recv_buf = rt_calloc(1, sizeof(rt_uint8_t) * mqtt_client->mqtt_info.recv_size); 1241 | if (mqtt_client->recv_buf == RT_NULL) 1242 | { 1243 | LOG_E(" client receive buff calloc failed!"); 1244 | _ret = UMQTT_MEM_FULL; 1245 | goto exit; 1246 | } 1247 | 1248 | mqtt_client->send_buf = rt_calloc(1, sizeof(rt_uint8_t) * mqtt_client->mqtt_info.send_size); 1249 | if (mqtt_client->send_buf == RT_NULL) 1250 | { 1251 | LOG_E(" client send buff calloc failed!"); 1252 | _ret = UMQTT_MEM_FULL; 1253 | goto exit; 1254 | } 1255 | 1256 | rt_memset(_name, 0x00, sizeof(_name)); 1257 | rt_snprintf(_name, RT_NAME_MAX, "umqtt_l%d", lock_cnt); 1258 | mqtt_client->lock_client = rt_mutex_create(_name, RT_IPC_FLAG_FIFO); 1259 | if (mqtt_client->lock_client == RT_NULL) 1260 | { 1261 | LOG_E(" create lock_client failed!"); 1262 | _ret = UMQTT_MEM_FULL; 1263 | goto exit; 1264 | } 1265 | 1266 | rt_memset(_name, 0x00, sizeof(_name)); 1267 | rt_snprintf(_name, RT_NAME_MAX, "umqtt_q%d", lock_cnt); 1268 | mqtt_client->msg_queue = rt_mq_create(_name, 1269 | sizeof(struct umqtt_msg_ack), 1270 | PKG_UMQTT_MSG_QUEUE_ACK_DEF_SIZE, 1271 | RT_IPC_FLAG_FIFO); 1272 | if (mqtt_client->msg_queue == RT_NULL) 1273 | { 1274 | LOG_E(" create msg_queue failed!"); 1275 | _ret = UMQTT_MEM_FULL; 1276 | goto exit; 1277 | } 1278 | 1279 | rt_memset(_name, 0x00, sizeof(_name)); 1280 | rt_snprintf(_name, RT_NAME_MAX, "umqtt_m%d", lock_cnt); 1281 | mqtt_client->uplink_timer = rt_timer_create(_name, 1282 | umqtt_uplink_timer_callback, 1283 | mqtt_client, 1284 | UMQTT_INFO_DEF_UPLINK_TIMER_TICK, 1285 | RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC); 1286 | if (mqtt_client->uplink_timer == RT_NULL) 1287 | { 1288 | LOG_E(" create uplink timer failed!"); 1289 | _ret = UMQTT_MEM_FULL; 1290 | goto exit; 1291 | } 1292 | 1293 | rt_list_init(&mqtt_client->list); /* objects, multi mqttclient */ 1294 | 1295 | rt_memset(_name, 0x00, sizeof(_name)); 1296 | rt_snprintf(_name, RT_NAME_MAX, "umqtt_t%d", lock_cnt++); 1297 | 1298 | mqtt_client->task_handle = rt_thread_create(_name, 1299 | umqtt_thread, 1300 | (void *)mqtt_client, 1301 | mqtt_client->mqtt_info.thread_stack_size, 1302 | mqtt_client->mqtt_info.thread_priority, 1303 | UMQTT_INFO_DEF_THREAD_TICK); 1304 | if (mqtt_client->task_handle == RT_NULL) 1305 | { 1306 | LOG_E(" create thread failed!"); 1307 | _ret = UMQTT_MEM_FULL; 1308 | goto exit; 1309 | } 1310 | 1311 | exit: 1312 | if (_ret < 0) 1313 | { 1314 | umqtt_delete(mqtt_client); 1315 | mqtt_client = RT_NULL; 1316 | } 1317 | return mqtt_client; 1318 | } 1319 | 1320 | /** 1321 | * start the umqtt client to work 1322 | * 1323 | * @param client the input, umqtt client 1324 | * 1325 | * @return < 0: failed 1326 | * >= 0: success 1327 | */ 1328 | int umqtt_start(struct umqtt_client *client) 1329 | { 1330 | int _ret = 0, _length = 0; 1331 | struct subtop_recv_handler *p_subtop = RT_NULL; 1332 | rt_list_t *node = RT_NULL; 1333 | struct umqtt_msg encode_msg = { 0 }; 1334 | struct umqtt_msg_ack msg_ack = { 0 }; 1335 | if (client == RT_NULL) { 1336 | _ret = UMQTT_INPARAMS_NULL; 1337 | LOG_E(" umqtt start, client is NULL!"); 1338 | goto exit; 1339 | } 1340 | 1341 | set_connect_status(client, UMQTT_CS_LINKING); 1342 | 1343 | _ret = umqtt_connect(client, 1); 1344 | if (_ret < 0) 1345 | goto exit; 1346 | 1347 | if (client->task_handle) 1348 | { 1349 | rt_thread_startup(client->task_handle); 1350 | } 1351 | 1352 | if (client->uplink_timer) 1353 | { 1354 | if (rt_timer_start(client->uplink_timer) != RT_EOK) 1355 | { 1356 | _ret = UMQTT_FAILED; 1357 | LOG_E(" timer start failed!"); 1358 | goto exit; 1359 | } 1360 | } 1361 | 1362 | /* will message topic to send & recv */ 1363 | if (0 == rt_list_isempty(&client->sub_recv_list)) 1364 | { 1365 | rt_list_for_each(node, &client->sub_recv_list) 1366 | { 1367 | p_subtop = rt_list_entry(node, struct subtop_recv_handler, next_list); 1368 | rt_memset(&encode_msg, 0, sizeof(encode_msg)); 1369 | encode_msg.header.bits.qos = UMQTT_QOS1; 1370 | encode_msg.msg.subscribe.packet_id = get_next_packetID(client); 1371 | encode_msg.msg.subscribe.topic_filter[0].topic_filter = p_subtop->topicfilter; 1372 | encode_msg.msg.subscribe.topic_filter[0].filter_len = strlen(p_subtop->topicfilter); 1373 | encode_msg.msg.subscribe.topic_filter[0].req_qos.request_qos = p_subtop->qos; 1374 | encode_msg.msg.subscribe.topic_count = 1; 1375 | rt_memset(client->send_buf, 0, sizeof(rt_uint8_t) * client->mqtt_info.send_size); 1376 | _length = umqtt_encode(UMQTT_TYPE_SUBSCRIBE, client->send_buf, client->mqtt_info.send_size, &encode_msg); 1377 | if (_length <= 0) 1378 | { 1379 | _ret = UMQTT_ENCODE_ERROR; 1380 | LOG_W(" subscribe encode failed! topic: %s", p_subtop->topicfilter); 1381 | continue; 1382 | } 1383 | client->send_len = _length; 1384 | 1385 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout); 1386 | if (_ret < 0) 1387 | { 1388 | _ret = UMQTT_SEND_FAILED; 1389 | LOG_W(" subscribe trans send failed! "); 1390 | continue; 1391 | } 1392 | 1393 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 1394 | 1395 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 1396 | if (RT_EOK == rt_mq_recv(client->msg_queue, 1397 | &msg_ack, sizeof(struct umqtt_msg_ack), 1398 | rt_tick_from_millisecond(client->mqtt_info.send_timeout * 1000))) 1399 | { 1400 | if (msg_ack.msg_type == UMQTT_TYPE_SUBACK) 1401 | { 1402 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 1403 | _ret = UMQTT_OK; 1404 | LOG_I(" subscribe ack ok! "); 1405 | continue; 1406 | } 1407 | else 1408 | { 1409 | _ret = UMQTT_READ_ERROR; 1410 | LOG_W(" subscribe ack error! message type: %d ", msg_ack.msg_type); 1411 | continue; 1412 | } 1413 | } 1414 | else 1415 | { 1416 | _ret = UMQTT_READ_FAILED; 1417 | LOG_W(" subscribe recv message timeout! topic: %s ", p_subtop->topicfilter); 1418 | continue; 1419 | } 1420 | } 1421 | } 1422 | 1423 | exit: 1424 | if (_ret == UMQTT_RECONNECT_FAILED) 1425 | LOG_W(" connect and reconnect failed!"); 1426 | 1427 | return _ret; 1428 | } 1429 | 1430 | /** 1431 | * stop the umqtt client to work 1432 | * 1433 | * @param client the input, umqtt client 1434 | * 1435 | * @return < 0: failed 1436 | * >= 0: success 1437 | */ 1438 | void umqtt_stop(struct umqtt_client *client) 1439 | { 1440 | if (client == RT_NULL) 1441 | return; 1442 | 1443 | if (client->task_handle) 1444 | { 1445 | rt_thread_delete(client->task_handle); 1446 | client->task_handle = RT_NULL; 1447 | } 1448 | 1449 | if (client->uplink_timer) 1450 | rt_timer_stop(client->uplink_timer); 1451 | 1452 | umqtt_disconnect(client); 1453 | if (client->sock != -1) 1454 | { 1455 | umqtt_trans_disconnect(client->sock); 1456 | client->sock = -1; 1457 | } 1458 | } 1459 | 1460 | /** 1461 | * Client to send a publish message to the broker 1462 | * 1463 | * @param client the input, umqtt client 1464 | * @param qos the input, qos of publish message 1465 | * @param topic the input, topic string 1466 | * @param payload the input, mqtt message payload 1467 | * @param length the input, mqtt message payload length 1468 | * @param timeout the input, msg queue wait timeout, uint:mSec 1469 | * 1470 | * @return < 0: failed 1471 | * >= 0: success 1472 | */ 1473 | int umqtt_publish(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, void *payload, size_t length, int timeout) 1474 | { 1475 | int _ret = 0, _length = 0; 1476 | int _cnt = 0; 1477 | rt_uint16_t packet_id = 0; 1478 | struct umqtt_msg_ack msg_ack = { 0 }; 1479 | struct umqtt_msg encode_msg = { 0 }; 1480 | 1481 | RT_ASSERT(client); 1482 | RT_ASSERT(topic); 1483 | RT_ASSERT(payload); 1484 | RT_ASSERT(length); 1485 | 1486 | packet_id = ((qos == UMQTT_QOS0) ? 0 : get_next_packetID(client)); 1487 | 1488 | encode_msg.header.bits.qos = qos; 1489 | encode_msg.header.bits.dup = 0; 1490 | encode_msg.msg.publish.packet_id = packet_id; 1491 | encode_msg.msg.publish.payload = payload; 1492 | encode_msg.msg.publish.payload_len = length; 1493 | encode_msg.msg.publish.topic_name = topic; 1494 | encode_msg.msg.publish.topic_name_len = strlen(topic); 1495 | _length = umqtt_encode(UMQTT_TYPE_PUBLISH, client->send_buf, client->mqtt_info.send_size, &encode_msg); 1496 | if (_length <= 0) 1497 | { 1498 | _ret = UMQTT_ENCODE_ERROR; 1499 | LOG_E(" publish encode failed! topic: %d", topic); 1500 | goto exit; 1501 | } 1502 | client->send_len = _length; 1503 | 1504 | _republish: 1505 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout); 1506 | if (_ret < 0) 1507 | { 1508 | _ret = UMQTT_SEND_FAILED; 1509 | LOG_E(" publish trans send failed!"); 1510 | goto exit; 1511 | } 1512 | _ret = UMQTT_OK; 1513 | if (qos == UMQTT_QOS1) 1514 | { 1515 | if (RT_EOK == rt_mq_recv(client->msg_queue, 1516 | &msg_ack, sizeof(struct umqtt_msg_ack), 1517 | rt_tick_from_millisecond(timeout))) 1518 | { 1519 | if (msg_ack.msg_type == UMQTT_TYPE_PUBACK) 1520 | { 1521 | _ret = UMQTT_OK; 1522 | LOG_I(" publish qos1 ack success!"); 1523 | goto exit; 1524 | } 1525 | else 1526 | { 1527 | _ret = UMQTT_READ_ERROR; 1528 | LOG_E(" publish qos1 msg_type(%d) error!", msg_ack.msg_type); 1529 | goto exit; 1530 | } 1531 | } 1532 | else 1533 | { 1534 | if (++_cnt >= PKG_UMQTT_PUBLISH_RECON_MAX) 1535 | { 1536 | _ret = UMQTT_READ_ERROR; 1537 | LOG_E(" publish qos1 recv error!"); 1538 | goto exit; 1539 | } 1540 | else 1541 | { 1542 | LOG_W(" qos1 publish failed! republish!"); 1543 | goto _republish; 1544 | } 1545 | 1546 | } 1547 | } 1548 | else if (qos == UMQTT_QOS2) 1549 | { 1550 | if (RT_EOK == rt_mq_recv(client->msg_queue, 1551 | &msg_ack, sizeof(struct umqtt_msg_ack), 1552 | rt_tick_from_millisecond(timeout))) 1553 | { 1554 | if (msg_ack.msg_type == UMQTT_TYPE_PUBREC) 1555 | { 1556 | LOG_D(" qos2 publish datas! success! then, pubrel msg! "); 1557 | } 1558 | else 1559 | { 1560 | _ret = UMQTT_READ_ERROR; 1561 | LOG_E(" publish qos2 msg_type(%d) error!", msg_ack.msg_type); 1562 | goto exit; 1563 | } 1564 | } 1565 | else 1566 | { 1567 | if (++_cnt >= PKG_UMQTT_PUBLISH_RECON_MAX) 1568 | { 1569 | _ret = UMQTT_READ_ERROR; 1570 | LOG_E(" publish qos2 recv error!"); 1571 | goto exit; 1572 | } 1573 | else 1574 | { 1575 | goto _republish; 1576 | } 1577 | } 1578 | 1579 | /* send pubreal datas */ 1580 | _repubrel: 1581 | _cnt = 0; 1582 | rt_memset(&encode_msg, 0, sizeof(encode_msg)); 1583 | encode_msg.header.bits.type = UMQTT_TYPE_PUBREL; 1584 | encode_msg.header.bits.qos = qos; 1585 | encode_msg.header.bits.dup = 0; 1586 | encode_msg.msg.publish.packet_id = packet_id; 1587 | _length = umqtt_encode(encode_msg.header.bits.type, client->send_buf, client->mqtt_info.send_size, &encode_msg); 1588 | if (_length <= 0) 1589 | { 1590 | _ret = UMQTT_ENCODE_ERROR; 1591 | LOG_E(" pubrel encode failed! topic: %d", topic); 1592 | goto exit; 1593 | } 1594 | client->send_len = _length; 1595 | 1596 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, 1597 | client->mqtt_info.send_timeout); 1598 | if (_ret < 0) 1599 | { 1600 | _ret = UMQTT_SEND_FAILED; 1601 | LOG_E(" publish trans send failed!"); 1602 | goto exit; 1603 | } 1604 | _ret = UMQTT_OK; 1605 | 1606 | if (RT_EOK == rt_mq_recv(client->msg_queue, 1607 | &msg_ack, sizeof(struct umqtt_msg_ack), 1608 | rt_tick_from_millisecond(timeout))) 1609 | { 1610 | if (msg_ack.msg_type == UMQTT_TYPE_PUBCOMP) 1611 | { 1612 | _ret = UMQTT_OK; 1613 | LOG_I(" pubcomp ack! publish qos2 ack success!"); 1614 | goto exit; 1615 | } 1616 | else 1617 | { 1618 | _ret = UMQTT_READ_ERROR; 1619 | LOG_E(" publish qos2 msg_type(%d) error!", msg_ack.msg_type); 1620 | goto exit; 1621 | } 1622 | } 1623 | else 1624 | { 1625 | if (++_cnt >= PKG_UMQTT_PUBLISH_RECON_MAX) 1626 | { 1627 | _ret = UMQTT_READ_ERROR; 1628 | LOG_E(" pubrel qos2 recv error!"); 1629 | goto exit; 1630 | } 1631 | else 1632 | { 1633 | goto _repubrel; 1634 | } 1635 | } 1636 | 1637 | } 1638 | 1639 | exit: 1640 | return _ret; 1641 | } 1642 | 1643 | /** 1644 | * Subscribe the client to defined topic with defined qos 1645 | * 1646 | * @param client the input, umqtt client 1647 | * @param topic the input, topic string 1648 | * @param qos the input, qos of publish message 1649 | * @param callback the input, when broke publish the topic is the sanme as sub topic, will run this callback 1650 | * 1651 | * @return < 0: failed 1652 | * >= 0: success 1653 | */ 1654 | int umqtt_subscribe(struct umqtt_client *client, const char *topic, enum umqtt_qos qos, umqtt_subscribe_cb callback) 1655 | { 1656 | int _ret = 0; 1657 | int _length = 0; 1658 | int _cnt = 0; 1659 | struct subtop_recv_handler *p_subtop = RT_NULL; 1660 | rt_list_t *node = RT_NULL; 1661 | struct umqtt_msg encode_msg = { 0 }; 1662 | struct umqtt_msg_ack msg_ack = { 0 }; 1663 | 1664 | RT_ASSERT(client); 1665 | RT_ASSERT(topic); 1666 | 1667 | _cnt = rt_list_isempty(&client->sub_recv_list); 1668 | 1669 | if (_cnt == 0) 1670 | { 1671 | _cnt = 0; 1672 | rt_list_for_each(node, &client->sub_recv_list) 1673 | { 1674 | p_subtop = rt_list_entry(node, struct subtop_recv_handler, next_list); 1675 | if (p_subtop->topicfilter 1676 | && (rt_strcmp(p_subtop->topicfilter, topic) == 0)) 1677 | { 1678 | LOG_D(" subscribe topic(%s) is already subscribed.", topic); 1679 | goto exit; 1680 | } 1681 | } 1682 | _length = rt_list_len(&client->sub_recv_list); 1683 | } 1684 | 1685 | if (_length > client->sub_recv_list_len) 1686 | { 1687 | _ret = UMQTT_MEM_FULL; 1688 | LOG_E(" subscribe size(%d) is not enough! now length(%d)!", client->sub_recv_list_len, _length); 1689 | goto exit; 1690 | } 1691 | else 1692 | { 1693 | rt_memset(&encode_msg, 0, sizeof(encode_msg)); 1694 | encode_msg.header.bits.qos = UMQTT_QOS1; 1695 | encode_msg.msg.subscribe.packet_id = get_next_packetID(client); 1696 | encode_msg.msg.subscribe.topic_filter[0].topic_filter = topic; 1697 | encode_msg.msg.subscribe.topic_filter[0].filter_len = strlen(topic); 1698 | encode_msg.msg.subscribe.topic_filter[0].req_qos.request_qos = qos; 1699 | encode_msg.msg.subscribe.topic_count = 1; 1700 | rt_memset(client->send_buf, 0, sizeof(rt_uint8_t) * client->mqtt_info.send_size); 1701 | _length = umqtt_encode(UMQTT_TYPE_SUBSCRIBE, client->send_buf, client->mqtt_info.send_size, &encode_msg); 1702 | if (_length <= 0) 1703 | { 1704 | _ret = UMQTT_ENCODE_ERROR; 1705 | LOG_E(" subscribe encode failed! topic: %s", topic); 1706 | goto exit; 1707 | } 1708 | client->send_len = _length; 1709 | 1710 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout); 1711 | if (_ret < 0) 1712 | { 1713 | _ret = UMQTT_SEND_FAILED; 1714 | LOG_E(" subscribe trans send failed!"); 1715 | goto exit; 1716 | } 1717 | 1718 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 1719 | 1720 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 1721 | if (RT_EOK == rt_mq_recv(client->msg_queue, 1722 | &msg_ack, sizeof(struct umqtt_msg_ack), 1723 | rt_tick_from_millisecond(client->mqtt_info.send_timeout * 1000))) 1724 | { 1725 | if (msg_ack.msg_type == UMQTT_TYPE_SUBACK) 1726 | { 1727 | p_subtop = RT_NULL; 1728 | p_subtop = (struct subtop_recv_handler *)rt_calloc(1, sizeof(struct subtop_recv_handler)); 1729 | RT_ASSERT(p_subtop); 1730 | LOG_D(" start assign datas !"); 1731 | p_subtop->topicfilter = rt_strdup(topic); 1732 | p_subtop->qos = qos; 1733 | if (callback) 1734 | { 1735 | p_subtop->callback = (void (*)(void *, void *))callback; 1736 | } 1737 | rt_list_insert_after(&client->sub_recv_list, &p_subtop->next_list); 1738 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 1739 | 1740 | _ret = UMQTT_OK; 1741 | LOG_I("subscribe ack ok! "); 1742 | goto exit; 1743 | } 1744 | else 1745 | { 1746 | _ret = UMQTT_READ_ERROR; 1747 | LOG_E("subscribe ack error!"); 1748 | goto exit; 1749 | } 1750 | } 1751 | else 1752 | { 1753 | _ret = UMQTT_READ_FAILED; 1754 | LOG_E(" subscribe recv message timeout! topic: %s", topic); 1755 | goto exit; 1756 | } 1757 | } 1758 | 1759 | exit: 1760 | return _ret; 1761 | } 1762 | 1763 | /** 1764 | * Unsubscribe the client from defined topic 1765 | * 1766 | * @param client the input, umqtt client 1767 | * @param topic the input, topic string 1768 | * 1769 | * @return < 0: failed 1770 | * >= 0: success 1771 | */ 1772 | int umqtt_unsubscribe(struct umqtt_client *client, const char *topic) 1773 | { 1774 | int _ret = 0, _cnt = 0, _length = 0; 1775 | struct subtop_recv_handler *p_subtop = RT_NULL; 1776 | rt_list_t *node = RT_NULL; 1777 | rt_list_t *node_tmp = RT_NULL; 1778 | struct umqtt_msg encode_msg = { 0 }; 1779 | struct umqtt_msg_ack msg_ack = { 0 }; 1780 | 1781 | RT_ASSERT(client); 1782 | RT_ASSERT(topic); 1783 | _cnt = rt_list_len(&client->sub_recv_list); 1784 | 1785 | if (_cnt > 0) 1786 | { 1787 | rt_list_for_each_safe(node, node_tmp, &client->sub_recv_list) 1788 | { 1789 | p_subtop = rt_list_entry(node, struct subtop_recv_handler, next_list); 1790 | _cnt--; 1791 | if (p_subtop->topicfilter 1792 | && (rt_strncmp(p_subtop->topicfilter, topic, strlen(topic)) == 0)) 1793 | { 1794 | rt_memset(&encode_msg, 0, sizeof(encode_msg)); 1795 | encode_msg.header.bits.qos = UMQTT_QOS1; 1796 | encode_msg.msg.unsubscribe.packet_id = get_next_packetID(client); 1797 | encode_msg.msg.unsubscribe.topic_count = 1; 1798 | encode_msg.msg.unsubscribe.topic_filter[0].topic_filter = topic, 1799 | encode_msg.msg.unsubscribe.topic_filter[0].filter_len = strlen(topic); 1800 | _length = umqtt_encode(UMQTT_TYPE_UNSUBSCRIBE, client->send_buf, client->mqtt_info.send_size, &encode_msg); 1801 | if (_length <= 0) 1802 | { 1803 | _ret = UMQTT_ENCODE_ERROR; 1804 | LOG_E(" unsubscribe encode failed! topic: %s", topic); 1805 | goto exit; 1806 | } 1807 | client->send_len = _length; 1808 | 1809 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout); 1810 | if (_ret < 0) 1811 | { 1812 | _ret = UMQTT_SEND_FAILED; 1813 | LOG_E(" unsubscribe trans send failed!"); 1814 | goto exit; 1815 | } 1816 | 1817 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 1818 | 1819 | rt_memset(&msg_ack, 0, sizeof(msg_ack)); 1820 | if (RT_EOK == rt_mq_recv(client->msg_queue, 1821 | &msg_ack, sizeof(struct umqtt_msg_ack), 1822 | rt_tick_from_millisecond(client->mqtt_info.send_timeout * 1000))) 1823 | { 1824 | if (msg_ack.msg_type == UMQTT_TYPE_UNSUBACK) 1825 | { 1826 | if (p_subtop->topicfilter) { 1827 | rt_free(p_subtop->topicfilter); 1828 | p_subtop->topicfilter = RT_NULL; 1829 | } 1830 | p_subtop->callback = RT_NULL; 1831 | rt_list_remove(&(p_subtop->next_list)); 1832 | rt_free(p_subtop); p_subtop = RT_NULL; 1833 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 1834 | 1835 | _ret = UMQTT_OK; 1836 | LOG_I(" unsubscribe ack ok! "); 1837 | goto exit; 1838 | } 1839 | else 1840 | { 1841 | _ret = UMQTT_READ_ERROR; 1842 | LOG_E(" unsubscribe ack error!"); 1843 | goto exit; 1844 | } 1845 | } 1846 | else 1847 | { 1848 | _ret = UMQTT_TIMEOUT; 1849 | LOG_E(" unsubscribe recv message timeout! topic: %s", topic); 1850 | goto exit; 1851 | } 1852 | } 1853 | } 1854 | 1855 | } 1856 | else 1857 | { 1858 | LOG_D(" unsubscribe topic(%s) is not exit!", topic); 1859 | } 1860 | 1861 | exit: 1862 | return _ret; 1863 | } 1864 | 1865 | /** 1866 | * umqtt client publish nonblocking datas 1867 | * 1868 | * @param client the input, umqtt client 1869 | * @param qos the input, qos of publish message 1870 | * @param topic the input, topic string 1871 | * @param payload the input, mqtt message payload 1872 | * @param length the input, mqtt message payload length 1873 | * 1874 | * @return < 0: failed 1875 | * >= 0: success 1876 | */ 1877 | int umqtt_publish_async(struct umqtt_client *client, enum umqtt_qos qos, const char *topic, 1878 | void *payload, size_t length) 1879 | { 1880 | int _ret = 0, _length = 0; 1881 | rt_uint16_t packet_id = 0; 1882 | struct umqtt_msg encode_msg = { 0 }; 1883 | 1884 | RT_ASSERT(client); 1885 | RT_ASSERT(topic); 1886 | RT_ASSERT(payload); 1887 | RT_ASSERT(length); 1888 | 1889 | packet_id = ((qos == UMQTT_QOS0) ? 0 : get_next_packetID(client)); 1890 | 1891 | encode_msg.header.bits.qos = qos; 1892 | encode_msg.header.bits.dup = 0; 1893 | encode_msg.msg.publish.packet_id = packet_id; 1894 | encode_msg.msg.publish.payload = payload; 1895 | encode_msg.msg.publish.payload_len = length; 1896 | encode_msg.msg.publish.topic_name = topic; 1897 | encode_msg.msg.publish.topic_name_len = strlen(topic); 1898 | _length = umqtt_encode(UMQTT_TYPE_PUBLISH, client->send_buf, client->mqtt_info.send_size, &encode_msg); 1899 | if (_length <= 0) 1900 | { 1901 | _ret = UMQTT_ENCODE_ERROR; 1902 | LOG_E(" publish encode failed! topic: %d", topic); 1903 | goto exit; 1904 | } 1905 | client->send_len = _length; 1906 | 1907 | _ret = umqtt_trans_send(client->sock, client->send_buf, client->send_len, client->mqtt_info.send_timeout); 1908 | if (_ret < 0) 1909 | { 1910 | _ret = UMQTT_SEND_FAILED; 1911 | LOG_E(" publish trans send failed!"); 1912 | goto exit; 1913 | } 1914 | 1915 | set_uplink_recon_tick(client, UPLINK_LAST_TICK); 1916 | set_uplink_recon_tick(client, UPLINK_NEXT_TICK); 1917 | exit: 1918 | return _ret; 1919 | } 1920 | 1921 | /** 1922 | * umqtt client publish nonblocking datas 1923 | * 1924 | * @param client the input, umqtt client 1925 | * @param cmd the input, UMQTT_CMD_SUB_CB / UMQTT_CMD_EVT_CB 1926 | * @param params the input, params according to cmd 1927 | * 1928 | */ 1929 | int umqtt_control(struct umqtt_client *client, enum umqtt_cmd cmd, void *params) 1930 | { 1931 | struct subtop_recv_handler *p_subtop = RT_NULL; 1932 | rt_list_t *node = RT_NULL; 1933 | char *topic = RT_NULL; 1934 | //int _ret = 0; 1935 | RT_ASSERT(client); 1936 | switch(cmd) 1937 | { 1938 | case UMQTT_CMD_SUB_CB: 1939 | { 1940 | RT_ASSERT(params); 1941 | if (0 == rt_list_isempty(&client->sub_recv_list)) /* has list item */ 1942 | { 1943 | rt_list_for_each(node, &client->sub_recv_list) 1944 | { 1945 | p_subtop = rt_list_entry(node, struct subtop_recv_handler, next_list); 1946 | topic = ((struct subtop_recv_handler *)params)->topicfilter; 1947 | if ((p_subtop->topicfilter != RT_NULL) 1948 | && (rt_strncmp(p_subtop->topicfilter, topic, rt_strlen(topic)) == 0)) 1949 | { 1950 | goto _exit; 1951 | } 1952 | } 1953 | } 1954 | rt_list_insert_after(&client->sub_recv_list, &((struct subtop_recv_handler *)params)->next_list); 1955 | } 1956 | break; 1957 | case UMQTT_CMD_EVT_CB: 1958 | { 1959 | client->user_handler = params; 1960 | } 1961 | break; 1962 | case UMQTT_CMD_SET_HB: 1963 | { 1964 | client->mqtt_info.keepalive_interval = *(rt_uint32_t *)params; 1965 | } 1966 | break; 1967 | case UMQTT_CMD_GET_CLIENT_STA: 1968 | { 1969 | return ((int)(client->connect_state)); 1970 | } 1971 | break; 1972 | case UMQTT_CMD_DEL_HANDLE: 1973 | { 1974 | if (RT_EOK == rt_thread_delete(client->task_handle)) 1975 | { 1976 | client->task_handle = RT_NULL; 1977 | LOG_D(" delete thread success! "); 1978 | } 1979 | else 1980 | { 1981 | LOG_E(" delete thread failed! "); 1982 | } 1983 | } 1984 | break; 1985 | case UMQTT_CMD_DISCONNECT: 1986 | { 1987 | umqtt_disconnect(client); 1988 | } 1989 | break; 1990 | #ifdef PKG_UMQTT_TEST_SHORT_KEEPALIVE_TIME 1991 | case UMQTT_CMD_SET_CON_KP: 1992 | { 1993 | client->mqtt_info.connect_keepalive_sec = (rt_uint16_t *)params; 1994 | } 1995 | break; 1996 | #endif 1997 | default: 1998 | LOG_W(" control cmd:%d", cmd); 1999 | break; 2000 | } 2001 | _exit: 2002 | return UMQTT_OK; 2003 | } 2004 | 2005 | --------------------------------------------------------------------------------