├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── MQTTClient-C ├── mqtt_client.c └── mqtt_client.h ├── MQTTPacket └── src │ ├── CMakeLists.txt │ ├── MQTTConnect.h │ ├── MQTTConnectClient.c │ ├── MQTTConnectServer.c │ ├── MQTTDeserializePublish.c │ ├── MQTTFormat.c │ ├── MQTTFormat.h │ ├── MQTTPacket.c │ ├── MQTTPacket.h │ ├── MQTTPublish.h │ ├── MQTTSerializePublish.c │ ├── MQTTSubscribe.h │ ├── MQTTSubscribeClient.c │ ├── MQTTSubscribeServer.c │ ├── MQTTUnsubscribe.h │ ├── MQTTUnsubscribeClient.c │ ├── MQTTUnsubscribeServer.c │ └── StackTrace.h ├── README.md ├── SConscript ├── docs ├── README.md ├── api.md ├── figures │ ├── mqtt_principle.png │ ├── open_mqtt_example.png │ ├── paho-mqtt-menuconfig.png │ └── select_mqtt_package.png ├── introduction.md ├── principle.md ├── samples.md ├── user-guide.md └── version.md ├── samples └── mqtt_sample.c └── tests └── mqtt_test.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 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Paho 2 | 3 | Thanks for your interest in this project! 4 | 5 | You can contribute bugfixes and new features by sending pull requests through GitHub. 6 | 7 | ## Legal 8 | 9 | In order for your contribution to be accepted, it must comply with the Eclipse Foundation IP policy. 10 | 11 | Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git). 12 | 13 | 1. Sign the [Eclipse ECA](http://www.eclipse.org/legal/ECA.php) 14 | 1. Register for an Eclipse Foundation User ID. You can register [here](https://dev.eclipse.org/site_login/createaccount.php). 15 | 2. Log into the [Eclipse projects forge](https://www.eclipse.org/contribute/cla), and click on 'Eclipse Contributor Agreement'. 16 | 2. Go to your [account settings](https://dev.eclipse.org/site_login/myaccount.php#open_tab_accountsettings) and add your GitHub username to your account. 17 | 3. Make sure that you _sign-off_ your Git commits in the following format: 18 | ``` Signed-off-by: Alex Smith ``` This is usually at the bottom of the commit message. You can automate this by adding the '-s' flag when you make the commits. e.g. ```git commit -s -m "Adding a cool feature"``` 19 | 4. Ensure that the email address that you make your commits with is the same one you used to sign up to the Eclipse Foundation website with. 20 | 21 | ## Contributing a change 22 | 23 | 1. [Fork the repository on GitHub](https://github.com/eclipse/paho.mqtt.embedded-c/fork) 24 | 2. Clone the forked repository onto your computer: ``` git clone https://github.com//paho.mqtt.embedded-c.git ``` 25 | 3. Create a new branch from the latest ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/develop``` 26 | 4. Make your changes 27 | 5. If developing a new feature, make sure to include JUnit tests. 28 | 6. Ensure that all new and existing tests pass. 29 | 7. Commit the changes into the branch: ``` git commit -s ``` Make sure that your commit message is meaningful and describes your changes correctly. 30 | 8. If you have a lot of commits for the change, squash them into a single / few commits. 31 | 9. Push the changes in your branch to your forked repository. 32 | 10. Finally, go to [https://github.com/eclipse/paho.mqtt.embedded-c](https://github.com/eclipse/paho.mqtt.embedded-c) and create a pull request from your "YOUR_BRANCH_NAME" branch to the ```develop``` one to request review and merge of the commits in your pushed branch. 33 | 34 | 35 | What happens next depends on the content of the patch. If it is 100% authored 36 | by the contributor and is less than 1000 lines (and meets the needs of the 37 | project), then it can be pulled into the main repository. If not, more steps 38 | are required. These are detailed in the 39 | [legal process poster](http://www.eclipse.org/legal/EclipseLegalProcessPoster.pdf). 40 | 41 | 42 | 43 | ## Developer resources: 44 | 45 | 46 | Information regarding source code management, builds, coding standards, and more. 47 | 48 | - [https://projects.eclipse.org/projects/iot.paho/developer](https://projects.eclipse.org/projects/iot.paho/developer) 49 | 50 | Contact: 51 | -------- 52 | 53 | Contact the project developers via the project's development 54 | [mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev). 55 | 56 | Search for bugs: 57 | ---------------- 58 | 59 | This project uses GitHub Issues here: [github.com/eclipse/paho.mqtt.embedded-c/issues](https://github.com/eclipse/paho.mqtt.embedded-c/issues) to track ongoing development and issues. 60 | 61 | Create a new bug: 62 | ----------------- 63 | 64 | Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome! 65 | 66 | - [Create new Paho bug](https://github.com/eclipse/paho.mqtt.embedded-c/issues) 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | -------------------------------------------------------------------------------- /MQTTClient-C/mqtt_client.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hichard/mymqtt/1b3b9367c659fdd8f132b18bcb32e5794a4fa7be/MQTTClient-C/mqtt_client.c -------------------------------------------------------------------------------- /MQTTClient-C/mqtt_client.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hichard/mymqtt/1b3b9367c659fdd8f132b18bcb32e5794a4fa7be/MQTTClient-C/mqtt_client.h -------------------------------------------------------------------------------- /MQTTPacket/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #******************************************************************************* 2 | # Copyright (c) 2017 IBM Corp. 3 | # 4 | # All rights reserved. This program and the accompanying materials 5 | # are made available under the terms of the Eclipse Public License v1.0 6 | # and Eclipse Distribution License v1.0 which accompany this distribution. 7 | # 8 | # The Eclipse Public License is available at 9 | # http://www.eclipse.org/legal/epl-v10.html 10 | # and the Eclipse Distribution License is available at 11 | # http://www.eclipse.org/org/documents/edl-v10.php. 12 | # 13 | # Contributors: 14 | # Ian Craggs - initial version 15 | #*******************************************************************************/ 16 | 17 | # MQTTPacket Library 18 | file(GLOB SOURCES "*.c") 19 | add_library(paho-embed-mqtt3c SHARED ${SOURCES}) 20 | install(TARGETS paho-embed-mqtt3c DESTINATION /usr/lib) 21 | target_compile_definitions(paho-embed-mqtt3c PRIVATE MQTT_SERVER MQTT_CLIENT) 22 | 23 | add_library(MQTTPacketClient SHARED MQTTFormat MQTTPacket 24 | MQTTSerializePublish MQTTDeserializePublish 25 | MQTTConnectClient MQTTSubscribeClient MQTTUnsubscribeClient) 26 | target_compile_definitions(MQTTPacketClient PRIVATE MQTT_CLIENT) 27 | 28 | add_library(MQTTPacketServer SHARED MQTTFormat MQTTPacket 29 | MQTTSerializePublish MQTTDeserializePublish 30 | MQTTConnectServer MQTTSubscribeServer MQTTUnsubscribeServer) 31 | target_compile_definitions(MQTTPacketServer PRIVATE MQTT_SERVER) 32 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTConnect.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, 2017 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Ian Craggs - add connack return code definitions 16 | * Xiang Rong - 442039 Add makefile to Embedded C client 17 | * Ian Craggs - fix for issue #64, bit order in connack response 18 | *******************************************************************************/ 19 | 20 | #ifndef MQTTCONNECT_H_ 21 | #define MQTTCONNECT_H_ 22 | 23 | enum connack_return_codes 24 | { 25 | MQTT_CONNECTION_ACCEPTED = 0, 26 | MQTT_UNNACCEPTABLE_PROTOCOL = 1, 27 | MQTT_CLIENTID_REJECTED = 2, 28 | MQTT_SERVER_UNAVAILABLE = 3, 29 | MQTT_BAD_USERNAME_OR_PASSWORD = 4, 30 | MQTT_NOT_AUTHORIZED = 5, 31 | }; 32 | 33 | #if !defined(DLLImport) 34 | #define DLLImport 35 | #endif 36 | #if !defined(DLLExport) 37 | #define DLLExport 38 | #endif 39 | 40 | 41 | typedef union 42 | { 43 | unsigned char all; /**< all connect flags */ 44 | #if defined(REVERSED) 45 | struct 46 | { 47 | unsigned int username : 1; /**< 3.1 user name */ 48 | unsigned int password : 1; /**< 3.1 password */ 49 | unsigned int willRetain : 1; /**< will retain setting */ 50 | unsigned int willQoS : 2; /**< will QoS value */ 51 | unsigned int will : 1; /**< will flag */ 52 | unsigned int cleansession : 1; /**< clean session flag */ 53 | unsigned int : 1; /**< unused */ 54 | } bits; 55 | #else 56 | struct 57 | { 58 | unsigned int : 1; /**< unused */ 59 | unsigned int cleansession : 1; /**< cleansession flag */ 60 | unsigned int will : 1; /**< will flag */ 61 | unsigned int willQoS : 2; /**< will QoS value */ 62 | unsigned int willRetain : 1; /**< will retain setting */ 63 | unsigned int password : 1; /**< 3.1 password */ 64 | unsigned int username : 1; /**< 3.1 user name */ 65 | } bits; 66 | #endif 67 | } MQTTConnectFlags; /**< connect flags byte */ 68 | 69 | 70 | 71 | /** 72 | * Defines the MQTT "Last Will and Testament" (LWT) settings for 73 | * the connect packet. 74 | */ 75 | typedef struct 76 | { 77 | /** The eyecatcher for this structure. must be MQTW. */ 78 | char struct_id[4]; 79 | /** The version number of this structure. Must be 0 */ 80 | int struct_version; 81 | /** The LWT topic to which the LWT message will be published. */ 82 | MQTTString topicName; 83 | /** The LWT payload. */ 84 | MQTTString message; 85 | /** 86 | * The retained flag for the LWT message (see MQTTAsync_message.retained). 87 | */ 88 | unsigned char retained; 89 | /** 90 | * The quality of service setting for the LWT message (see 91 | * MQTTAsync_message.qos and @ref qos). 92 | */ 93 | char qos; 94 | } MQTTPacket_willOptions; 95 | 96 | 97 | #define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } 98 | 99 | 100 | typedef struct 101 | { 102 | /** The eyecatcher for this structure. must be MQTC. */ 103 | char struct_id[4]; 104 | /** The version number of this structure. Must be 0 */ 105 | int struct_version; 106 | /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 107 | */ 108 | unsigned char MQTTVersion; 109 | MQTTString clientID; 110 | unsigned short keepAliveInterval; 111 | unsigned char cleansession; 112 | unsigned char willFlag; 113 | MQTTPacket_willOptions will; 114 | MQTTString username; 115 | MQTTString password; 116 | } MQTTPacket_connectData; 117 | 118 | typedef union 119 | { 120 | unsigned char all; /**< all connack flags */ 121 | #if defined(REVERSED) 122 | struct 123 | { 124 | unsigned int sessionpresent : 1; /**< session present flag */ 125 | unsigned int reserved: 7; /**< unused */ 126 | 127 | } bits; 128 | #else 129 | struct 130 | { 131 | unsigned int reserved : 7; /**< unused */ 132 | unsigned int sessionpresent : 1; /**< session present flag */ 133 | } bits; 134 | #endif 135 | } MQTTConnackFlags; /**< connack flags byte */ 136 | 137 | #define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ 138 | MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } 139 | 140 | DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); 141 | DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); 142 | 143 | DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); 144 | DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); 145 | 146 | DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); 147 | DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); 148 | const char *MQTTSerialize_connack_string(unsigned char connack_rc); 149 | 150 | #endif /* MQTTCONNECT_H_ */ 151 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTConnectClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | /** 23 | * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. 24 | * @param options the options to be used to build the connect packet 25 | * @return the length of buffer needed to contain the serialized version of the packet 26 | */ 27 | int MQTTSerialize_connectLength(MQTTPacket_connectData* options) 28 | { 29 | int len = 0; 30 | 31 | FUNC_ENTRY; 32 | 33 | if (options->MQTTVersion == 3) 34 | len = 12; /* variable depending on MQTT or MQIsdp */ 35 | else if (options->MQTTVersion == 4) 36 | len = 10; 37 | 38 | len += MQTTstrlen(options->clientID)+2; 39 | if (options->willFlag) 40 | len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; 41 | if (options->username.cstring || options->username.lenstring.data) 42 | len += MQTTstrlen(options->username)+2; 43 | if (options->password.cstring || options->password.lenstring.data) 44 | len += MQTTstrlen(options->password)+2; 45 | 46 | FUNC_EXIT_RC(len); 47 | return len; 48 | } 49 | 50 | 51 | /** 52 | * Serializes the connect options into the buffer. 53 | * @param buf the buffer into which the packet will be serialized 54 | * @param len the length in bytes of the supplied buffer 55 | * @param options the options to be used to build the connect packet 56 | * @return serialized length, or error if 0 57 | */ 58 | int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) 59 | { 60 | unsigned char *ptr = buf; 61 | MQTTHeader header = {0}; 62 | MQTTConnectFlags flags = {0}; 63 | int len = 0; 64 | int rc = -1; 65 | 66 | FUNC_ENTRY; 67 | if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) 68 | { 69 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 70 | goto exit; 71 | } 72 | 73 | header.byte = 0; 74 | header.bits.type = CONNECT; 75 | writeChar(&ptr, header.byte); /* write header */ 76 | 77 | ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ 78 | 79 | if (options->MQTTVersion == 4) 80 | { 81 | writeCString(&ptr, "MQTT"); 82 | writeChar(&ptr, (char) 4); 83 | } 84 | else 85 | { 86 | writeCString(&ptr, "MQIsdp"); 87 | writeChar(&ptr, (char) 3); 88 | } 89 | 90 | flags.all = 0; 91 | flags.bits.cleansession = options->cleansession; 92 | flags.bits.will = (options->willFlag) ? 1 : 0; 93 | if (flags.bits.will) 94 | { 95 | flags.bits.willQoS = options->will.qos; 96 | flags.bits.willRetain = options->will.retained; 97 | } 98 | 99 | if (options->username.cstring || options->username.lenstring.data) 100 | flags.bits.username = 1; 101 | if (options->password.cstring || options->password.lenstring.data) 102 | flags.bits.password = 1; 103 | 104 | writeChar(&ptr, flags.all); 105 | writeInt(&ptr, options->keepAliveInterval); 106 | writeMQTTString(&ptr, options->clientID); 107 | if (options->willFlag) 108 | { 109 | writeMQTTString(&ptr, options->will.topicName); 110 | writeMQTTString(&ptr, options->will.message); 111 | } 112 | if (flags.bits.username) 113 | writeMQTTString(&ptr, options->username); 114 | if (flags.bits.password) 115 | writeMQTTString(&ptr, options->password); 116 | 117 | rc = ptr - buf; 118 | 119 | exit: FUNC_EXIT_RC(rc); 120 | return rc; 121 | } 122 | 123 | 124 | /** 125 | * Deserializes the supplied (wire) buffer into connack data - return code 126 | * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) 127 | * @param connack_rc returned integer value of the connack return code 128 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 129 | * @param len the length in bytes of the data in the supplied buffer 130 | * @return error code. 1 is success, 0 is failure 131 | */ 132 | int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) 133 | { 134 | MQTTHeader header = {0}; 135 | unsigned char* curdata = buf; 136 | unsigned char* enddata = NULL; 137 | int rc = 0; 138 | int mylen; 139 | MQTTConnackFlags flags = {0}; 140 | 141 | FUNC_ENTRY; 142 | header.byte = readChar(&curdata); 143 | if (header.bits.type != CONNACK) 144 | goto exit; 145 | 146 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 147 | enddata = curdata + mylen; 148 | if (enddata - curdata < 2) 149 | goto exit; 150 | 151 | flags.all = readChar(&curdata); 152 | *sessionPresent = flags.bits.sessionpresent; 153 | *connack_rc = readChar(&curdata); 154 | 155 | rc = 1; 156 | exit: 157 | FUNC_EXIT_RC(rc); 158 | return rc; 159 | } 160 | 161 | 162 | /** 163 | * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket 164 | * @param buf the buffer into which the packet will be serialized 165 | * @param buflen the length in bytes of the supplied buffer, to avoid overruns 166 | * @param packettype the message type 167 | * @return serialized length, or error if 0 168 | */ 169 | int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) 170 | { 171 | MQTTHeader header = {0}; 172 | int rc = -1; 173 | unsigned char *ptr = buf; 174 | 175 | FUNC_ENTRY; 176 | if (buflen < 2) 177 | { 178 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 179 | goto exit; 180 | } 181 | header.byte = 0; 182 | header.bits.type = packettype; 183 | writeChar(&ptr, header.byte); /* write header */ 184 | 185 | ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ 186 | rc = ptr - buf; 187 | exit: 188 | FUNC_EXIT_RC(rc); 189 | return rc; 190 | } 191 | 192 | 193 | /** 194 | * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket 195 | * @param buf the buffer into which the packet will be serialized 196 | * @param buflen the length in bytes of the supplied buffer, to avoid overruns 197 | * @return serialized length, or error if 0 198 | */ 199 | int MQTTSerialize_disconnect(unsigned char* buf, int buflen) 200 | { 201 | return MQTTSerialize_zero(buf, buflen, DISCONNECT); 202 | } 203 | 204 | 205 | /** 206 | * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket 207 | * @param buf the buffer into which the packet will be serialized 208 | * @param buflen the length in bytes of the supplied buffer, to avoid overruns 209 | * @return serialized length, or error if 0 210 | */ 211 | int MQTTSerialize_pingreq(unsigned char* buf, int buflen) 212 | { 213 | return MQTTSerialize_zero(buf, buflen, PINGREQ); 214 | } 215 | 216 | const char *MQTTSerialize_connack_string(unsigned char connack_rc) 217 | { 218 | #define PAHO_STRING(s) #s 219 | #define PAHO_TOSTRING(s) PAHO_STRING(s) 220 | 221 | switch (connack_rc) 222 | { 223 | case MQTT_CONNECTION_ACCEPTED: 224 | return PAHO_TOSTRING(MQTT_CONNECTION_ACCEPTED); 225 | case MQTT_UNNACCEPTABLE_PROTOCOL: 226 | return PAHO_TOSTRING(MQTT_UNNACCEPTABLE_PROTOCOL); 227 | case MQTT_CLIENTID_REJECTED: 228 | return PAHO_TOSTRING(MQTT_CLIENTID_REJECTED); 229 | case MQTT_SERVER_UNAVAILABLE: 230 | return PAHO_TOSTRING(MQTT_SERVER_UNAVAILABLE); 231 | case MQTT_BAD_USERNAME_OR_PASSWORD: 232 | return PAHO_TOSTRING(MQTT_BAD_USERNAME_OR_PASSWORD); 233 | case MQTT_NOT_AUTHORIZED: 234 | return PAHO_TOSTRING(MQTT_NOT_AUTHORIZED); 235 | default: 236 | return "UNKNOWN_ERROR"; 237 | } 238 | } 239 | 240 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTConnectServer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "StackTrace.h" 18 | #include "MQTTPacket.h" 19 | #include 20 | 21 | #define min(a, b) ((a < b) ? a : b) 22 | 23 | 24 | /** 25 | * Validates MQTT protocol name and version combinations 26 | * @param protocol the MQTT protocol name as an MQTTString 27 | * @param version the MQTT protocol version number, as in the connect packet 28 | * @return correct MQTT combination? 1 is true, 0 is false 29 | */ 30 | int MQTTPacket_checkVersion(MQTTString* protocol, int version) 31 | { 32 | int rc = 0; 33 | 34 | if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp", 35 | min(6, protocol->lenstring.len)) == 0) 36 | rc = 1; 37 | else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT", 38 | min(4, protocol->lenstring.len)) == 0) 39 | rc = 1; 40 | return rc; 41 | } 42 | 43 | 44 | /** 45 | * Deserializes the supplied (wire) buffer into connect data structure 46 | * @param data the connect data structure to be filled out 47 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 48 | * @param len the length in bytes of the data in the supplied buffer 49 | * @return error code. 1 is success, 0 is failure 50 | */ 51 | int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len) 52 | { 53 | MQTTHeader header = {0}; 54 | MQTTConnectFlags flags = {0}; 55 | unsigned char* curdata = buf; 56 | unsigned char* enddata = &buf[len]; 57 | int rc = 0; 58 | MQTTString Protocol; 59 | int version; 60 | int mylen = 0; 61 | 62 | FUNC_ENTRY; 63 | header.byte = readChar(&curdata); 64 | if (header.bits.type != CONNECT) 65 | goto exit; 66 | 67 | curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */ 68 | 69 | if (!readMQTTLenString(&Protocol, &curdata, enddata) || 70 | enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ 71 | goto exit; 72 | 73 | version = (int)readChar(&curdata); /* Protocol version */ 74 | /* If we don't recognize the protocol version, we don't parse the connect packet on the 75 | * basis that we don't know what the format will be. 76 | */ 77 | if (MQTTPacket_checkVersion(&Protocol, version)) 78 | { 79 | flags.all = readChar(&curdata); 80 | data->cleansession = flags.bits.cleansession; 81 | data->keepAliveInterval = readInt(&curdata); 82 | if (!readMQTTLenString(&data->clientID, &curdata, enddata)) 83 | goto exit; 84 | data->willFlag = flags.bits.will; 85 | if (flags.bits.will) 86 | { 87 | data->will.qos = flags.bits.willQoS; 88 | data->will.retained = flags.bits.willRetain; 89 | if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) || 90 | !readMQTTLenString(&data->will.message, &curdata, enddata)) 91 | goto exit; 92 | } 93 | if (flags.bits.username) 94 | { 95 | if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata)) 96 | goto exit; /* username flag set, but no username supplied - invalid */ 97 | if (flags.bits.password && 98 | (enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata))) 99 | goto exit; /* password flag set, but no password supplied - invalid */ 100 | } 101 | else if (flags.bits.password) 102 | goto exit; /* password flag set without username - invalid */ 103 | rc = 1; 104 | } 105 | exit: 106 | FUNC_EXIT_RC(rc); 107 | return rc; 108 | } 109 | 110 | 111 | /** 112 | * Serializes the connack packet into the supplied buffer. 113 | * @param buf the buffer into which the packet will be serialized 114 | * @param buflen the length in bytes of the supplied buffer 115 | * @param connack_rc the integer connack return code to be used 116 | * @param sessionPresent the MQTT 3.1.1 sessionPresent flag 117 | * @return serialized length, or error if 0 118 | */ 119 | int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent) 120 | { 121 | MQTTHeader header = {0}; 122 | int rc = 0; 123 | unsigned char *ptr = buf; 124 | MQTTConnackFlags flags = {0}; 125 | 126 | FUNC_ENTRY; 127 | if (buflen < 2) 128 | { 129 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 130 | goto exit; 131 | } 132 | header.byte = 0; 133 | header.bits.type = CONNACK; 134 | writeChar(&ptr, header.byte); /* write header */ 135 | 136 | ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ 137 | 138 | flags.all = 0; 139 | flags.bits.sessionpresent = sessionPresent; 140 | writeChar(&ptr, flags.all); 141 | writeChar(&ptr, connack_rc); 142 | 143 | rc = ptr - buf; 144 | exit: 145 | FUNC_EXIT_RC(rc); 146 | return rc; 147 | } 148 | 149 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTDeserializePublish.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "StackTrace.h" 18 | #include "MQTTPacket.h" 19 | #include 20 | 21 | #define min(a, b) ((a < b) ? 1 : 0) 22 | 23 | /** 24 | * Deserializes the supplied (wire) buffer into publish data 25 | * @param dup returned integer - the MQTT dup flag 26 | * @param qos returned integer - the MQTT QoS value 27 | * @param retained returned integer - the MQTT retained flag 28 | * @param packetid returned integer - the MQTT packet identifier 29 | * @param topicName returned MQTTString - the MQTT topic in the publish 30 | * @param payload returned byte buffer - the MQTT publish payload 31 | * @param payloadlen returned integer - the length of the MQTT payload 32 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 33 | * @param buflen the length in bytes of the data in the supplied buffer 34 | * @return error code. 1 is success 35 | */ 36 | int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, 37 | unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen) 38 | { 39 | MQTTHeader header = {0}; 40 | unsigned char* curdata = buf; 41 | unsigned char* enddata = NULL; 42 | int rc = 0; 43 | int mylen = 0; 44 | 45 | FUNC_ENTRY; 46 | header.byte = readChar(&curdata); 47 | if (header.bits.type != PUBLISH) 48 | goto exit; 49 | *dup = header.bits.dup; 50 | *qos = header.bits.qos; 51 | *retained = header.bits.retain; 52 | 53 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 54 | enddata = curdata + mylen; 55 | 56 | if (!readMQTTLenString(topicName, &curdata, enddata) || 57 | enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ 58 | goto exit; 59 | 60 | if (*qos > 0) 61 | *packetid = readInt(&curdata); 62 | 63 | *payloadlen = enddata - curdata; 64 | *payload = curdata; 65 | rc = 1; 66 | exit: 67 | FUNC_EXIT_RC(rc); 68 | return rc; 69 | } 70 | 71 | 72 | 73 | /** 74 | * Deserializes the supplied (wire) buffer into an ack 75 | * @param packettype returned integer - the MQTT packet type 76 | * @param dup returned integer - the MQTT dup flag 77 | * @param packetid returned integer - the MQTT packet identifier 78 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 79 | * @param buflen the length in bytes of the data in the supplied buffer 80 | * @return error code. 1 is success, 0 is failure 81 | */ 82 | int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) 83 | { 84 | MQTTHeader header = {0}; 85 | unsigned char* curdata = buf; 86 | unsigned char* enddata = NULL; 87 | int rc = 0; 88 | int mylen; 89 | 90 | FUNC_ENTRY; 91 | header.byte = readChar(&curdata); 92 | *dup = header.bits.dup; 93 | *packettype = header.bits.type; 94 | 95 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 96 | enddata = curdata + mylen; 97 | 98 | if (enddata - curdata < 2) 99 | goto exit; 100 | *packetid = readInt(&curdata); 101 | 102 | rc = 1; 103 | exit: 104 | FUNC_EXIT_RC(rc); 105 | return rc; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTFormat.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "StackTrace.h" 18 | #include "MQTTPacket.h" 19 | 20 | #include 21 | 22 | 23 | const char* MQTTPacket_names[] = 24 | { 25 | "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", 26 | "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", 27 | "PINGREQ", "PINGRESP", "DISCONNECT" 28 | }; 29 | 30 | 31 | const char* MQTTPacket_getName(unsigned short packetid) 32 | { 33 | return MQTTPacket_names[packetid]; 34 | } 35 | 36 | 37 | int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data) 38 | { 39 | int strindex = 0; 40 | 41 | strindex = snprintf(strbuf, strbuflen, 42 | "CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d", 43 | (int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data, 44 | (int)data->cleansession, data->keepAliveInterval); 45 | if (data->willFlag) 46 | strindex += snprintf(&strbuf[strindex], strbuflen - strindex, 47 | ", will QoS %d, will retain %d, will topic %.*s, will message %.*s", 48 | data->will.qos, data->will.retained, 49 | data->will.topicName.lenstring.len, data->will.topicName.lenstring.data, 50 | data->will.message.lenstring.len, data->will.message.lenstring.data); 51 | if (data->username.lenstring.data && data->username.lenstring.len > 0) 52 | strindex += snprintf(&strbuf[strindex], strbuflen - strindex, 53 | ", user name %.*s", data->username.lenstring.len, data->username.lenstring.data); 54 | if (data->password.lenstring.data && data->password.lenstring.len > 0) 55 | strindex += snprintf(&strbuf[strindex], strbuflen - strindex, 56 | ", password %.*s", data->password.lenstring.len, data->password.lenstring.data); 57 | return strindex; 58 | } 59 | 60 | 61 | int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent) 62 | { 63 | int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc); 64 | return strindex; 65 | } 66 | 67 | 68 | int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, 69 | unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen) 70 | { 71 | int strindex = snprintf(strbuf, strbuflen, 72 | "PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s", 73 | dup, qos, retained, packetid, 74 | (topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data, 75 | payloadlen, (payloadlen < 20) ? payloadlen : 20, payload); 76 | return strindex; 77 | } 78 | 79 | 80 | int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid) 81 | { 82 | int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid); 83 | if (dup) 84 | strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup); 85 | return strindex; 86 | } 87 | 88 | 89 | int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, 90 | MQTTString topicFilters[], int requestedQoSs[]) 91 | { 92 | return snprintf(strbuf, strbuflen, 93 | "SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d", 94 | dup, packetid, count, 95 | topicFilters[0].lenstring.len, topicFilters[0].lenstring.data, 96 | requestedQoSs[0]); 97 | } 98 | 99 | 100 | int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs) 101 | { 102 | return snprintf(strbuf, strbuflen, 103 | "SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]); 104 | } 105 | 106 | 107 | int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, 108 | int count, MQTTString topicFilters[]) 109 | { 110 | return snprintf(strbuf, strbuflen, 111 | "UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s", 112 | dup, packetid, count, 113 | topicFilters[0].lenstring.len, topicFilters[0].lenstring.data); 114 | } 115 | 116 | 117 | #if defined(MQTT_CLIENT) 118 | char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) 119 | { 120 | int index = 0; 121 | int rem_length = 0; 122 | MQTTHeader header = {0}; 123 | int strindex = 0; 124 | 125 | header.byte = buf[index++]; 126 | index += MQTTPacket_decodeBuf(&buf[index], &rem_length); 127 | 128 | switch (header.bits.type) 129 | { 130 | 131 | case CONNACK: 132 | { 133 | unsigned char sessionPresent, connack_rc; 134 | if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1) 135 | strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent); 136 | } 137 | break; 138 | case PUBLISH: 139 | { 140 | unsigned char dup, retained, *payload; 141 | unsigned short packetid; 142 | int qos, payloadlen; 143 | MQTTString topicName = MQTTString_initializer; 144 | if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, 145 | &payload, &payloadlen, buf, buflen) == 1) 146 | strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, 147 | topicName, payload, payloadlen); 148 | } 149 | break; 150 | case PUBACK: 151 | case PUBREC: 152 | case PUBREL: 153 | case PUBCOMP: 154 | { 155 | unsigned char packettype, dup; 156 | unsigned short packetid; 157 | if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) 158 | strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); 159 | } 160 | break; 161 | case SUBACK: 162 | { 163 | unsigned short packetid; 164 | int maxcount = 1, count = 0; 165 | int grantedQoSs[1]; 166 | if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1) 167 | strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs); 168 | } 169 | break; 170 | case UNSUBACK: 171 | { 172 | unsigned short packetid; 173 | if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1) 174 | strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid); 175 | } 176 | break; 177 | case PINGREQ: 178 | case PINGRESP: 179 | case DISCONNECT: 180 | strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); 181 | break; 182 | } 183 | return strbuf; 184 | } 185 | #endif 186 | 187 | #if defined(MQTT_SERVER) 188 | char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) 189 | { 190 | int index = 0; 191 | int rem_length = 0; 192 | MQTTHeader header = {0}; 193 | int strindex = 0; 194 | 195 | header.byte = buf[index++]; 196 | index += MQTTPacket_decodeBuf(&buf[index], &rem_length); 197 | 198 | switch (header.bits.type) 199 | { 200 | case CONNECT: 201 | { 202 | MQTTPacket_connectData data; 203 | int rc; 204 | if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1) 205 | strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data); 206 | } 207 | break; 208 | case PUBLISH: 209 | { 210 | unsigned char dup, retained, *payload; 211 | unsigned short packetid; 212 | int qos, payloadlen; 213 | MQTTString topicName = MQTTString_initializer; 214 | if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, 215 | &payload, &payloadlen, buf, buflen) == 1) 216 | strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, 217 | topicName, payload, payloadlen); 218 | } 219 | break; 220 | case PUBACK: 221 | case PUBREC: 222 | case PUBREL: 223 | case PUBCOMP: 224 | { 225 | unsigned char packettype, dup; 226 | unsigned short packetid; 227 | if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) 228 | strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); 229 | } 230 | break; 231 | case SUBSCRIBE: 232 | { 233 | unsigned char dup; 234 | unsigned short packetid; 235 | int maxcount = 1, count = 0; 236 | MQTTString topicFilters[1]; 237 | int requestedQoSs[1]; 238 | if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count, 239 | topicFilters, requestedQoSs, buf, buflen) == 1) 240 | strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);; 241 | } 242 | break; 243 | case UNSUBSCRIBE: 244 | { 245 | unsigned char dup; 246 | unsigned short packetid; 247 | int maxcount = 1, count = 0; 248 | MQTTString topicFilters[1]; 249 | if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1) 250 | strindex = MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters); 251 | } 252 | break; 253 | case PINGREQ: 254 | case PINGRESP: 255 | case DISCONNECT: 256 | strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); 257 | break; 258 | } 259 | strbuf[strbuflen] = '\0'; 260 | return strbuf; 261 | } 262 | #endif 263 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTFormat.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #if !defined(MQTTFORMAT_H) 18 | #define MQTTFORMAT_H 19 | 20 | #include "StackTrace.h" 21 | #include "MQTTPacket.h" 22 | 23 | const char* MQTTPacket_getName(unsigned short packetid); 24 | int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data); 25 | int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent); 26 | int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, 27 | unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen); 28 | int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid); 29 | int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, 30 | MQTTString topicFilters[], int requestedQoSs[]); 31 | int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs); 32 | int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, 33 | int count, MQTTString topicFilters[]); 34 | char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); 35 | char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTPacket.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Sergio R. Caprile - non-blocking packet read functions for stream transport 16 | *******************************************************************************/ 17 | 18 | #include "StackTrace.h" 19 | #include "MQTTPacket.h" 20 | 21 | #include 22 | 23 | /** 24 | * Encodes the message length according to the MQTT algorithm 25 | * @param buf the buffer into which the encoded data is written 26 | * @param length the length to be encoded 27 | * @return the number of bytes written to buffer 28 | */ 29 | int MQTTPacket_encode(unsigned char* buf, int length) 30 | { 31 | int rc = 0; 32 | 33 | FUNC_ENTRY; 34 | do 35 | { 36 | char d = length % 128; 37 | length /= 128; 38 | /* if there are more digits to encode, set the top bit of this digit */ 39 | if (length > 0) 40 | d |= 0x80; 41 | buf[rc++] = d; 42 | } while (length > 0); 43 | FUNC_EXIT_RC(rc); 44 | return rc; 45 | } 46 | 47 | 48 | /** 49 | * Decodes the message length according to the MQTT algorithm 50 | * @param getcharfn pointer to function to read the next character from the data source 51 | * @param value the decoded length returned 52 | * @return the number of bytes read from the socket 53 | */ 54 | int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) 55 | { 56 | unsigned char c; 57 | int multiplier = 1; 58 | int len = 0; 59 | #define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 60 | 61 | FUNC_ENTRY; 62 | *value = 0; 63 | do 64 | { 65 | int rc = MQTTPACKET_READ_ERROR; 66 | 67 | if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) 68 | { 69 | rc = MQTTPACKET_READ_ERROR; /* bad data */ 70 | goto exit; 71 | } 72 | rc = (*getcharfn)(&c, 1); 73 | if (rc != 1) 74 | goto exit; 75 | *value += (c & 127) * multiplier; 76 | multiplier *= 128; 77 | } while ((c & 128) != 0); 78 | exit: 79 | FUNC_EXIT_RC(len); 80 | return len; 81 | } 82 | 83 | 84 | int MQTTPacket_len(int rem_len) 85 | { 86 | rem_len += 1; /* header byte */ 87 | 88 | /* now remaining_length field */ 89 | if (rem_len < 128) 90 | rem_len += 1; 91 | else if (rem_len < 16384) 92 | rem_len += 2; 93 | else if (rem_len < 2097151) 94 | rem_len += 3; 95 | else 96 | rem_len += 4; 97 | return rem_len; 98 | } 99 | 100 | 101 | static unsigned char* bufptr; 102 | 103 | int bufchar(unsigned char* c, int count) 104 | { 105 | int i; 106 | 107 | for (i = 0; i < count; ++i) 108 | *c = *bufptr++; 109 | return count; 110 | } 111 | 112 | 113 | int MQTTPacket_decodeBuf(unsigned char* buf, int* value) 114 | { 115 | bufptr = buf; 116 | return MQTTPacket_decode(bufchar, value); 117 | } 118 | 119 | 120 | /** 121 | * Calculates an integer from two bytes read from the input buffer 122 | * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned 123 | * @return the integer value calculated 124 | */ 125 | int readInt(unsigned char** pptr) 126 | { 127 | unsigned char* ptr = *pptr; 128 | int len = 256*(*ptr) + (*(ptr+1)); 129 | *pptr += 2; 130 | return len; 131 | } 132 | 133 | 134 | /** 135 | * Reads one character from the input buffer. 136 | * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned 137 | * @return the character read 138 | */ 139 | char readChar(unsigned char** pptr) 140 | { 141 | char c = **pptr; 142 | (*pptr)++; 143 | return c; 144 | } 145 | 146 | 147 | /** 148 | * Writes one character to an output buffer. 149 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 150 | * @param c the character to write 151 | */ 152 | void writeChar(unsigned char** pptr, char c) 153 | { 154 | **pptr = c; 155 | (*pptr)++; 156 | } 157 | 158 | 159 | /** 160 | * Writes an integer as 2 bytes to an output buffer. 161 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 162 | * @param anInt the integer to write 163 | */ 164 | void writeInt(unsigned char** pptr, int anInt) 165 | { 166 | **pptr = (unsigned char)(anInt / 256); 167 | (*pptr)++; 168 | **pptr = (unsigned char)(anInt % 256); 169 | (*pptr)++; 170 | } 171 | 172 | 173 | /** 174 | * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. 175 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 176 | * @param string the C string to write 177 | */ 178 | void writeCString(unsigned char** pptr, const char* string) 179 | { 180 | int len = strlen(string); 181 | writeInt(pptr, len); 182 | memcpy(*pptr, string, len); 183 | *pptr += len; 184 | } 185 | 186 | 187 | int getLenStringLen(char* ptr) 188 | { 189 | int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); 190 | return len; 191 | } 192 | 193 | 194 | void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) 195 | { 196 | if (mqttstring.lenstring.len > 0) 197 | { 198 | writeInt(pptr, mqttstring.lenstring.len); 199 | memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); 200 | *pptr += mqttstring.lenstring.len; 201 | } 202 | else if (mqttstring.cstring) 203 | writeCString(pptr, mqttstring.cstring); 204 | else 205 | writeInt(pptr, 0); 206 | } 207 | 208 | 209 | /** 210 | * @param mqttstring the MQTTString structure into which the data is to be read 211 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 212 | * @param enddata pointer to the end of the data: do not read beyond 213 | * @return 1 if successful, 0 if not 214 | */ 215 | int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) 216 | { 217 | int rc = 0; 218 | 219 | FUNC_ENTRY; 220 | /* the first two bytes are the length of the string */ 221 | if (enddata - (*pptr) > 1) /* enough length to read the integer? */ 222 | { 223 | mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ 224 | if (&(*pptr)[mqttstring->lenstring.len] <= enddata) 225 | { 226 | mqttstring->lenstring.data = (char*)*pptr; 227 | *pptr += mqttstring->lenstring.len; 228 | rc = 1; 229 | } 230 | } 231 | mqttstring->cstring = NULL; 232 | FUNC_EXIT_RC(rc); 233 | return rc; 234 | } 235 | 236 | 237 | /** 238 | * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string 239 | * @param mqttstring the string to return the length of 240 | * @return the length of the string 241 | */ 242 | int MQTTstrlen(MQTTString mqttstring) 243 | { 244 | int rc = 0; 245 | 246 | if (mqttstring.cstring) 247 | rc = strlen(mqttstring.cstring); 248 | else 249 | rc = mqttstring.lenstring.len; 250 | return rc; 251 | } 252 | 253 | 254 | /** 255 | * Compares an MQTTString to a C string 256 | * @param a the MQTTString to compare 257 | * @param bptr the C string to compare 258 | * @return boolean - equal or not 259 | */ 260 | int MQTTPacket_equals(MQTTString* a, char* bptr) 261 | { 262 | int alen = 0, 263 | blen = 0; 264 | char *aptr; 265 | 266 | if (a->cstring) 267 | { 268 | aptr = a->cstring; 269 | alen = strlen(a->cstring); 270 | } 271 | else 272 | { 273 | aptr = a->lenstring.data; 274 | alen = a->lenstring.len; 275 | } 276 | blen = strlen(bptr); 277 | 278 | return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); 279 | } 280 | 281 | 282 | /** 283 | * Helper function to read packet data from some source into a buffer 284 | * @param buf the buffer into which the packet will be serialized 285 | * @param buflen the length in bytes of the supplied buffer 286 | * @param getfn pointer to a function which will read any number of bytes from the needed source 287 | * @return integer MQTT packet type, or -1 on error 288 | * @note the whole message must fit into the caller's buffer 289 | */ 290 | int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) 291 | { 292 | int rc = -1; 293 | MQTTHeader header = {0}; 294 | int len = 0; 295 | int rem_len = 0; 296 | 297 | /* 1. read the header byte. This has the packet type in it */ 298 | if ((*getfn)(buf, 1) != 1) 299 | goto exit; 300 | 301 | len = 1; 302 | /* 2. read the remaining length. This is variable in itself */ 303 | MQTTPacket_decode(getfn, &rem_len); 304 | len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ 305 | 306 | /* 3. read the rest of the buffer using a callback to supply the rest of the data */ 307 | if((rem_len + len) > buflen) 308 | goto exit; 309 | if (rem_len && ((*getfn)(buf + len, rem_len) != rem_len)) 310 | goto exit; 311 | 312 | header.byte = buf[0]; 313 | rc = header.bits.type; 314 | exit: 315 | return rc; 316 | } 317 | 318 | /** 319 | * Decodes the message length according to the MQTT algorithm, non-blocking 320 | * @param trp pointer to a transport structure holding what is needed to solve getting data from it 321 | * @param value the decoded length returned 322 | * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error 323 | */ 324 | static int MQTTPacket_decodenb(MQTTTransport *trp) 325 | { 326 | unsigned char c; 327 | int rc = MQTTPACKET_READ_ERROR; 328 | 329 | FUNC_ENTRY; 330 | if(trp->len == 0){ /* initialize on first call */ 331 | trp->multiplier = 1; 332 | trp->rem_len = 0; 333 | } 334 | do { 335 | int frc; 336 | if (trp->len >= MAX_NO_OF_REMAINING_LENGTH_BYTES) 337 | goto exit; 338 | if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) 339 | goto exit; 340 | if (frc == 0){ 341 | rc = 0; 342 | goto exit; 343 | } 344 | ++(trp->len); 345 | trp->rem_len += (c & 127) * trp->multiplier; 346 | trp->multiplier *= 128; 347 | } while ((c & 128) != 0); 348 | rc = trp->len; 349 | exit: 350 | FUNC_EXIT_RC(rc); 351 | return rc; 352 | } 353 | 354 | /** 355 | * Helper function to read packet data from some source into a buffer, non-blocking 356 | * @param buf the buffer into which the packet will be serialized 357 | * @param buflen the length in bytes of the supplied buffer 358 | * @param trp pointer to a transport structure holding what is needed to solve getting data from it 359 | * @return integer MQTT packet type, 0 for call again, or -1 on error 360 | * @note the whole message must fit into the caller's buffer 361 | */ 362 | int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) 363 | { 364 | int rc = -1, frc; 365 | MQTTHeader header = {0}; 366 | 367 | switch(trp->state){ 368 | default: 369 | trp->state = 0; 370 | /*FALLTHROUGH*/ 371 | case 0: 372 | /* read the header byte. This has the packet type in it */ 373 | if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) 374 | goto exit; 375 | if (frc == 0) 376 | return 0; 377 | trp->len = 0; 378 | ++trp->state; 379 | /*FALLTHROUGH*/ 380 | /* read the remaining length. This is variable in itself */ 381 | case 1: 382 | if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) 383 | goto exit; 384 | if(frc == 0) 385 | return 0; 386 | trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ 387 | if((trp->rem_len + trp->len) > buflen) 388 | goto exit; 389 | ++trp->state; 390 | /*FALLTHROUGH*/ 391 | case 2: 392 | if(trp->rem_len){ 393 | /* read the rest of the buffer using a callback to supply the rest of the data */ 394 | if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) 395 | goto exit; 396 | if (frc == 0) 397 | return 0; 398 | trp->rem_len -= frc; 399 | trp->len += frc; 400 | if(trp->rem_len) 401 | return 0; 402 | } 403 | header.byte = buf[0]; 404 | rc = header.bits.type; 405 | break; 406 | } 407 | 408 | exit: 409 | trp->state = 0; 410 | return rc; 411 | } 412 | 413 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTPacket.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTPACKET_H_ 19 | #define MQTTPACKET_H_ 20 | 21 | #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ 22 | extern "C" { 23 | #endif 24 | 25 | #if defined(WIN32_DLL) || defined(WIN64_DLL) 26 | #define DLLImport __declspec(dllimport) 27 | #define DLLExport __declspec(dllexport) 28 | #elif defined(LINUX_SO) 29 | #define DLLImport extern 30 | #define DLLExport __attribute__ ((visibility ("default"))) 31 | #else 32 | #define DLLImport 33 | #define DLLExport 34 | #endif 35 | 36 | enum errors 37 | { 38 | MQTTPACKET_BUFFER_TOO_SHORT = -2, 39 | MQTTPACKET_READ_ERROR = -1, 40 | MQTTPACKET_READ_COMPLETE 41 | }; 42 | 43 | enum msgTypes 44 | { 45 | CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, 46 | PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, 47 | PINGREQ, PINGRESP, DISCONNECT 48 | }; 49 | 50 | /** 51 | * Bitfields for the MQTT header byte. 52 | */ 53 | typedef union 54 | { 55 | unsigned char byte; /**< the whole byte */ 56 | #if defined(REVERSED) 57 | struct 58 | { 59 | unsigned int type : 4; /**< message type nibble */ 60 | unsigned int dup : 1; /**< DUP flag bit */ 61 | unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ 62 | unsigned int retain : 1; /**< retained flag bit */ 63 | } bits; 64 | #else 65 | struct 66 | { 67 | unsigned int retain : 1; /**< retained flag bit */ 68 | unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ 69 | unsigned int dup : 1; /**< DUP flag bit */ 70 | unsigned int type : 4; /**< message type nibble */ 71 | } bits; 72 | #endif 73 | } MQTTHeader; 74 | 75 | typedef struct 76 | { 77 | int len; 78 | char* data; 79 | } MQTTLenString; 80 | 81 | typedef struct 82 | { 83 | char* cstring; 84 | MQTTLenString lenstring; 85 | } MQTTString; 86 | 87 | #define MQTTString_initializer {NULL, {0, NULL}} 88 | 89 | int MQTTstrlen(MQTTString mqttstring); 90 | 91 | #include "MQTTConnect.h" 92 | #include "MQTTPublish.h" 93 | #include "MQTTSubscribe.h" 94 | #include "MQTTUnsubscribe.h" 95 | #include "MQTTFormat.h" 96 | 97 | DLLExport int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); 98 | DLLExport int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); 99 | 100 | int MQTTPacket_len(int rem_len); 101 | DLLExport int MQTTPacket_equals(MQTTString* a, char* b); 102 | 103 | DLLExport int MQTTPacket_encode(unsigned char* buf, int length); 104 | int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); 105 | int MQTTPacket_decodeBuf(unsigned char* buf, int* value); 106 | 107 | int readInt(unsigned char** pptr); 108 | char readChar(unsigned char** pptr); 109 | void writeChar(unsigned char** pptr, char c); 110 | void writeInt(unsigned char** pptr, int anInt); 111 | int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); 112 | void writeCString(unsigned char** pptr, const char* string); 113 | void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); 114 | 115 | DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); 116 | 117 | typedef struct { 118 | int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ 119 | void *sck; /* pointer to whatever the system may use to identify the transport */ 120 | int multiplier; 121 | int rem_len; 122 | int len; 123 | char state; 124 | }MQTTTransport; 125 | 126 | int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); 127 | 128 | #ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ 129 | } 130 | #endif 131 | 132 | 133 | #endif /* MQTTPACKET_H_ */ 134 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTPublish.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTPUBLISH_H_ 19 | #define MQTTPUBLISH_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, 29 | MQTTString topicName, unsigned char* payload, int payloadlen); 30 | 31 | DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, 32 | unsigned char** payload, int* payloadlen, unsigned char* buf, int len); 33 | 34 | DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); 35 | DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); 36 | DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); 37 | 38 | #endif /* MQTTPUBLISH_H_ */ 39 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSerializePublish.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 16 | *******************************************************************************/ 17 | 18 | #include "MQTTPacket.h" 19 | #include "StackTrace.h" 20 | 21 | #include 22 | 23 | 24 | /** 25 | * Determines the length of the MQTT publish packet that would be produced using the supplied parameters 26 | * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) 27 | * @param topicName the topic name to be used in the publish 28 | * @param payloadlen the length of the payload to be sent 29 | * @return the length of buffer needed to contain the serialized version of the packet 30 | */ 31 | int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) 32 | { 33 | int len = 0; 34 | 35 | len += 2 + MQTTstrlen(topicName) + payloadlen; 36 | if (qos > 0) 37 | len += 2; /* packetid */ 38 | return len; 39 | } 40 | 41 | 42 | /** 43 | * Serializes the supplied publish data into the supplied buffer, ready for sending 44 | * @param buf the buffer into which the packet will be serialized 45 | * @param buflen the length in bytes of the supplied buffer 46 | * @param dup integer - the MQTT dup flag 47 | * @param qos integer - the MQTT QoS value 48 | * @param retained integer - the MQTT retained flag 49 | * @param packetid integer - the MQTT packet identifier 50 | * @param topicName MQTTString - the MQTT topic in the publish 51 | * @param payload byte buffer - the MQTT publish payload 52 | * @param payloadlen integer - the length of the MQTT payload 53 | * @return the length of the serialized data. <= 0 indicates error 54 | */ 55 | int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, 56 | MQTTString topicName, unsigned char* payload, int payloadlen) 57 | { 58 | unsigned char *ptr = buf; 59 | MQTTHeader header = {0}; 60 | int rem_len = 0; 61 | int rc = 0; 62 | 63 | FUNC_ENTRY; 64 | if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) 65 | { 66 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 67 | goto exit; 68 | } 69 | 70 | header.bits.type = PUBLISH; 71 | header.bits.dup = dup; 72 | header.bits.qos = qos; 73 | header.bits.retain = retained; 74 | writeChar(&ptr, header.byte); /* write header */ 75 | 76 | ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; 77 | 78 | writeMQTTString(&ptr, topicName); 79 | 80 | if (qos > 0) 81 | writeInt(&ptr, packetid); 82 | 83 | memcpy(ptr, payload, payloadlen); 84 | ptr += payloadlen; 85 | 86 | rc = ptr - buf; 87 | 88 | exit: 89 | FUNC_EXIT_RC(rc); 90 | return rc; 91 | } 92 | 93 | 94 | 95 | /** 96 | * Serializes the ack packet into the supplied buffer. 97 | * @param buf the buffer into which the packet will be serialized 98 | * @param buflen the length in bytes of the supplied buffer 99 | * @param type the MQTT packet type 100 | * @param dup the MQTT dup flag 101 | * @param packetid the MQTT packet identifier 102 | * @return serialized length, or error if 0 103 | */ 104 | int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) 105 | { 106 | MQTTHeader header = {0}; 107 | int rc = 0; 108 | unsigned char *ptr = buf; 109 | 110 | FUNC_ENTRY; 111 | if (buflen < 4) 112 | { 113 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 114 | goto exit; 115 | } 116 | header.bits.type = packettype; 117 | header.bits.dup = dup; 118 | header.bits.qos = (packettype == PUBREL) ? 1 : 0; 119 | writeChar(&ptr, header.byte); /* write header */ 120 | 121 | ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ 122 | writeInt(&ptr, packetid); 123 | rc = ptr - buf; 124 | exit: 125 | FUNC_EXIT_RC(rc); 126 | return rc; 127 | } 128 | 129 | 130 | /** 131 | * Serializes a puback packet into the supplied buffer. 132 | * @param buf the buffer into which the packet will be serialized 133 | * @param buflen the length in bytes of the supplied buffer 134 | * @param packetid integer - the MQTT packet identifier 135 | * @return serialized length, or error if 0 136 | */ 137 | int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) 138 | { 139 | return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); 140 | } 141 | 142 | 143 | /** 144 | * Serializes a pubrel packet into the supplied buffer. 145 | * @param buf the buffer into which the packet will be serialized 146 | * @param buflen the length in bytes of the supplied buffer 147 | * @param dup integer - the MQTT dup flag 148 | * @param packetid integer - the MQTT packet identifier 149 | * @return serialized length, or error if 0 150 | */ 151 | int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) 152 | { 153 | return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); 154 | } 155 | 156 | 157 | /** 158 | * Serializes a pubrel packet into the supplied buffer. 159 | * @param buf the buffer into which the packet will be serialized 160 | * @param buflen the length in bytes of the supplied buffer 161 | * @param packetid integer - the MQTT packet identifier 162 | * @return serialized length, or error if 0 163 | */ 164 | int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) 165 | { 166 | return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSubscribe.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTSUBSCRIBE_H_ 19 | #define MQTTSUBSCRIBE_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, 29 | int count, MQTTString topicFilters[], int requestedQoSs[]); 30 | 31 | DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, 32 | int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); 33 | 34 | DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); 35 | 36 | DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len); 37 | 38 | 39 | #endif /* MQTTSUBSCRIBE_H_ */ 40 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSubscribeClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | /** 23 | * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters 24 | * @param count the number of topic filter strings in topicFilters 25 | * @param topicFilters the array of topic filter strings to be used in the publish 26 | * @return the length of buffer needed to contain the serialized version of the packet 27 | */ 28 | int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) 29 | { 30 | int i; 31 | int len = 2; /* packetid */ 32 | 33 | for (i = 0; i < count; ++i) 34 | len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ 35 | return len; 36 | } 37 | 38 | 39 | /** 40 | * Serializes the supplied subscribe data into the supplied buffer, ready for sending 41 | * @param buf the buffer into which the packet will be serialized 42 | * @param buflen the length in bytes of the supplied bufferr 43 | * @param dup integer - the MQTT dup flag 44 | * @param packetid integer - the MQTT packet identifier 45 | * @param count - number of members in the topicFilters and reqQos arrays 46 | * @param topicFilters - array of topic filter names 47 | * @param requestedQoSs - array of requested QoS 48 | * @return the length of the serialized data. <= 0 indicates error 49 | */ 50 | int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, 51 | MQTTString topicFilters[], int requestedQoSs[]) 52 | { 53 | unsigned char *ptr = buf; 54 | MQTTHeader header = {0}; 55 | int rem_len = 0; 56 | int rc = 0; 57 | int i = 0; 58 | 59 | FUNC_ENTRY; 60 | if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) 61 | { 62 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 63 | goto exit; 64 | } 65 | 66 | header.byte = 0; 67 | header.bits.type = SUBSCRIBE; 68 | header.bits.dup = dup; 69 | header.bits.qos = 1; 70 | writeChar(&ptr, header.byte); /* write header */ 71 | 72 | ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; 73 | 74 | writeInt(&ptr, packetid); 75 | 76 | for (i = 0; i < count; ++i) 77 | { 78 | writeMQTTString(&ptr, topicFilters[i]); 79 | writeChar(&ptr, requestedQoSs[i]); 80 | } 81 | 82 | rc = ptr - buf; 83 | exit: 84 | FUNC_EXIT_RC(rc); 85 | return rc; 86 | } 87 | 88 | 89 | 90 | /** 91 | * Deserializes the supplied (wire) buffer into suback data 92 | * @param packetid returned integer - the MQTT packet identifier 93 | * @param maxcount - the maximum number of members allowed in the grantedQoSs array 94 | * @param count returned integer - number of members in the grantedQoSs array 95 | * @param grantedQoSs returned array of integers - the granted qualities of service 96 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 97 | * @param buflen the length in bytes of the data in the supplied buffer 98 | * @return error code. 1 is success, 0 is failure 99 | */ 100 | int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen) 101 | { 102 | MQTTHeader header = {0}; 103 | unsigned char* curdata = buf; 104 | unsigned char* enddata = NULL; 105 | int rc = 0; 106 | int mylen; 107 | 108 | FUNC_ENTRY; 109 | header.byte = readChar(&curdata); 110 | if (header.bits.type != SUBACK) 111 | goto exit; 112 | 113 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 114 | enddata = curdata + mylen; 115 | if (enddata - curdata < 2) 116 | goto exit; 117 | 118 | *packetid = readInt(&curdata); 119 | 120 | *count = 0; 121 | while (curdata < enddata) 122 | { 123 | if (*count > maxcount) 124 | { 125 | rc = -1; 126 | goto exit; 127 | } 128 | grantedQoSs[(*count)++] = readChar(&curdata); 129 | } 130 | 131 | rc = 1; 132 | exit: 133 | FUNC_EXIT_RC(rc); 134 | return rc; 135 | } 136 | 137 | 138 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSubscribeServer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | 23 | /** 24 | * Deserializes the supplied (wire) buffer into subscribe data 25 | * @param dup integer returned - the MQTT dup flag 26 | * @param packetid integer returned - the MQTT packet identifier 27 | * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays 28 | * @param count - number of members in the topicFilters and requestedQoSs arrays 29 | * @param topicFilters - array of topic filter names 30 | * @param requestedQoSs - array of requested QoS 31 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 32 | * @param buflen the length in bytes of the data in the supplied buffer 33 | * @return the length of the serialized data. <= 0 indicates error 34 | */ 35 | int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], 36 | int requestedQoSs[], unsigned char* buf, int buflen) 37 | { 38 | MQTTHeader header = {0}; 39 | unsigned char* curdata = buf; 40 | unsigned char* enddata = NULL; 41 | int rc = -1; 42 | int mylen = 0; 43 | 44 | FUNC_ENTRY; 45 | header.byte = readChar(&curdata); 46 | if (header.bits.type != SUBSCRIBE) 47 | goto exit; 48 | *dup = header.bits.dup; 49 | 50 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 51 | enddata = curdata + mylen; 52 | 53 | *packetid = readInt(&curdata); 54 | 55 | *count = 0; 56 | while (curdata < enddata) 57 | { 58 | if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) 59 | goto exit; 60 | if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */ 61 | goto exit; 62 | requestedQoSs[*count] = readChar(&curdata); 63 | (*count)++; 64 | } 65 | 66 | rc = 1; 67 | exit: 68 | FUNC_EXIT_RC(rc); 69 | return rc; 70 | } 71 | 72 | 73 | /** 74 | * Serializes the supplied suback data into the supplied buffer, ready for sending 75 | * @param buf the buffer into which the packet will be serialized 76 | * @param buflen the length in bytes of the supplied buffer 77 | * @param packetid integer - the MQTT packet identifier 78 | * @param count - number of members in the grantedQoSs array 79 | * @param grantedQoSs - array of granted QoS 80 | * @return the length of the serialized data. <= 0 indicates error 81 | */ 82 | int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs) 83 | { 84 | MQTTHeader header = {0}; 85 | int rc = -1; 86 | unsigned char *ptr = buf; 87 | int i; 88 | 89 | FUNC_ENTRY; 90 | if (buflen < 2 + count) 91 | { 92 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 93 | goto exit; 94 | } 95 | header.byte = 0; 96 | header.bits.type = SUBACK; 97 | writeChar(&ptr, header.byte); /* write header */ 98 | 99 | ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */ 100 | 101 | writeInt(&ptr, packetid); 102 | 103 | for (i = 0; i < count; ++i) 104 | writeChar(&ptr, grantedQoSs[i]); 105 | 106 | rc = ptr - buf; 107 | exit: 108 | FUNC_EXIT_RC(rc); 109 | return rc; 110 | } 111 | 112 | 113 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTUnsubscribe.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTUNSUBSCRIBE_H_ 19 | #define MQTTUNSUBSCRIBE_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, 29 | int count, MQTTString topicFilters[]); 30 | 31 | DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], 32 | unsigned char* buf, int len); 33 | 34 | DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); 35 | 36 | DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); 37 | 38 | #endif /* MQTTUNSUBSCRIBE_H_ */ 39 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTUnsubscribeClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | /** 23 | * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters 24 | * @param count the number of topic filter strings in topicFilters 25 | * @param topicFilters the array of topic filter strings to be used in the publish 26 | * @return the length of buffer needed to contain the serialized version of the packet 27 | */ 28 | int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) 29 | { 30 | int i; 31 | int len = 2; /* packetid */ 32 | 33 | for (i = 0; i < count; ++i) 34 | len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ 35 | return len; 36 | } 37 | 38 | 39 | /** 40 | * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending 41 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 42 | * @param buflen the length in bytes of the data in the supplied buffer 43 | * @param dup integer - the MQTT dup flag 44 | * @param packetid integer - the MQTT packet identifier 45 | * @param count - number of members in the topicFilters array 46 | * @param topicFilters - array of topic filter names 47 | * @return the length of the serialized data. <= 0 indicates error 48 | */ 49 | int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, 50 | int count, MQTTString topicFilters[]) 51 | { 52 | unsigned char *ptr = buf; 53 | MQTTHeader header = {0}; 54 | int rem_len = 0; 55 | int rc = -1; 56 | int i = 0; 57 | 58 | FUNC_ENTRY; 59 | if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) 60 | { 61 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 62 | goto exit; 63 | } 64 | 65 | header.byte = 0; 66 | header.bits.type = UNSUBSCRIBE; 67 | header.bits.dup = dup; 68 | header.bits.qos = 1; 69 | writeChar(&ptr, header.byte); /* write header */ 70 | 71 | ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; 72 | 73 | writeInt(&ptr, packetid); 74 | 75 | for (i = 0; i < count; ++i) 76 | writeMQTTString(&ptr, topicFilters[i]); 77 | 78 | rc = ptr - buf; 79 | exit: 80 | FUNC_EXIT_RC(rc); 81 | return rc; 82 | } 83 | 84 | 85 | /** 86 | * Deserializes the supplied (wire) buffer into unsuback data 87 | * @param packetid returned integer - the MQTT packet identifier 88 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 89 | * @param buflen the length in bytes of the data in the supplied buffer 90 | * @return error code. 1 is success, 0 is failure 91 | */ 92 | int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) 93 | { 94 | unsigned char type = 0; 95 | unsigned char dup = 0; 96 | int rc = 0; 97 | 98 | FUNC_ENTRY; 99 | rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); 100 | if (type == UNSUBACK) 101 | rc = 1; 102 | FUNC_EXIT_RC(rc); 103 | return rc; 104 | } 105 | 106 | 107 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTUnsubscribeServer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | 23 | /** 24 | * Deserializes the supplied (wire) buffer into unsubscribe data 25 | * @param dup integer returned - the MQTT dup flag 26 | * @param packetid integer returned - the MQTT packet identifier 27 | * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays 28 | * @param count - number of members in the topicFilters and requestedQoSs arrays 29 | * @param topicFilters - array of topic filter names 30 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 31 | * @param buflen the length in bytes of the data in the supplied buffer 32 | * @return the length of the serialized data. <= 0 indicates error 33 | */ 34 | int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], 35 | unsigned char* buf, int len) 36 | { 37 | MQTTHeader header = {0}; 38 | unsigned char* curdata = buf; 39 | unsigned char* enddata = NULL; 40 | int rc = 0; 41 | int mylen = 0; 42 | 43 | FUNC_ENTRY; 44 | header.byte = readChar(&curdata); 45 | if (header.bits.type != UNSUBSCRIBE) 46 | goto exit; 47 | *dup = header.bits.dup; 48 | 49 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 50 | enddata = curdata + mylen; 51 | 52 | *packetid = readInt(&curdata); 53 | 54 | *count = 0; 55 | while (curdata < enddata) 56 | { 57 | if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) 58 | goto exit; 59 | (*count)++; 60 | } 61 | 62 | rc = 1; 63 | exit: 64 | FUNC_EXIT_RC(rc); 65 | return rc; 66 | } 67 | 68 | 69 | /** 70 | * Serializes the supplied unsuback data into the supplied buffer, ready for sending 71 | * @param buf the buffer into which the packet will be serialized 72 | * @param buflen the length in bytes of the supplied buffer 73 | * @param packetid integer - the MQTT packet identifier 74 | * @return the length of the serialized data. <= 0 indicates error 75 | */ 76 | int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid) 77 | { 78 | MQTTHeader header = {0}; 79 | int rc = 0; 80 | unsigned char *ptr = buf; 81 | 82 | FUNC_ENTRY; 83 | if (buflen < 2) 84 | { 85 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 86 | goto exit; 87 | } 88 | header.byte = 0; 89 | header.bits.type = UNSUBACK; 90 | writeChar(&ptr, header.byte); /* write header */ 91 | 92 | ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ 93 | 94 | writeInt(&ptr, packetid); 95 | 96 | rc = ptr - buf; 97 | exit: 98 | FUNC_EXIT_RC(rc); 99 | return rc; 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /MQTTPacket/src/StackTrace.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Ian Craggs - fix for bug #434081 16 | *******************************************************************************/ 17 | 18 | #ifndef STACKTRACE_H_ 19 | #define STACKTRACE_H_ 20 | 21 | #include 22 | #define NOSTACKTRACE 1 23 | 24 | #if defined(NOSTACKTRACE) 25 | #define FUNC_ENTRY 26 | #define FUNC_ENTRY_NOLOG 27 | #define FUNC_ENTRY_MED 28 | #define FUNC_ENTRY_MAX 29 | #define FUNC_EXIT 30 | #define FUNC_EXIT_NOLOG 31 | #define FUNC_EXIT_MED 32 | #define FUNC_EXIT_MAX 33 | #define FUNC_EXIT_RC(x) 34 | #define FUNC_EXIT_MED_RC(x) 35 | #define FUNC_EXIT_MAX_RC(x) 36 | 37 | #else 38 | 39 | #if defined(WIN32) 40 | #define inline __inline 41 | #define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM) 42 | #define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1) 43 | #define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM) 44 | #define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM) 45 | #define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM) 46 | #define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1) 47 | #define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM) 48 | #define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM) 49 | #define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM) 50 | #define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM) 51 | #define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM) 52 | #else 53 | #define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM) 54 | #define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1) 55 | #define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM) 56 | #define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM) 57 | #define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM) 58 | #define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1) 59 | #define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM) 60 | #define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM) 61 | #define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM) 62 | #define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM) 63 | #define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM) 64 | 65 | void StackTrace_entry(const char* name, int line, int trace); 66 | void StackTrace_exit(const char* name, int line, void* return_value, int trace); 67 | 68 | void StackTrace_printStack(FILE* dest); 69 | char* StackTrace_get(unsigned long); 70 | 71 | #endif 72 | 73 | #endif 74 | 75 | 76 | 77 | 78 | #endif /* STACKTRACE_H_ */ 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # my-mqtt 2 | ## 1、介绍 3 | - [Paho MQTT](http://www.eclipse.org/paho/downloads.php) 是 Eclipse 实现的基于 MQTT 协议的客户端, 4 | 本软件包是在 Eclipse [paho-mqtt](https://github.com/eclipse/paho.mqtt.embedded-c) 源码包的基础上设计的一套 MQTT 客户端程序。 5 | 6 | - 原本rt-thread的packages中已经有了paho-mqtt软件包,但是使用中发现不少问题。首先,占用内存大、发送一次数据来回拷贝的次数多, 7 | 其次,使用pipe通信,pipe转发mqtt订阅数据包的流程是存在问题;最后,代码结构混乱,函数及变量命令有win风格、也有linux风格。 8 | 9 | - 鉴于原有的paho-mqtt软件包存在这样或那样的问题,于是本人重写的该软件包,命名为my-mqtt。该软件包也是基于Eclispe的Paho MQTT来 10 | 实现的,参考了很多其它厂家的mqtt协议栈实现,力求这是最简洁,最稳定的实现,以达到商业应用的目的。 11 | 12 | - my-mqtt实现了可商用的mqtt协议栈,它支持mqtt协议定义的Qos0、Qos1、Qos2所有协议标准等级的订阅,但是由于为了简单、可靠,处理机制 13 | 上较为简单。比如,发布一次主题,如果是Qos1或者Qos2,要等待ACK,这会影响发布主题的速率。但是如果要高效率就要像TCP那样,建立滑动 14 | 窗口的机制,这又太为复杂了。事实上目前,商用的mqtt协议栈,笔者研究了很多家的,基本都是这样简单粗暴的处理。如果用户,有更为高效的实现 15 | 方式,欢迎PR。我们目的都是为rt-thread提供一个稳定可靠,能够商用的mqtt客户端软件包。 16 | 17 | - my-mqtt目录组装和之前的paho-mqtt软件包相似,代码组织和使用方式及调用函数都是相同的。本人直接拷贝了以前paho-mqtt软件包的文档。使用方式 18 | 都可以参考之前的软件包中的文档。 19 | 20 | - 该软件包命名为my-mqtt,只是为了和之前的paho-mqtt软件包区分,这是一个全新的软件包。但是,对外基本api和paho-mqtt保持兼容,并且还扩展提供了 21 | 一些api,以满足商业应用的需求。使用paho-mqtt的用户可以无缝切换到my-mqtt软件包。 22 | 23 | - `my-mqtt` 软件包功能特点以及 MQTT 协议介绍请参考 [软件包详细介绍](docs/introduction.md)。 24 | 25 | ### 1.1 目录结构 26 | 27 | `my-mqtt` 软件包目录结构如下所示: 28 | 29 | ``` 30 | my-mqtt 31 | ├───docs 32 | │ └───figures // 文档使用图片 33 | │ │ api.md // API 使用说明 34 | │ │ introduction.md // 介绍文档 35 | │ │ principle.md // 实现原理 36 | │ │ README.md // 文档结构说明 37 | │ │ samples.md // 软件包示例 38 | │ │ user-guide.md // 使用说明 39 | │ └───version.md // 版本 40 | ├───MQTTClient-C // 移植实现文件 41 | ├───MQTTPacket // 源文件 42 | ├───samples // 示例代码 43 | │ mqtt_sample.c // 软件包应用示例代码 44 | ├───tests // mqtt 功能测试程序 45 | │ LICENSE // 软件包许可证 46 | │ README.md // 软件包使用说明 47 | └───SConscript // RT-Thread 默认的构建脚本 48 | ``` 49 | 50 | ### 1.2 许可证 51 | 52 | `my-mqtt` package 遵循 Eclipse Public License - v 1.0 许可,详见 `LICENSE` 文件。 53 | 54 | * 维护:hichard 55 | * 主页:https://github.com/hichard/mymqtt 56 | -------------------------------------------------------------------------------- /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('MQTTPacket/src/*.c') 9 | 10 | src += ['MQTTClient-C/mqtt_client.c'] 11 | 12 | if GetDepend(['PKG_USING_MYMQTT_EXAMPLE']): 13 | src += Glob('samples/*.c') 14 | 15 | if GetDepend(['PKG_USING_MYMQTT_TEST']): 16 | src += Glob('tests/*.c') 17 | 18 | path = [cwd + '/MQTTPacket/src'] 19 | path += [cwd + '/MQTTClient-C'] 20 | 21 | group = DefineGroup('my-mqtt', src, depend = ['PKG_USING_MYMQTT'], CPPPATH = path) 22 | 23 | Return('group') 24 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 文档 2 | 3 | ## 软件包地址 4 | 5 | - https://github.com/RT-Thread-packages/paho-mqtt 6 | 7 | ## 文档列表 8 | 9 | |**文件名** |**描述**| 10 | |:----- |:----| 11 | |[version.md](version.md) |版本信息| 12 | |[introduction.md](introduction.md) |详细介绍| 13 | |[principle.md](principle.md) |工作原理| 14 | |[user-guide.md](user-guide.md) |使用指南| 15 | |[api.md](api.md) |API 说明| 16 | |[samples.md](samples.md) |示例说明| 17 | 18 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | # MQTT API 介绍 2 | 3 | ## 订阅列表 4 | 5 | Paho MQTT 中采用订阅列表的形式进行多个 Topic 的订阅,订阅列表存储在 `MQTTClient` 结构体实例中,在 MQTT 启动前配置,如下所示: 6 | 7 | ```c 8 | ... // 省略代码 9 | 10 | MQTTClient client; 11 | 12 | ... // 省略代码 13 | 14 | /* set subscribe table and event callback */ 15 | client.messageHandlers[0].topicFilter = MQTT_SUBTOPIC; 16 | client.messageHandlers[0].callback = mqtt_sub_callback; 17 | client.messageHandlers[0].qos = QOS1; 18 | ``` 19 | 20 | 详细的代码讲解请参考 Samples 章节,订阅列表的最大数量可以由 `menuconfig` 中的 `Max pahomqtt subscribe topic handlers` 选项进行配置。 21 | 22 | ## callback 23 | 24 | paho-mqtt 使用 callback 的方式向用户提供 MQTT 的工作状态以及相关事件的处理,需要在 `MQTTClient` 结构体实例中注册使用。 25 | 26 | |callback 名称 |描述| 27 | |:----- |:----| 28 | |connect_callback |MQTT 连接成功的回调| 29 | |online_callback |MQTT 客户端成功上线的回调| 30 | |offline_callback |MQTT 客户端掉线的回调| 31 | |defaultMessageHandler |默认的订阅消息接收回调| 32 | |messageHandlers[x].callback |订阅列表中对应的订阅消息接收回调| 33 | 34 | 用户可以使用 `defaultMessageHandler` 回调默认处理接收到的订阅消息,也可以使用 `messageHandlers` 订阅列表,为 `messageHandlers` 数组中对应的每一个 Topic 提供一个独立的订阅消息接收回调。 35 | 36 | ## MQTT_URI 37 | 38 | paho-mqtt 中提供了 uri 解析功能,可以解析域名地址、ipv4 和 ipv6 地址,可解析 `tcp://` 和 `ssl://` 类型的 URI,用户只需要按照要求填写可用的 uri 即可。 39 | 40 | - uri 示例: 41 | 42 | ``` 43 | domain 类型 44 | tcp://iot.eclipse.org:1883 45 | 46 | ipv4 类型 47 | tcp://192.168.10.1:1883 48 | ssl://192.168.10.1:1884 49 | 50 | ipv6 类型 51 | tcp://[fe80::20c:29ff:fe9a:a07e]:1883 52 | ssl://[fe80::20c:29ff:fe9a:a07e]:1884 53 | ``` 54 | 55 | ## paho_mqtt_start 56 | 57 | ```c 58 | int paho_mqtt_start(MQTTClient *client); 59 | ``` 60 | 61 | |**参数** |**描述**| 62 | |:----- |:----| 63 | |client |MQTT 客户端实例对象| 64 | |return |0 : 成功; 其他 : 失败| 65 | 66 | 该函数启动 MQTT 客户端,根据配置项订阅相应的主题。 67 | 68 | ## paho_mqtt_stop 69 | 70 | ```c 71 | int paho_mqtt_stop(MQTTClient *client); 72 | ``` 73 | 74 | | **参数** | **描述** | 75 | | :------- | :-------------------- | 76 | | client | MQTT 客户端实例对象 | 77 | | return | 0 : 成功; 其他 : 失败 | 78 | 79 | 该函数关闭 MQTT 客户端,并且释放客户端对象申请的空间。 80 | 81 | ## paho_mqtt_subscribe 82 | 83 | ```c 84 | int paho_mqtt_subscribe(MQTTClient *client, enum QoS qos, const char *topic, subscribe_cb callback); 85 | ``` 86 | 87 | | **参数** | **描述** | 88 | | :------- | :------------------------------- | 89 | | client | MQTT 客户端实例对象 | 90 | | qos | 订阅的 QOS 级别,目前只支持 QOS1 | 91 | | topic | 需要订阅的主题 | 92 | | callback | 订阅主题获取数据时执行的回调函数 | 93 | | return | 0 : 成功; 其他 : 失败 | 94 | 95 | 该函数用于客户端订阅新的 Topic,并且注册数据获取回调函数。 96 | 97 | ## paho_mqtt_unsubscribe 98 | 99 | ```c 100 | int paho_mqtt_unsubscribe(MQTTClient *client, const char *topic); 101 | ``` 102 | 103 | | **参数** | **描述** | 104 | | :------- | :-------------------- | 105 | | client | MQTT 客户端实例对象 | 106 | | topic | 需要取消订阅的主题 | 107 | | return | 0 : 成功; 其他 : 失败 | 108 | 109 | 该函数用于客户端取消指定 Topic 的订阅。 110 | 111 | ## paho_mqtt_publish 112 | 113 | ```c 114 | int paho_mqtt_publish(MQTTClient *client, enum QoS qos, const char *topic, const char *msg_str); 115 | ``` 116 | 117 | | **参数** | **描述** | 118 | | :------- | :------------------------------- | 119 | | client | MQTT 客户端实例对象 | 120 | | qos | 发送的 QOS 级别,目前只支持 QOS1 | 121 | | topic | 数据发送的主题 | 122 | | msg_str | 需要发送的数据指针 | 123 | | return | 0 : 成功; 其他 : 失败 | 124 | 125 | 该函数用于客户端向指定订阅的 Topic 发送数据。 126 | 127 | ## paho_mqtt_control 128 | 129 | ```c 130 | int paho_mqtt_control(MQTTClient *client, int cmd, void *arg); 131 | ``` 132 | 133 | | **参数** | **描述** | 134 | | :------- | :-------------------- | 135 | | client | MQTT 客户端实例对象 | 136 | | cmd | 用于控制的参数类型 | 137 | | arg | 用于控制的参数值 | 138 | | return | 0 : 成功; 其他 : 失败 | 139 | 140 | 该函数用于控制客户端部分参数的值,参数的类型有如下几种: 141 | 142 | | 参数名称 | 描述 | 143 | | -------------------------------- | ---------------------------------------------- | 144 | | MQTT_CTRL_SET_CONN_TIMEO | 用于设置客户端连接的超时时间 | 145 | | MQTT_CTRL_SET_RECONN_INTERVAL | 用于设备客户端断线重新连接的间隔时间 | 146 | | MQTT_CTRL_SET_KEEPALIVE_INTERVAL | 用于设置客户端发送 ping 的间隔时间 | 147 | | MQTT_CTRL_PUBLISH_BLOCK | 用于设置客户端发送数据时阻塞模式还是非阻塞模式 | 148 | 149 | -------------------------------------------------------------------------------- /docs/figures/mqtt_principle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hichard/mymqtt/1b3b9367c659fdd8f132b18bcb32e5794a4fa7be/docs/figures/mqtt_principle.png -------------------------------------------------------------------------------- /docs/figures/open_mqtt_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hichard/mymqtt/1b3b9367c659fdd8f132b18bcb32e5794a4fa7be/docs/figures/open_mqtt_example.png -------------------------------------------------------------------------------- /docs/figures/paho-mqtt-menuconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hichard/mymqtt/1b3b9367c659fdd8f132b18bcb32e5794a4fa7be/docs/figures/paho-mqtt-menuconfig.png -------------------------------------------------------------------------------- /docs/figures/select_mqtt_package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hichard/mymqtt/1b3b9367c659fdd8f132b18bcb32e5794a4fa7be/docs/figures/select_mqtt_package.png -------------------------------------------------------------------------------- /docs/introduction.md: -------------------------------------------------------------------------------- 1 | # MQTT 软件包介绍 2 | 3 | [Paho MQTT](http://www.eclipse.org/paho/downloads.php) 是 Eclipse 实现的 MQTT 协议的客户端,本软件包是在 Eclipse [paho-mqtt](https://github.com/eclipse/paho.mqtt.embedded-c) 源码包的基础上设计的一套 MQTT 客户端程序。 4 | 5 | ## 文件目录结构 6 | 7 | ```c 8 | pahomqtt 9 | ├───docs 10 | │ └───figures // 文档使用图片 11 | │ │ api.md // API 使用说明 12 | │ │ introduction.md // 介绍文档 13 | │ │ principle.md // 实现原理 14 | │ │ README.md // 文档结构说明 15 | │ │ samples.md // 软件包示例 16 | │ │ user-guide.md // 使用说明 17 | │ └───version.md // 版本 18 | ├───MQTTClient-RT // 移植文件 19 | ├───MQTTPacket // 源文件 20 | ├───samples // 示例代码 21 | │ mqtt_sample.c // 软件包应用示例代码 22 | ├───tests // mqtt 功能测试程序 23 | │ LICENSE // 软件包许可证 24 | │ README.md // 软件包使用说明 25 | └───SConscript // RT-Thread 默认的构建脚本 26 | ``` 27 | 28 | ## RT-Thread 软件包功能特点 29 | 30 | RT-Thread MQTT 客户端功能特点如下: 31 | 32 | - 断线自动重连 33 | 34 | RT-Thread MQTT 软件包实现了断线重连机制,在断网或网络不稳定导致连接断开时,会维护登陆状态,重新连接,并自动重新订阅 Topic。提高连接的可靠性,增加了软件包的易用性。 35 | 36 | - pipe 模型,非阻塞 API 37 | 38 | 降低编程难度,提高代码运行效率,适用于高并发数据量小的情况。 39 | 40 | - 事件回调机制 41 | 42 | 在建立连接、收到消息或者断开连接等事件时,可以执行自定义的回调函数。 43 | 44 | - TLS 加密传输 45 | 46 | MQTT 可以采用 TLS 加密方式传输,保证数据的安全性和完整性 。 47 | 48 | ## MQTT 简述 49 | 50 | MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于 TCP/IP 协议上,由 IBM 在1999年发布。MQTT 最大优点在于,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。 51 | 52 | MQTT 是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT 协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。 53 | 54 | ## MQTT 功能介绍 55 | 56 | MQTT 协议运行在 TCP/IP 或其他网络协议之上,它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的、基于字节流的双向传输。当应用数据通过 MQTT 网络发送时, MQTT 会把与之相关的服务质量(QoS)和主题名(Topic)相关连,其特点包括: 57 | 58 | 1. 使用发布/订阅消息模式,它提供了一对多消息分发,以实现与应用程序的解耦。 59 | 2. 对负载内容屏蔽的消息传输机制。 60 | 3. 对传输消息有三种服务质量(QoS): 61 | 1. 最多一次,这一级别会发生消息丢失或重复,消息发布依赖于底层 TCP/IP 网络。即:<=1 62 | 2. 至多一次,这一级别会确保消息到达,但消息可能会重复。即:>=1 63 | 3. 只有一次,确保消息只有一次到达。即:=1。在一些要求比较严格的计费系统中,可以使用此级别。 64 | 4. 数据传输和协议交换的最小化(协议头部只有2字节),以减少网络流量。 65 | 5. 通知机制,异常中断时通知传输双方。 66 | 67 | ### MQTT 客户端 68 | 69 | 一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以: 70 | 71 | - 与服务器建立连接 72 | - 发布其他客户端可能会订阅的信息 73 | - 接收其它客户端发布的消息 74 | - 退订已订阅的消息 75 | 76 | ### MQTT 服务器 77 | 78 | MQTT服务器以称为“消息代理”(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以: 79 | 80 | - 接受来自客户的网络连接 81 | - 接收客户发布的应用信息 82 | - 处理来自客户端的订阅和退订请求 83 | - 向订阅的客户转发应用程序消息 84 | 85 | ### MQTT 协议中的方法 86 | 87 | MQTT协议中定义了一些方法(也被称为动作), 用来表示对确定资源所进行操作。 这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。 88 | 89 | - Connect:等待与服务器建立连接。 90 | - Disconnect:等待 MQTT 客户端完成所做的工作,并与服务器断开 TCP/IP 会话。 91 | - Subscribe:等待完成订阅。 92 | - UnSubscribe:等待服务器取消客户端的一个或多个 Topics 订阅。 93 | - Publish:MQTT 客户端发送消息请求,发送完成后返回应用程序线程。 94 | 95 | ### MQTT 协议中的订阅、主题、会话 96 | 97 | - 订阅(Subscription) 98 | 99 | 订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。 100 | 101 | - 会话(Session) 102 | 103 | 每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。 104 | 105 | - 主题名(Topic Name) 106 | 107 | 连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。 108 | 109 | - 主题筛选器(Topic Filter) 110 | 111 | 一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。 112 | 113 | - 负载(Payload) 114 | 115 | 消息订阅者所具体接收的内容。 116 | 117 | - 应用消息 Application Message 118 | 119 | MQTT协议通过网络传输应用数据。应用消息通过MQTT传输时,它们有关联的服务质量(QoS)和主题(Topic)。 120 | 121 | - 控制报文 MQTT Control Packet 122 | 123 | 通过网络连接发送的信息数据包。MQTT规范定义了十四种不同类型的控制报文,其中一个(PUBLISH报文)用于传输应用消息。 124 | -------------------------------------------------------------------------------- /docs/principle.md: -------------------------------------------------------------------------------- 1 | # MQTT 工作原理 2 | 3 | ## MQTT 协议工作原理 4 | 在 MQTT 协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)和订阅者(Subscribe)。其中消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者,这三者的关系如下图所示: 5 | 6 | ![MQTT 工作原理示意图](figures/mqtt_principle.png) 7 | 8 | 在 MQTT 协议的实际使用过程中,一般遵循以下流程: 9 | 10 | - 发布者通过代理服务器向指定的 Topic 发布消息。 11 | - 订阅者通过代理服务器订阅所需要的 Topic。 12 | - 订阅成功后如果有发布者向订阅者订阅的 Topic 发布消息,那么订阅者就会收到代理服务器的推送消息,通过这种方式可以进行高效的数据交换。 13 | 14 | -------------------------------------------------------------------------------- /docs/samples.md: -------------------------------------------------------------------------------- 1 | # MQTT 示例程序 2 | 3 | ## 示例代码讲解 4 | 5 | 下面讲解 RT-Thread 提供的 MQTT 示例代码,测试服务器使用 Eclipse 的测试服务器,地址 `iot.eclipse.org` ,端口 `1883`,MQTT 功能示例代码如下: 6 | 7 | ```c 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #define DBG_ENABLE 15 | #define DBG_SECTION_NAME "mqtt.sample" 16 | #define DBG_LEVEL DBG_LOG 17 | #define DBG_COLOR 18 | #include 19 | 20 | #include "paho_mqtt.h" 21 | 22 | /** 23 | * MQTT URI farmat: 24 | * domain mode 25 | * tcp://iot.eclipse.org:1883 26 | * 27 | * ipv4 mode 28 | * tcp://192.168.10.1:1883 29 | * ssl://192.168.10.1:1884 30 | * 31 | * ipv6 mode 32 | * tcp://[fe80::20c:29ff:fe9a:a07e]:1883 33 | * ssl://[fe80::20c:29ff:fe9a:a07e]:1884 34 | */ 35 | #define MQTT_URI "tcp://iot.eclipse.org:1883" // 配置测试服务器地址 36 | #define MQTT_USERNAME "admin" 37 | #define MQTT_PASSWORD "admin" 38 | #define MQTT_SUBTOPIC "/mqtt/test" // 设置订阅主题 39 | #define MQTT_PUBTOPIC "/mqtt/test" // 设置推送主题 40 | #define MQTT_WILLMSG "Goodbye!" // 设置遗言消息 41 | 42 | /* 定义 MQTT 客户端环境结构体 */ 43 | static MQTTClient client; 44 | static int is_started = 0; 45 | 46 | /* MQTT 订阅事件自定义回调函数 */ 47 | static void mqtt_sub_callback(MQTTClient *c, MessageData *msg_data) 48 | { 49 | *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0'; 50 | LOG_D("mqtt sub callback: %.*s %.*s", 51 | msg_data->topicName->lenstring.len, 52 | msg_data->topicName->lenstring.data, 53 | msg_data->message->payloadlen, 54 | (char *)msg_data->message->payload); 55 | } 56 | 57 | /* MQTT 订阅事件默认回调函数 */ 58 | static void mqtt_sub_default_callback(MQTTClient *c, MessageData *msg_data) 59 | { 60 | *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0'; 61 | LOG_D("mqtt sub default callback: %.*s %.*s", 62 | msg_data->topicName->lenstring.len, 63 | msg_data->topicName->lenstring.data, 64 | msg_data->message->payloadlen, 65 | (char *)msg_data->message->payload); 66 | } 67 | 68 | /* MQTT 连接事件回调函数 */ 69 | static void mqtt_connect_callback(MQTTClient *c) 70 | { 71 | LOG_D("inter mqtt_connect_callback!"); 72 | } 73 | 74 | /* MQTT 上线事件回调函数 */ 75 | static void mqtt_online_callback(MQTTClient *c) 76 | { 77 | LOG_D("inter mqtt_online_callback!"); 78 | } 79 | 80 | /* MQTT 下线事件回调函数 */ 81 | static void mqtt_offline_callback(MQTTClient *c) 82 | { 83 | LOG_D("inter mqtt_offline_callback!"); 84 | } 85 | 86 | static int mqtt_start(int argc, char **argv) 87 | { 88 | /* 使用 MQTTPacket_connectData_initializer 初始化 condata 参数 */ 89 | MQTTPacket_connectData condata = MQTTPacket_connectData_initializer; 90 | static char cid[20] = { 0 }; 91 | 92 | if (argc != 1) 93 | { 94 | rt_kprintf("mqtt_start --start a mqtt worker thread.\n"); 95 | return -1; 96 | } 97 | 98 | if (is_started) 99 | { 100 | LOG_E("mqtt client is already connected."); 101 | return -1; 102 | } 103 | /* 配置 MQTT 结构体内容参数 */ 104 | { 105 | client.isconnected = 0; 106 | client.uri = MQTT_URI; 107 | 108 | /* 产生随机的客户端 ID */ 109 | rt_snprintf(cid, sizeof(cid), "rtthread%d", rt_tick_get()); 110 | /* 配置连接参数 */ 111 | memcpy(&client.condata, &condata, sizeof(condata)); 112 | client.condata.clientID.cstring = cid; 113 | client.condata.keepAliveInterval = 30; 114 | client.condata.cleansession = 1; 115 | client.condata.username.cstring = MQTT_USERNAME; 116 | client.condata.password.cstring = MQTT_PASSWORD; 117 | 118 | /* 配置 MQTT 遗言参数 */ 119 | client.condata.willFlag = 1; 120 | client.condata.will.qos = 1; 121 | client.condata.will.retained = 0; 122 | client.condata.will.topicName.cstring = MQTT_PUBTOPIC; 123 | client.condata.will.message.cstring = MQTT_WILLMSG; 124 | 125 | /* 分配缓冲区 */ 126 | client.buf_size = client.readbuf_size = 1024; 127 | client.buf = rt_calloc(1, client.buf_size); 128 | client.readbuf = rt_calloc(1, client.readbuf_size); 129 | if (!(client.buf && client.readbuf)) 130 | { 131 | LOG_E("no memory for MQTT client buffer!"); 132 | return -1; 133 | } 134 | 135 | /* 设置事件回调函数 */ 136 | client.connect_callback = mqtt_connect_callback; 137 | client.online_callback = mqtt_online_callback; 138 | client.offline_callback = mqtt_offline_callback; 139 | 140 | /* 设置订阅表和事件回调函数*/ 141 | client.messageHandlers[0].topicFilter = rt_strdup(MQTT_SUBTOPIC); 142 | client.messageHandlers[0].callback = mqtt_sub_callback; 143 | client.messageHandlers[0].qos = QOS1; 144 | 145 | /* 设置默认的订阅主题*/ 146 | client.defaultMessageHandler = mqtt_sub_default_callback; 147 | } 148 | 149 | /* 运行 MQTT 客户端 */ 150 | paho_mqtt_start(&client); 151 | is_started = 1; 152 | 153 | return 0; 154 | } 155 | 156 | /* 该函数用于停止 MQTT 客户端并释放内存空间 */ 157 | static int mqtt_stop(int argc, char **argv) 158 | { 159 | if (argc != 1) 160 | { 161 | rt_kprintf("mqtt_stop --stop mqtt worker thread and free mqtt client object.\n"); 162 | } 163 | 164 | is_started = 0; 165 | 166 | return paho_mqtt_stop(&client); 167 | } 168 | 169 | /* 该函数用于发送数据到指定 topic */ 170 | static int mqtt_publish(int argc, char **argv) 171 | { 172 | if (is_started == 0) 173 | { 174 | LOG_E("mqtt client is not connected."); 175 | return -1; 176 | } 177 | 178 | if (argc == 2) 179 | { 180 | paho_mqtt_publish(&client, QOS1, MQTT_PUBTOPIC, argv[1]); 181 | } 182 | else if (argc == 3) 183 | { 184 | paho_mqtt_publish(&client, QOS1, argv[1], argv[2]); 185 | } 186 | else 187 | { 188 | rt_kprintf("mqtt_publish [message] --mqtt publish message to specified topic.\n"); 189 | return -1; 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | /* MQTT 新的订阅事件自定义回调函数 */ 196 | static void mqtt_new_sub_callback(MQTTClient *client, MessageData *msg_data) 197 | { 198 | *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0'; 199 | LOG_D("mqtt new subscribe callback: %.*s %.*s", 200 | msg_data->topicName->lenstring.len, 201 | msg_data->topicName->lenstring.data, 202 | msg_data->message->payloadlen, 203 | (char *)msg_data->message->payload); 204 | } 205 | 206 | /* 该函数用于订阅新的 Topic */ 207 | static int mqtt_subscribe(int argc, char **argv) 208 | { 209 | if (argc != 2) 210 | { 211 | rt_kprintf("mqtt_subscribe [topic] --send an mqtt subscribe packet and wait for suback before returning.\n"); 212 | return -1; 213 | } 214 | 215 | if (is_started == 0) 216 | { 217 | LOG_E("mqtt client is not connected."); 218 | return -1; 219 | } 220 | 221 | return paho_mqtt_subscribe(&client, QOS1, argv[1], mqtt_new_sub_callback); 222 | } 223 | 224 | /* 该函数用于取消订阅指定的 Topic */ 225 | static int mqtt_unsubscribe(int argc, char **argv) 226 | { 227 | if (argc != 2) 228 | { 229 | rt_kprintf("mqtt_unsubscribe [topic] --send an mqtt unsubscribe packet and wait for suback before returning.\n"); 230 | return -1; 231 | } 232 | 233 | if (is_started == 0) 234 | { 235 | LOG_E("mqtt client is not connected."); 236 | return -1; 237 | } 238 | 239 | return paho_mqtt_unsubscribe(&client, argv[1]); 240 | } 241 | 242 | #ifdef FINSH_USING_MSH 243 | MSH_CMD_EXPORT(mqtt_start, startup mqtt client); 244 | MSH_CMD_EXPORT(mqtt_stop, stop mqtt client); 245 | MSH_CMD_EXPORT(mqtt_publish, mqtt publish message to specified topic); 246 | MSH_CMD_EXPORT(mqtt_subscribe, mqtt subscribe topic); 247 | MSH_CMD_EXPORT(mqtt_unsubscribe, mqtt unsubscribe topic); 248 | #endif /* FINSH_USING_MSH */ 249 | ``` 250 | 251 | ## 运行示例 252 | 253 | 在 msh 中运行上述功能示例代码,可以实现向服务器订阅主题和向特定主题推送消息的功能,功能示例代码运行效果如下: 254 | 255 | - 启动 MQTT 客户端,连接代理服务器并订阅主题: 256 | 257 | ```c 258 | msh />mqtt_start /* 启动 MQTT 客户端连接 Eclipse 服务器 */ 259 | inter mqtt_connect_callback! /* 服务器连接成功,调用连接回调函数打印服务器信息 */ 260 | ipv4 address port: 1883 261 | [MQTT] HOST = 'iot.eclipse.org' 262 | msh />[MQTT] Subscribe #0 /mqtt/test OK! /* 订阅主题 /mqtt/test 成功 */ 263 | inter mqtt_online_callback! /* MQTT 上线成功,调用上线回调函数 */ 264 | msh /> 265 | ``` 266 | - 作为发布者向指定主题发布消息: 267 | 268 | ```c 269 | msh />mqtt_pub /mqtt/test hello-rtthread /* 向指定主题发送 hello-rtthread 消息 */ 270 | msh />mqtt sub callback: /mqtt/test hello-rtthread /* 收到消息,执行回调函数 */ 271 | msh /> 272 | ``` 273 | 274 | - 订阅新的 topic 275 | 276 | ```c 277 | msh />mqtt_subscribe /test/topic /* 订阅指定的 topic */ 278 | [I/MQTT:] Subscribe #1 /test/topic OK! 279 | msh /> 280 | ``` 281 | 282 | - 取消订阅 topic 283 | 284 | ```c 285 | msh />mqtt_unsubscribe /test/topic /* 取消订阅指定的 topic */ 286 | [561315] I/MQTT: Unsubscribe #1 /test/topic OK! 287 | msh /> 288 | ``` 289 | 290 | - 关闭 MQTT 客户端 291 | 292 | ```c 293 | msh />mqtt_stop 294 | [585107] D/MQTT: pub_sock recv 11 byte: DISCONNECT 295 | [585110] I/MQTT: MQTT server is disconnected. 296 | msh /> 297 | ``` 298 | -------------------------------------------------------------------------------- /docs/user-guide.md: -------------------------------------------------------------------------------- 1 | # MQTT 使用说明 2 | 3 | ## 准备工作 4 | 5 | 首先需要下载 MQTT 软件包,并将软件包加入到项目中。在 BSP 目录下使用 menuconfig 命令打开 Env 配置界面,在 `RT-Thread online packages → IoT - internet of things` 中选择 Paho MQTT 软件包,操作界面如下图所示: 6 | 7 | ![选中 Paho MQTT 软件包](figures/select_mqtt_package.png) 8 | 9 | 开启功能示例,便于测试 MQTT 功能: 10 | 11 | ![开启 MQTT 软件包测试例程](figures/open_mqtt_example.png) 12 | 13 | 配置项介绍如下: 14 | 15 | ```c 16 | --- Paho MQTT: Eclipse Paho MQTT C/C++ client for Embedded platforms 17 | MQTT mode (Pipe mode: high performance and depends on DFS) --->#高级功能 18 | [*] Enable MQTT example #开启 MQTT 功能示例 19 | [ ] Enable MQTT test #开启 MQTT 测试例程 20 | [ ] Enable support tls protocol #开启 TLS 安全传输选项 21 | (1) Max pahomqtt subscribe topic handlers #设置 Topic 最大订阅数量 22 | [*] Enable debug log output #开启调试Log输出 23 | version (latest) ---> #选择软件包版本,默认为最新版 24 | ``` 25 | 选择合适的配置项后,使用 `pkgs --update` 命令下载软件包并添加到工程中即可。 26 | 27 | ## 使用流程 28 | 29 | 这一节介绍 MQTT 软件包的配置参数和使用方法。 30 | 31 | ### 设置代理信息 32 | 33 | 首先要设置好代理服务器的地址,用户名、密码等必要信息。以 MQTT sample 为例有如下的设置: 34 | 35 | ```c 36 | #define MQTT_URI "tcp://iot.eclipse.org:1883" //设置服务器地址 37 | #define MQTT_USERNAME "admin" //代理服务器用户名 38 | #define MQTT_PASSWORD "admin" //代理服务器密码 39 | #define MQTT_SUBTOPIC "/mqtt/test" //订阅的 Topic 40 | #define MQTT_PUBTOPIC "/mqtt/test" //推送的 Topic 41 | #define MQTT_WILLMSG "Goodbye!" //设置断开通知消息 42 | ``` 43 | 44 | ### 配置 MQTT 客户端结构体 45 | 46 | 接下来需要初始化 MQTT 软件包客户端实例,将上一步设定的数据写入客户端实例的配置项,对客户端进行必要的配置,在这一步需要进行如下操作: 47 | 48 | - 设置服务器地址,以及服务器账号密码等信息,示例代码如下: 49 | 50 | ``` c 51 | /* 配置连接参数 */ 52 | memcpy(&client.condata, &condata, sizeof(condata)); 53 | client.condata.clientID.cstring = cid; 54 | client.condata.keepAliveInterval = 60; 55 | client.condata.cleansession = 1; 56 | client.condata.username.cstring = MQTT_USERNAME; //设置账号 57 | client.condata.password.cstring = MQTT_PASSWORD; //设置密码 58 | ``` 59 | 60 | - 设置消息等级、推送 Topic、以及断开通知消息等配置,示例如下: 61 | 62 | ``` c 63 | /* 配置断开通知消息 */ 64 | client.condata.willFlag = 1; 65 | client.condata.will.qos = 1; 66 | client.condata.will.retained = 0; 67 | client.condata.will.topicName.cstring = rt_strdup(MQTT_PUBTOPIC); //设置推送主题,需要分配空间存储 topic,以便后面订阅多个 topic 68 | client.condata.will.message.cstring = MQTT_WILLMSG; //设置断开通知消息 69 | ``` 70 | 71 | - 设置事件回调函数,这里需要为事件设置回掉函数,如连接成功事件、上线成功事件、下线事件等,示例代码如下: 72 | 73 | ``` c 74 | /* 设置事件回调函数,回调函数需要自己编写,在例程中为回调函数留了空函数 */ 75 | client.connect_callback = mqtt_connect_callback; //设置连接回调函数 76 | client.online_callback = mqtt_online_callback; //设置上线回调函数 77 | client.offline_callback = mqtt_offline_callback; //设置下线回调函数 78 | ``` 79 | 80 | - 设置客户端订阅表,MQTT 客户端可以同时订阅多个 Topic, 所以需要维护一个订阅表,在这一步需要为每一个 Topic 的订阅设置参数,主要包括 Topic 名称、该订阅的回调函数以及消息等级,代码示例如下: 81 | 82 | ``` c 83 | /* 配置订阅表 */ 84 | client.messageHandlers[0].topicFilter = MQTT_SUBTOPIC; //设置第一个订阅的 Topic 85 | client.messageHandlers[0].callback = mqtt_sub_callback;//设置该订阅的回调函数 86 | client.messageHandlers[0].qos = QOS1; //设置该订阅的消息等级 87 | /* set default subscribe event callback */ 88 | client.defaultMessageHandler = mqtt_sub_default_callback; //设置一个默认的回调函数,如果有订阅的 Topic 没有设置回调函数,则使用该默认回调函数 89 | ``` 90 | 91 | ### 启动 MQTT 客户端 92 | 93 | 配置完成 MQTT 客户端实例后,需要启动客户端,代码示例如下: 94 | 95 | ``` c 96 | /* 运行 MQTT 客户端 */ 97 | paho_mqtt_start(&client); 98 | ``` 99 | 100 | 启动 MQTT 客户端之后,客户端会自动连接代理服务器,自动订阅已经设置的 Topic,根据事件执行回调函数进行数据的处理。 101 | 102 | ### 向指定 Topic 推送消息 103 | 104 | 连接服务器成功之后,便可以通过代理服务器向指定的 Topic 推送消息。推送消息时需要设置消息内容、Topic、消息等级等配置,使用接口如下: 105 | 106 | ``` c 107 | // 向指定 Topic 发送消息信息 108 | int paho_mqtt_publish(MQTTClient *client, enum QoS qos, const char *topic, const char *msg_str); 109 | ``` 110 | 111 | ## 运行效果 112 | 113 | 演示示例可以展示连接服务器、订阅 Topic、向指定 Topic 推送消息的功能,如下所示: 114 | 115 | ``` c 116 | msh />mqtt_start /* 启动 MQTT 客户端连接代理服务器 */ 117 | inter mqtt_connect_callback! /* 连接成功,运行上线回调函数 */ 118 | ipv4 address port: 1883 119 | [MQTT] HOST = 'iot.eclipse.org' 120 | msh />[MQTT] Subscribe 121 | inter mqtt_online_callback! /* 上线成功,运行在线回调函数 */ 122 | msh />mqtt_publish hello-rtthread /* 向指定 Topic 推送消息 */ 123 | msh />mqtt sub callback: /mqtt/test hello-rtthread /* 收到消息,执行回调函数 */ 124 | ``` 125 | 126 | !!! note "注意事项" 127 | 需要注意正确填写 `MQTT_USERNAME` 和 `MQTT_PASSWORD` ,如果 `MQTT_USERNAME` 和 `MQTT_PASSWORD` 填写错误,MQTT 客户端无法正确连接到 MQTT 服务器。 128 | 129 | ## 参考资料 130 | 131 | - [MQTT 官网](http://mqtt.org/) 132 | - [Paho 官网](http://www.eclipse.org/paho/downloads.php) 133 | - [IBM MQTT 介绍](https://www.ibm.com/developerworks/cn/iot/iot-mqtt-why-good-for-iot/index.html) 134 | - [Eclipse paho.mqtt 源码](https://github.com/eclipse/paho.mqtt.embedded-c) 135 | -------------------------------------------------------------------------------- /docs/version.md: -------------------------------------------------------------------------------- 1 | # 版本和修订 # 2 | 3 | | **Date** | **Version** | **Author** | **Note** | 4 | | -------- | :-----: | :---- | :---- | 5 | | 2018-04-28 | v0.1 | Armink | 初始版本 | 6 | | 2018-05-28 | v1.0 | Armink | 同步更新 | 7 | | 2018-06-29 | v1.1 | SummerGift | 添加UM文档 | 8 | 9 | -------------------------------------------------------------------------------- /samples/mqtt_sample.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define DBG_ENABLE 8 | #define DBG_SECTION_NAME "mqtt.sample" 9 | #define DBG_LEVEL DBG_LOG 10 | #define DBG_COLOR 11 | #include 12 | 13 | #include "mqtt_client.h" 14 | 15 | /** 16 | * MQTT URI farmat: 17 | * domain mode 18 | * tcp://iot.eclipse.org:1883 19 | * 20 | * ipv4 mode 21 | * tcp://192.168.10.1:1883 22 | * ssl://192.168.10.1:1884 23 | * 24 | * ipv6 mode 25 | * tcp://[fe80::20c:29ff:fe9a:a07e]:1883 26 | * ssl://[fe80::20c:29ff:fe9a:a07e]:1884 27 | */ 28 | #define MQTT_URI "tcp://test.mosquitto.org:1883" //"tcp://mq.tongxinmao.com:18831" 29 | #define MQTT_SUBTOPIC "/mqtt/test" 30 | #define MQTT_PUBTOPIC "/mqtt/test" 31 | #define MQTT_WILLMSG "Goodbye!" 32 | 33 | /* define MQTT client context */ 34 | static mqtt_client client; 35 | static int is_started = 0; 36 | 37 | static void mqtt_sub_callback(mqtt_client *c, message_data *msg_data) 38 | { 39 | *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0'; 40 | LOG_D("mqtt sub callback: %.*s %.*s", 41 | msg_data->topic_name->lenstring.len, 42 | msg_data->topic_name->lenstring.data, 43 | msg_data->message->payloadlen, 44 | (char *)msg_data->message->payload); 45 | } 46 | 47 | static void mqtt_sub_default_callback(mqtt_client *c, message_data *msg_data) 48 | { 49 | *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0'; 50 | LOG_D("mqtt sub default callback: %.*s %.*s", 51 | msg_data->topic_name->lenstring.len, 52 | msg_data->topic_name->lenstring.data, 53 | msg_data->message->payloadlen, 54 | (char *)msg_data->message->payload); 55 | } 56 | 57 | static void mqtt_connect_callback(mqtt_client *c) 58 | { 59 | LOG_D("inter mqtt_connect_callback!"); 60 | } 61 | 62 | static void mqtt_online_callback(mqtt_client *c) 63 | { 64 | LOG_D("inter mqtt_online_callback!"); 65 | } 66 | 67 | static void mqtt_offline_callback(mqtt_client *c) 68 | { 69 | LOG_D("inter mqtt_offline_callback!"); 70 | } 71 | 72 | static int mqtt_start(int argc, char **argv) 73 | { 74 | /* init condata param by using MQTTPacket_connectData_initializer */ 75 | MQTTPacket_connectData condata = MQTTPacket_connectData_initializer; 76 | static char cid[20] = { 0 }; 77 | 78 | 79 | if (argc != 1) 80 | { 81 | rt_kprintf("mqtt_start --start a mqtt worker thread.\n"); 82 | return -1; 83 | } 84 | 85 | if (is_started) 86 | { 87 | LOG_E("mqtt client is already connected."); 88 | return -1; 89 | } 90 | /* config MQTT context param */ 91 | { 92 | client.isconnected = 0; 93 | client.uri = MQTT_URI; 94 | 95 | /* generate the random client ID */ 96 | rt_snprintf(cid, sizeof(cid), "rtthread%d", rt_tick_get()); 97 | /* config connect param */ 98 | memcpy(&client.condata, &condata, sizeof(condata)); 99 | client.condata.clientID.cstring = cid; 100 | // client.condata.username.cstring = "huaning"; 101 | // client.condata.password.cstring = "sjhn20140916"; 102 | client.condata.keepAliveInterval = 30; 103 | client.condata.cleansession = 1; 104 | 105 | /* config MQTT will param. */ 106 | client.condata.willFlag = 1; 107 | client.condata.will.qos = 1; 108 | client.condata.will.retained = 0; 109 | client.condata.will.topicName.cstring = MQTT_PUBTOPIC; 110 | client.condata.will.message.cstring = MQTT_WILLMSG; 111 | 112 | /* malloc buffer. */ 113 | client.buf_size = client.readbuf_size = 1024; 114 | client.buf = rt_calloc(1, client.buf_size); 115 | client.readbuf = rt_calloc(1, client.readbuf_size); 116 | if (!(client.buf && client.readbuf)) 117 | { 118 | LOG_E("no memory for MQTT client buffer!"); 119 | return -1; 120 | } 121 | 122 | /* set event callback function */ 123 | client.connect_callback = mqtt_connect_callback; 124 | client.online_callback = mqtt_online_callback; 125 | client.offline_callback = mqtt_offline_callback; 126 | 127 | /* set subscribe table and event callback */ 128 | client.message_handlers[0].topicFilter = rt_strdup(MQTT_SUBTOPIC); 129 | client.message_handlers[0].callback = mqtt_sub_callback; 130 | client.message_handlers[0].qos = QOS1; 131 | 132 | /* set default subscribe event callback */ 133 | client.default_message_handlers = mqtt_sub_default_callback; 134 | } 135 | 136 | { 137 | int value; 138 | uint16_t u16Value; 139 | value = 5; 140 | paho_mqtt_control(&client, MQTT_CTRL_SET_CONN_TIMEO, &value); 141 | value = 5; 142 | paho_mqtt_control(&client, MQTT_CTRL_SET_MSG_TIMEO, &value); 143 | value = 5; 144 | paho_mqtt_control(&client, MQTT_CTRL_SET_RECONN_INTERVAL, &value); 145 | value = 30; 146 | paho_mqtt_control(&client, MQTT_CTRL_SET_KEEPALIVE_INTERVAL, &value); 147 | u16Value = 3; 148 | paho_mqtt_control(&client, MQTT_CTRL_SET_KEEPALIVE_COUNT, &u16Value); 149 | } 150 | 151 | /* run mqtt client */ 152 | paho_mqtt_start(&client, 8196, 20); 153 | is_started = 1; 154 | 155 | return 0; 156 | } 157 | 158 | static int mqtt_stop(int argc, char **argv) 159 | { 160 | if (argc != 1) 161 | { 162 | rt_kprintf("mqtt_stop --stop mqtt worker thread and free mqtt client object.\n"); 163 | } 164 | 165 | is_started = 0; 166 | 167 | return paho_mqtt_stop(&client); 168 | } 169 | 170 | static int mqtt_publish(int argc, char **argv) 171 | { 172 | if (is_started == 0) 173 | { 174 | LOG_E("mqtt client is not connected."); 175 | return -1; 176 | } 177 | 178 | if (argc == 2) 179 | { 180 | paho_mqtt_publish(&client, QOS1, MQTT_PUBTOPIC, argv[1], strlen(argv[1])); 181 | } 182 | else if (argc == 3) 183 | { 184 | paho_mqtt_publish(&client, QOS1, argv[1], argv[2],strlen(argv[2])); 185 | } 186 | else 187 | { 188 | rt_kprintf("mqtt_publish [message] --mqtt publish message to specified topic.\n"); 189 | return -1; 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | static void mqtt_new_sub_callback(mqtt_client *client, message_data *msg_data) 196 | { 197 | *((char *)msg_data->message->payload + msg_data->message->payloadlen) = '\0'; 198 | LOG_D("mqtt new subscribe callback: %.*s %.*s", 199 | msg_data->topic_name->lenstring.len, 200 | msg_data->topic_name->lenstring.data, 201 | msg_data->message->payloadlen, 202 | (char *)msg_data->message->payload); 203 | } 204 | 205 | static int mqtt_subscribe(int argc, char **argv) 206 | { 207 | if (argc != 2) 208 | { 209 | rt_kprintf("mqtt_subscribe [topic] --send an mqtt subscribe packet and wait for suback before returning.\n"); 210 | return -1; 211 | } 212 | 213 | if (is_started == 0) 214 | { 215 | LOG_E("mqtt client is not connected."); 216 | return -1; 217 | } 218 | 219 | return paho_mqtt_subscribe(&client, QOS1, argv[1], mqtt_new_sub_callback); 220 | } 221 | 222 | static int mqtt_unsubscribe(int argc, char **argv) 223 | { 224 | if (argc != 2) 225 | { 226 | rt_kprintf("mqtt_unsubscribe [topic] --send an mqtt unsubscribe packet and wait for suback before returning.\n"); 227 | return -1; 228 | } 229 | 230 | if (is_started == 0) 231 | { 232 | LOG_E("mqtt client is not connected."); 233 | return -1; 234 | } 235 | 236 | return paho_mqtt_unsubscribe(&client, argv[1]); 237 | } 238 | 239 | #ifdef FINSH_USING_MSH 240 | MSH_CMD_EXPORT(mqtt_start, startup mqtt client); 241 | MSH_CMD_EXPORT(mqtt_stop, stop mqtt client); 242 | MSH_CMD_EXPORT(mqtt_publish, mqtt publish message to specified topic); 243 | MSH_CMD_EXPORT(mqtt_subscribe, mqtt subscribe topic); 244 | MSH_CMD_EXPORT(mqtt_unsubscribe, mqtt unsubscribe topic); 245 | #endif /* FINSH_USING_MSH */ 246 | 247 | -------------------------------------------------------------------------------- /tests/mqtt_test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * File : mqtt_test.c 3 | * COPYRIGHT (C) 2012-2018, Shanghai Real-Thread Technology Co., Ltd 4 | * 5 | * Change Logs: 6 | * Date Author Notes 7 | * 2018-03-12 armink the first version 8 | */ 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include "mqtt_client.h" 15 | 16 | #ifdef PKG_USING_MYMQTT_TEST 17 | 18 | /** 19 | * MQTT URI farmat: 20 | * domain mode 21 | * tcp://iot.eclipse.org:1883 22 | * 23 | * ipv4 mode 24 | * tcp://192.168.10.1:1883 25 | * ssl://192.168.10.1:1884 26 | * 27 | * ipv6 mode 28 | * tcp://[fe80::20c:29ff:fe9a:a07e]:1883 29 | * ssl://[fe80::20c:29ff:fe9a:a07e]:1884 30 | */ 31 | #define MQTT_TEST_SERVER_URI "tcp://iot.eclipse.org:1883" 32 | #define MQTT_CLIENTID "rtthread-mqtt" 33 | #define MQTT_USERNAME "admin" 34 | #define MQTT_PASSWORD "admin" 35 | #define MQTT_SUBTOPIC "/mqtt/test" 36 | #define MQTT_PUBTOPIC "/mqtt/test" 37 | #define MQTT_WILLMSG "Goodbye!" 38 | #define MQTT_TEST_QOS 1 39 | #define MQTT_PUB_SUB_BUF_SIZE 1024 40 | 41 | #define CMD_INFO "'mqtt_test '" 42 | #define TEST_DATA_SIZE 256 43 | #define PUB_CYCLE_TM 1000 44 | 45 | static rt_thread_t pub_thread_tid = RT_NULL; 46 | 47 | static char *pub_data = RT_NULL; 48 | 49 | /* define MQTT client context */ 50 | static MQTTClient client; 51 | 52 | static rt_uint32_t pub_count = 0, sub_count = 0; 53 | static int recon_count = -1; 54 | static int test_start_tm = 0; 55 | static int test_is_started = 0; 56 | 57 | static void mqtt_sub_callback(MQTTClient *c, MessageData *msg_data) 58 | { 59 | sub_count ++; 60 | return; 61 | } 62 | 63 | static void mqtt_connect_callback(MQTTClient *c) 64 | { 65 | return; 66 | } 67 | 68 | static void mqtt_online_callback(MQTTClient *c) 69 | { 70 | recon_count ++; 71 | return; 72 | } 73 | 74 | static void mqtt_offline_callback(MQTTClient *c) 75 | { 76 | return; 77 | } 78 | 79 | /** 80 | * This function create and config a mqtt client. 81 | * 82 | * @param void 83 | * 84 | * @return none 85 | */ 86 | static void mq_start(void) 87 | { 88 | /* init condata param by using MQTTPacket_connectData_initializer */ 89 | MQTTPacket_connectData condata = MQTTPacket_connectData_initializer; 90 | 91 | rt_memset(&client, 0, sizeof(MQTTClient)); 92 | 93 | /* config MQTT context param */ 94 | { 95 | client.uri = MQTT_TEST_SERVER_URI; 96 | 97 | /* config connect param */ 98 | memcpy(&client.condata, &condata, sizeof(condata)); 99 | client.condata.clientID.cstring = MQTT_CLIENTID; 100 | client.condata.keepAliveInterval = 60; 101 | client.condata.cleansession = 1; 102 | client.condata.username.cstring = MQTT_USERNAME; 103 | client.condata.password.cstring = MQTT_PASSWORD; 104 | 105 | /* config MQTT will param. */ 106 | client.condata.willFlag = 1; 107 | client.condata.will.qos = MQTT_TEST_QOS; 108 | client.condata.will.retained = 0; 109 | client.condata.will.topicName.cstring = MQTT_PUBTOPIC; 110 | client.condata.will.message.cstring = MQTT_WILLMSG; 111 | 112 | /* malloc buffer. */ 113 | client.buf_size = client.readbuf_size = MQTT_PUB_SUB_BUF_SIZE; 114 | client.buf = rt_malloc(client.buf_size); 115 | client.readbuf = rt_malloc(client.readbuf_size); 116 | if (!(client.buf && client.readbuf)) 117 | { 118 | rt_kprintf("no memory for MQTT client buffer!\n"); 119 | goto _exit; 120 | } 121 | 122 | /* set event callback function */ 123 | client.connect_callback = mqtt_connect_callback; 124 | client.online_callback = mqtt_online_callback; 125 | client.offline_callback = mqtt_offline_callback; 126 | 127 | /* set subscribe table and event callback */ 128 | client.messageHandlers[0].topicFilter = rt_strdup(MQTT_SUBTOPIC); 129 | client.messageHandlers[0].callback = mqtt_sub_callback; 130 | client.messageHandlers[0].qos = MQTT_TEST_QOS; 131 | 132 | /* set default subscribe event callback */ 133 | client.defaultMessageHandler = mqtt_sub_callback; 134 | } 135 | 136 | /* run mqtt client */ 137 | paho_mqtt_start(&client); 138 | 139 | return; 140 | 141 | _exit: 142 | if (client.buf) 143 | { 144 | rt_free(client.buf); 145 | client.buf = RT_NULL; 146 | } 147 | if (client.readbuf) 148 | { 149 | rt_free(client.readbuf); 150 | client.readbuf = RT_NULL; 151 | } 152 | return; 153 | } 154 | 155 | static void show_test_info(void) 156 | { 157 | char temp[50] = {0}; 158 | rt_kprintf("\r==== MQTT Stability test ====\n"); 159 | rt_kprintf("Server: "MQTT_TEST_SERVER_URI"\n"); 160 | rt_kprintf("QoS : %d\n", MQTT_TEST_QOS); 161 | 162 | rt_kprintf("Test duration(sec) : %d\n", time((time_t *)RT_NULL) - test_start_tm); 163 | rt_kprintf("Number of published packages : %d\n", pub_count); 164 | rt_kprintf("Number of subscribed packages : %d\n", sub_count); 165 | snprintf(temp, sizeof(temp), "Packet loss rate : %.2f%% \n", (float)((float)(pub_count - sub_count) * 100.0f / pub_count)); 166 | rt_kprintf(temp); 167 | rt_kprintf("Number of reconnections : %d\n", recon_count); 168 | /* up the cursor 8 line */ 169 | rt_kprintf("\033[8A"); 170 | } 171 | 172 | static void thread_pub(void *parameter) 173 | { 174 | pub_data = rt_malloc(TEST_DATA_SIZE * sizeof(char)); 175 | if (!pub_data) 176 | { 177 | rt_kprintf("no memory for pub_data\n"); 178 | return; 179 | } 180 | rt_memset(pub_data, '*', TEST_DATA_SIZE * sizeof(char)); 181 | 182 | test_start_tm = time((time_t *)RT_NULL); 183 | rt_kprintf("test start at '%d'\r\n", test_start_tm); 184 | 185 | while (1) 186 | { 187 | if (!paho_mqtt_publish(&client, QOS1, MQTT_PUBTOPIC, pub_data)) 188 | { 189 | ++ pub_count; 190 | } 191 | 192 | rt_thread_delay(PUB_CYCLE_TM); 193 | 194 | show_test_info(); 195 | } 196 | } 197 | 198 | static void mqtt_test_start(void) 199 | { 200 | if (test_is_started) 201 | { 202 | return; 203 | } 204 | 205 | mq_start(); 206 | 207 | while (!client.isconnected) 208 | { 209 | rt_kprintf("Waiting for mqtt connection...\n"); 210 | rt_thread_delay(1000); 211 | } 212 | 213 | pub_thread_tid = rt_thread_create("pub_thread", thread_pub, RT_NULL, 1024, 8, 100); 214 | if (pub_thread_tid != RT_NULL) 215 | { 216 | rt_thread_startup(pub_thread_tid); 217 | } 218 | 219 | test_is_started = 1; 220 | 221 | return; 222 | } 223 | 224 | static void mqtt_test_stop(void) 225 | { 226 | MQTTClient *local_client = &client; 227 | 228 | if (pub_thread_tid) 229 | { 230 | rt_thread_delete(pub_thread_tid); 231 | } 232 | 233 | if (pub_data) 234 | { 235 | rt_free(pub_data); 236 | pub_data = RT_NULL; 237 | } 238 | 239 | if (local_client) 240 | { 241 | paho_mqtt_stop(local_client); 242 | } 243 | 244 | /* up the cursor 1 line */ 245 | rt_kprintf("\033[1A"); 246 | 247 | show_test_info(); 248 | 249 | /* down the cursor 10 line */ 250 | rt_kprintf("\033[10B"); 251 | 252 | pub_count = sub_count = recon_count = 0; 253 | test_is_started = 0; 254 | 255 | rt_kprintf("==== MQTT Stability test stop ====\n"); 256 | } 257 | 258 | static void mqtt_test(uint8_t argc, char **argv) 259 | { 260 | if (argc >= 2) 261 | { 262 | if (!strcmp(argv[1], "start")) 263 | { 264 | mqtt_test_start(); 265 | } 266 | else if (!strcmp(argv[1], "stop")) 267 | { 268 | mqtt_test_stop(); 269 | } 270 | else 271 | { 272 | rt_kprintf("Please input "CMD_INFO"\n"); 273 | } 274 | } 275 | else 276 | { 277 | rt_kprintf("Please input "CMD_INFO"\n"); 278 | } 279 | } 280 | MSH_CMD_EXPORT(mqtt_test, MQTT test CMD_INFO); 281 | 282 | #endif /* PKG_USING_PAHOMQTT_TEST */ 283 | --------------------------------------------------------------------------------