2023_open_source_contest_preliminary_1st_issue2 from Huang_Chen
it is OK
|  | @ -130,6 +130,7 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) | ||||||
|     endif |     endif | ||||||
| 
 | 
 | ||||||
|     ifeq ($(CONFIG_USER_TEST_MQTTCLIENT),y) |     ifeq ($(CONFIG_USER_TEST_MQTTCLIENT),y) | ||||||
|  |         SRC_DIR:= test_mqttclient | ||||||
|         SRC_FILES +=  |         SRC_FILES +=  | ||||||
|     endif |     endif | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | SRC_FILES += test_mqttclient.c | ||||||
|  | 			 | ||||||
|  | 
 | ||||||
|  | SRC_DIR += mqtt | ||||||
|  | include $(KERNEL_ROOT)/compiler.mk | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | ## 1. 简介 | ||||||
|  | 
 | ||||||
|  | 本代码实现了MQTT对服务器订阅主体并发送信息功能 | ||||||
|  | 
 | ||||||
|  | ## 2. 数据结构设计说明 | ||||||
|  | 
 | ||||||
|  | ### 2.1 MQTT数据结构定义 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ### 2.2 数据解析与加密 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ### 2.3 MQTT连接与订阅 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ## 3. 测试程序说明 | ||||||
|  | 
 | ||||||
|  | MQTT基于TCP/IP协议,分为客户端与服务端。在本任务中,ARM终端作为客户端,与服务端连接并进行主题订阅。 | ||||||
|  | 
 | ||||||
|  | 以下代码设置了进行订阅时需要的Client ID、用户名、密码及订阅主题。 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | 客户端首先需要根据以上信息向服务端发起连接请求,验证用户名及密码 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | 随后进行主题订阅,接收服务端发布的消息 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | 在订阅过程中需要对获取的信息进行读取,同时每隔一段时间向服务端发送保持活性ping请求,以维持与服务端的连接 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | ## 4. 测试流程 | ||||||
|  | 
 | ||||||
|  | 测试流程为: | ||||||
|  | 
 | ||||||
|  | 首先执行setip命令,设置设备ip地址 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | 随后执行“MqttSocketRecvTest 服务器ip”命令,订阅主题,然后在服务器端发布消息如图所示 | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | 最终设备端接收到信息 | ||||||
|  | 
 | ||||||
|  |  | ||||||
| After Width: | Height: | Size: 8.3 KiB | 
| After Width: | Height: | Size: 30 KiB | 
| After Width: | Height: | Size: 27 KiB | 
| After Width: | Height: | Size: 74 KiB | 
| After Width: | Height: | Size: 14 KiB | 
| After Width: | Height: | Size: 39 KiB | 
| After Width: | Height: | Size: 20 KiB | 
| After Width: | Height: | Size: 20 KiB | 
| After Width: | Height: | Size: 35 KiB | 
| After Width: | Height: | Size: 41 KiB | 
| After Width: | Height: | Size: 106 KiB | 
|  | @ -0,0 +1,137 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014, 2017 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Xiang Rong - 442039 Add makefile to Embedded C client | ||||||
|  |  *    Ian Craggs - fix for issue #64, bit order in connack response | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef MQTTCONNECT_H_ | ||||||
|  | #define MQTTCONNECT_H_ | ||||||
|  | 
 | ||||||
|  | #if !defined(DLLImport) | ||||||
|  |   #define DLLImport | ||||||
|  | #endif | ||||||
|  | #if !defined(DLLExport) | ||||||
|  |   #define DLLExport | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef union | ||||||
|  | { | ||||||
|  | 	unsigned char all;	/**< all connect flags */ | ||||||
|  | #if defined(REVERSED) | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		unsigned int username : 1;			/**< 3.1 user name */ | ||||||
|  | 		unsigned int password : 1; 			/**< 3.1 password */ | ||||||
|  | 		unsigned int willRetain : 1;		/**< will retain setting */ | ||||||
|  | 		unsigned int willQoS : 2;				/**< will QoS value */ | ||||||
|  | 		unsigned int will : 1;			    /**< will flag */ | ||||||
|  | 		unsigned int cleansession : 1;	  /**< clean session flag */ | ||||||
|  | 		unsigned int : 1;	  	          /**< unused */ | ||||||
|  | 	} bits; | ||||||
|  | #else | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		unsigned int : 1;	     					/**< unused */ | ||||||
|  | 		unsigned int cleansession : 1;	  /**< cleansession flag */ | ||||||
|  | 		unsigned int will : 1;			    /**< will flag */ | ||||||
|  | 		unsigned int willQoS : 2;				/**< will QoS value */ | ||||||
|  | 		unsigned int willRetain : 1;		/**< will retain setting */ | ||||||
|  | 		unsigned int password : 1; 			/**< 3.1 password */ | ||||||
|  | 		unsigned int username : 1;			/**< 3.1 user name */ | ||||||
|  | 	} bits; | ||||||
|  | #endif | ||||||
|  | } MQTTConnectFlags;	/**< connect flags byte */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Defines the MQTT "Last Will and Testament" (LWT) settings for | ||||||
|  |  * the connect packet. | ||||||
|  |  */ | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	/** The eyecatcher for this structure.  must be MQTW. */ | ||||||
|  | 	char struct_id[4]; | ||||||
|  | 	/** The version number of this structure.  Must be 0 */ | ||||||
|  | 	int struct_version; | ||||||
|  | 	/** The LWT topic to which the LWT message will be published. */ | ||||||
|  | 	MQTTString topicName; | ||||||
|  | 	/** The LWT payload. */ | ||||||
|  | 	MQTTString message; | ||||||
|  | 	/**
 | ||||||
|  |       * The retained flag for the LWT message (see MQTTAsync_message.retained). | ||||||
|  |       */ | ||||||
|  | 	unsigned char retained; | ||||||
|  | 	/**
 | ||||||
|  |       * The quality of service setting for the LWT message (see | ||||||
|  |       * MQTTAsync_message.qos and @ref qos). | ||||||
|  |       */ | ||||||
|  | 	char qos; | ||||||
|  | } MQTTPacket_willOptions; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	/** The eyecatcher for this structure.  must be MQTC. */ | ||||||
|  | 	char struct_id[4]; | ||||||
|  | 	/** The version number of this structure.  Must be 0 */ | ||||||
|  | 	int struct_version; | ||||||
|  | 	/** Version of MQTT to be used.  3 = 3.1 4 = 3.1.1
 | ||||||
|  | 	  */ | ||||||
|  | 	unsigned char MQTTVersion; | ||||||
|  | 	MQTTString clientID; | ||||||
|  | 	unsigned short keepAliveInterval; | ||||||
|  | 	unsigned char cleansession; | ||||||
|  | 	unsigned char willFlag; | ||||||
|  | 	MQTTPacket_willOptions will; | ||||||
|  | 	MQTTString username; | ||||||
|  | 	MQTTString password; | ||||||
|  | } MQTTPacket_connectData; | ||||||
|  | 
 | ||||||
|  | typedef union | ||||||
|  | { | ||||||
|  | 	unsigned char all;	/**< all connack flags */ | ||||||
|  | #if defined(REVERSED) | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  |     unsigned int reserved : 7;	  	    /**< unused */ | ||||||
|  | 		unsigned int sessionpresent : 1;    /**< session present flag */ | ||||||
|  | 	} bits; | ||||||
|  | #else | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		unsigned int sessionpresent : 1;    /**< session present flag */ | ||||||
|  |     unsigned int reserved: 7;	     			/**< unused */ | ||||||
|  | 	} bits; | ||||||
|  | #endif | ||||||
|  | } MQTTConnackFlags;	/**< connack flags byte */ | ||||||
|  | 
 | ||||||
|  | #define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ | ||||||
|  | 		MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); | ||||||
|  | DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); | ||||||
|  | DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); | ||||||
|  | DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); | ||||||
|  | 
 | ||||||
|  | #endif /* MQTTCONNECT_H_ */ | ||||||
|  | @ -0,0 +1,214 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include "StackTrace.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. | ||||||
|  |   * @param options the options to be used to build the connect packet | ||||||
|  |   * @return the length of buffer needed to contain the serialized version of the packet | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_connectLength(MQTTPacket_connectData* options) | ||||||
|  | { | ||||||
|  | 	int len = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 
 | ||||||
|  | 	if (options->MQTTVersion == 3) | ||||||
|  | 		len = 12; /* variable depending on MQTT or MQIsdp */ | ||||||
|  | 	else if (options->MQTTVersion == 4) | ||||||
|  | 		len = 10; | ||||||
|  | 
 | ||||||
|  | 	len += MQTTstrlen(options->clientID)+2; | ||||||
|  | 	if (options->willFlag) | ||||||
|  | 		len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; | ||||||
|  | 	if (options->username.cstring || options->username.lenstring.data) | ||||||
|  | 		len += MQTTstrlen(options->username)+2; | ||||||
|  | 	if (options->password.cstring || options->password.lenstring.data) | ||||||
|  | 		len += MQTTstrlen(options->password)+2; | ||||||
|  | 
 | ||||||
|  | 	FUNC_EXIT_RC(len); | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the connect options into the buffer. | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param len the length in bytes of the supplied buffer | ||||||
|  |   * @param options the options to be used to build the connect packet | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) | ||||||
|  | { | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	MQTTConnectFlags flags = {0}; | ||||||
|  | 	int len = 0; | ||||||
|  | 	int rc = -1; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = CONNECT; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ | ||||||
|  | 
 | ||||||
|  | 	if (options->MQTTVersion == 4) | ||||||
|  | 	{ | ||||||
|  | 		writeCString(&ptr, "MQTT"); | ||||||
|  | 		writeChar(&ptr, (char) 4); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		writeCString(&ptr, "MQIsdp"); | ||||||
|  | 		writeChar(&ptr, (char) 3); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	flags.all = 0; | ||||||
|  | 	flags.bits.cleansession = options->cleansession; | ||||||
|  | 	flags.bits.will = (options->willFlag) ? 1 : 0; | ||||||
|  | 	if (flags.bits.will) | ||||||
|  | 	{ | ||||||
|  | 		flags.bits.willQoS = options->will.qos; | ||||||
|  | 		flags.bits.willRetain = options->will.retained; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (options->username.cstring || options->username.lenstring.data) | ||||||
|  | 		flags.bits.username = 1; | ||||||
|  | 	if (options->password.cstring || options->password.lenstring.data) | ||||||
|  | 		flags.bits.password = 1; | ||||||
|  | 
 | ||||||
|  | 	writeChar(&ptr, flags.all); | ||||||
|  | 	writeInt(&ptr, options->keepAliveInterval); | ||||||
|  | 	writeMQTTString(&ptr, options->clientID); | ||||||
|  | 	if (options->willFlag) | ||||||
|  | 	{ | ||||||
|  | 		writeMQTTString(&ptr, options->will.topicName); | ||||||
|  | 		writeMQTTString(&ptr, options->will.message); | ||||||
|  | 	} | ||||||
|  | 	if (flags.bits.username) | ||||||
|  | 		writeMQTTString(&ptr, options->username); | ||||||
|  | 	if (flags.bits.password) | ||||||
|  | 		writeMQTTString(&ptr, options->password); | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | 
 | ||||||
|  | 	exit: FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into connack data - return code | ||||||
|  |   * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) | ||||||
|  |   * @param connack_rc returned integer value of the connack return code | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param len the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return error code.  1 is success, 0 is failure | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = NULL; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int mylen; | ||||||
|  | 	MQTTConnackFlags flags = {0}; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	if (header.bits.type != CONNACK) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ | ||||||
|  | 	enddata = curdata + mylen; | ||||||
|  | 	if (enddata - curdata < 2) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	flags.all = readChar(&curdata); | ||||||
|  | 	*sessionPresent = flags.bits.sessionpresent; | ||||||
|  | 	*connack_rc = readChar(&curdata); | ||||||
|  | 
 | ||||||
|  | 	rc = 1; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer, to avoid overruns | ||||||
|  |   * @param packettype the message type | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rc = -1; | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (buflen < 2) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = packettype; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer, to avoid overruns | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_disconnect(unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	return MQTTSerialize_zero(buf, buflen, DISCONNECT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer, to avoid overruns | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_pingreq(unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	return MQTTSerialize_zero(buf, buflen, PINGREQ); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,148 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "StackTrace.h" | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #define min(a, b) ((a < b) ? a : b) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Validates MQTT protocol name and version combinations | ||||||
|  |   * @param protocol the MQTT protocol name as an MQTTString | ||||||
|  |   * @param version the MQTT protocol version number, as in the connect packet | ||||||
|  |   * @return correct MQTT combination?  1 is true, 0 is false | ||||||
|  |   */ | ||||||
|  | int MQTTPacket_checkVersion(MQTTString* protocol, int version) | ||||||
|  | { | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp", | ||||||
|  | 			min(6, protocol->lenstring.len)) == 0) | ||||||
|  | 		rc = 1; | ||||||
|  | 	else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT", | ||||||
|  | 			min(4, protocol->lenstring.len)) == 0) | ||||||
|  | 		rc = 1; | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into connect data structure | ||||||
|  |   * @param data the connect data structure to be filled out | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param len the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return error code.  1 is success, 0 is failure | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	MQTTConnectFlags flags = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = &buf[len]; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	MQTTString Protocol; | ||||||
|  | 	int version; | ||||||
|  | 	int mylen = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	if (header.bits.type != CONNECT) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */ | ||||||
|  | 
 | ||||||
|  | 	if (!readMQTTLenString(&Protocol, &curdata, enddata) || | ||||||
|  | 		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	version = (int)readChar(&curdata); /* Protocol version */ | ||||||
|  | 	/* If we don't recognize the protocol version, we don't parse the connect packet on the
 | ||||||
|  | 	 * basis that we don't know what the format will be. | ||||||
|  | 	 */ | ||||||
|  | 	if (MQTTPacket_checkVersion(&Protocol, version)) | ||||||
|  | 	{ | ||||||
|  | 		flags.all = readChar(&curdata); | ||||||
|  | 		data->cleansession = flags.bits.cleansession; | ||||||
|  | 		data->keepAliveInterval = readInt(&curdata); | ||||||
|  | 		if (!readMQTTLenString(&data->clientID, &curdata, enddata)) | ||||||
|  | 			goto exit; | ||||||
|  | 		data->willFlag = flags.bits.will; | ||||||
|  | 		if (flags.bits.will) | ||||||
|  | 		{ | ||||||
|  | 			data->will.qos = flags.bits.willQoS; | ||||||
|  | 			data->will.retained = flags.bits.willRetain; | ||||||
|  | 			if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) || | ||||||
|  | 				  !readMQTTLenString(&data->will.message, &curdata, enddata)) | ||||||
|  | 				goto exit; | ||||||
|  | 		} | ||||||
|  | 		if (flags.bits.username) | ||||||
|  | 		{ | ||||||
|  | 			if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata)) | ||||||
|  | 				goto exit; /* username flag set, but no username supplied - invalid */ | ||||||
|  | 			if (flags.bits.password && | ||||||
|  | 				(enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata))) | ||||||
|  | 				goto exit; /* password flag set, but no password supplied - invalid */ | ||||||
|  | 		} | ||||||
|  | 		else if (flags.bits.password) | ||||||
|  | 			goto exit; /* password flag set without username - invalid */ | ||||||
|  | 		rc = 1; | ||||||
|  | 	} | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the connack packet into the supplied buffer. | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param connack_rc the integer connack return code to be used  | ||||||
|  |   * @param sessionPresent the MQTT 3.1.1 sessionPresent flag | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 	MQTTConnackFlags flags = {0}; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (buflen < 2) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = CONNACK; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ | ||||||
|  | 
 | ||||||
|  | 	flags.all = 0; | ||||||
|  | 	flags.bits.sessionpresent = sessionPresent; | ||||||
|  | 	writeChar(&ptr, flags.all);  | ||||||
|  | 	writeChar(&ptr, connack_rc); | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,107 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "StackTrace.h" | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #define min(a, b) ((a < b) ? 1 : 0) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into publish data | ||||||
|  |   * @param dup returned integer - the MQTT dup flag | ||||||
|  |   * @param qos returned integer - the MQTT QoS value | ||||||
|  |   * @param retained returned integer - the MQTT retained flag | ||||||
|  |   * @param packetid returned integer - the MQTT packet identifier | ||||||
|  |   * @param topicName returned MQTTString - the MQTT topic in the publish | ||||||
|  |   * @param payload returned byte buffer - the MQTT publish payload | ||||||
|  |   * @param payloadlen returned integer - the length of the MQTT payload | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return error code.  1 is success | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, | ||||||
|  | 		unsigned char** payload, int32_t* payloadlen, unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = NULL; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int mylen = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	if (header.bits.type != PUBLISH) | ||||||
|  | 		goto exit; | ||||||
|  | 	*dup = header.bits.dup; | ||||||
|  | 	*qos = header.bits.qos; | ||||||
|  | 	*retained = header.bits.retain; | ||||||
|  | 
 | ||||||
|  | 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ | ||||||
|  | 	enddata = curdata + mylen; | ||||||
|  | 
 | ||||||
|  | 	if (!readMQTTLenString(topicName, &curdata, enddata) || | ||||||
|  | 		enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	if (*qos > 0) | ||||||
|  | 		*packetid = readInt(&curdata); | ||||||
|  | 
 | ||||||
|  | 	*payloadlen = enddata - curdata; | ||||||
|  | 	*payload = curdata; | ||||||
|  | 	rc = 1; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into an ack | ||||||
|  |   * @param packettype returned integer - the MQTT packet type | ||||||
|  |   * @param dup returned integer - the MQTT dup flag | ||||||
|  |   * @param packetid returned integer - the MQTT packet identifier | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return error code.  1 is success, 0 is failure | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = NULL; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int mylen; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	*dup = header.bits.dup; | ||||||
|  | 	*packettype = header.bits.type; | ||||||
|  | 
 | ||||||
|  | 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ | ||||||
|  | 	enddata = curdata + mylen; | ||||||
|  | 
 | ||||||
|  | 	if (enddata - curdata < 2) | ||||||
|  | 		goto exit; | ||||||
|  | 	*packetid = readInt(&curdata); | ||||||
|  | 
 | ||||||
|  | 	rc = 1; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,262 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "StackTrace.h" | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const char* MQTTPacket_names[] = | ||||||
|  | { | ||||||
|  | 	"RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", | ||||||
|  | 	"PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", | ||||||
|  | 	"PINGREQ", "PINGRESP", "DISCONNECT" | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | const char* MQTTPacket_getName(unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	return MQTTPacket_names[packetid]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data) | ||||||
|  | { | ||||||
|  | 	int strindex = 0; | ||||||
|  | 
 | ||||||
|  | 	strindex = snprintf(strbuf, strbuflen, | ||||||
|  | 			"CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d", | ||||||
|  | 			(int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data, | ||||||
|  | 			(int)data->cleansession, data->keepAliveInterval); | ||||||
|  | 	if (data->willFlag) | ||||||
|  | 		strindex += snprintf(&strbuf[strindex], strbuflen - strindex, | ||||||
|  | 				", will QoS %d, will retain %d, will topic %.*s, will message %.*s", | ||||||
|  | 				data->will.qos, data->will.retained, | ||||||
|  | 				data->will.topicName.lenstring.len, data->will.topicName.lenstring.data, | ||||||
|  | 				data->will.message.lenstring.len, data->will.message.lenstring.data); | ||||||
|  | 	if (data->username.lenstring.data && data->username.lenstring.len > 0) | ||||||
|  | 		strindex += snprintf(&strbuf[strindex], strbuflen - strindex, | ||||||
|  | 				", user name %.*s", data->username.lenstring.len, data->username.lenstring.data); | ||||||
|  | 	if (data->password.lenstring.data && data->password.lenstring.len > 0) | ||||||
|  | 		strindex += snprintf(&strbuf[strindex], strbuflen - strindex, | ||||||
|  | 				", password %.*s", data->password.lenstring.len, data->password.lenstring.data); | ||||||
|  | 	return strindex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent) | ||||||
|  | { | ||||||
|  | 	int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc); | ||||||
|  | 	return strindex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, | ||||||
|  | 		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen) | ||||||
|  | { | ||||||
|  | 	int strindex = snprintf(strbuf, strbuflen, | ||||||
|  | 				"PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s", | ||||||
|  | 				dup, qos, retained, packetid, | ||||||
|  | 				(topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data, | ||||||
|  | 				payloadlen, (payloadlen < 20) ? payloadlen : 20, payload); | ||||||
|  | 	return strindex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid); | ||||||
|  | 	if (dup) | ||||||
|  | 		strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup); | ||||||
|  | 	return strindex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, | ||||||
|  | 		MQTTString topicFilters[], int requestedQoSs[]) | ||||||
|  | { | ||||||
|  | 	return snprintf(strbuf, strbuflen, | ||||||
|  | 		"SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d", | ||||||
|  | 		dup, packetid, count, | ||||||
|  | 		topicFilters[0].lenstring.len, topicFilters[0].lenstring.data, | ||||||
|  | 		requestedQoSs[0]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs) | ||||||
|  | { | ||||||
|  | 	return snprintf(strbuf, strbuflen, | ||||||
|  | 		"SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, | ||||||
|  | 		int count, MQTTString topicFilters[]) | ||||||
|  | { | ||||||
|  | 	return snprintf(strbuf, strbuflen, | ||||||
|  | 					"UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s", | ||||||
|  | 					dup, packetid, count, | ||||||
|  | 					topicFilters[0].lenstring.len, topicFilters[0].lenstring.data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #if defined(MQTT_CLIENT) | ||||||
|  | char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	int index = 0; | ||||||
|  | 	int rem_length = 0; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int strindex = 0; | ||||||
|  | 
 | ||||||
|  | 	header.byte = buf[index++]; | ||||||
|  | 	index += MQTTPacket_decodeBuf(&buf[index], &rem_length); | ||||||
|  | 
 | ||||||
|  | 	switch (header.bits.type) | ||||||
|  | 	{ | ||||||
|  | 
 | ||||||
|  | 	case CONNACK: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char sessionPresent, connack_rc; | ||||||
|  | 		if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case PUBLISH: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char dup, retained, *payload; | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		int qos, payloadlen; | ||||||
|  | 		MQTTString topicName = MQTTString_initializer; | ||||||
|  | 		if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, | ||||||
|  | 				&payload, &payloadlen, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, | ||||||
|  | 					topicName, payload, payloadlen); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case PUBACK: | ||||||
|  | 	case PUBREC: | ||||||
|  | 	case PUBREL: | ||||||
|  | 	case PUBCOMP: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char packettype, dup; | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case SUBACK: | ||||||
|  | 	{ | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		int maxcount = 1, count = 0; | ||||||
|  | 		int grantedQoSs[1]; | ||||||
|  | 		if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case UNSUBACK: | ||||||
|  | 	{ | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case PINGREQ: | ||||||
|  | 	case PINGRESP: | ||||||
|  | 	case DISCONNECT: | ||||||
|  | 		strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return strbuf; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(MQTT_SERVER) | ||||||
|  | char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	int index = 0; | ||||||
|  | 	int rem_length = 0; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int strindex = 0; | ||||||
|  | 
 | ||||||
|  | 	header.byte = buf[index++]; | ||||||
|  | 	index += MQTTPacket_decodeBuf(&buf[index], &rem_length); | ||||||
|  | 
 | ||||||
|  | 	switch (header.bits.type) | ||||||
|  | 	{ | ||||||
|  | 	case CONNECT: | ||||||
|  | 	{ | ||||||
|  | 		MQTTPacket_connectData data; | ||||||
|  | 		int rc; | ||||||
|  | 		if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case PUBLISH: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char dup, retained, *payload; | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		int qos, payloadlen; | ||||||
|  | 		MQTTString topicName = MQTTString_initializer; | ||||||
|  | 		if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, | ||||||
|  | 				&payload, &payloadlen, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, | ||||||
|  | 					topicName, payload, payloadlen); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case PUBACK: | ||||||
|  | 	case PUBREC: | ||||||
|  | 	case PUBREL: | ||||||
|  | 	case PUBCOMP: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char packettype, dup; | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case SUBSCRIBE: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char dup; | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		int maxcount = 1, count = 0; | ||||||
|  | 		MQTTString topicFilters[1]; | ||||||
|  | 		int requestedQoSs[1]; | ||||||
|  | 		if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count, | ||||||
|  | 				topicFilters, requestedQoSs, buf, buflen) == 1) | ||||||
|  | 			strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);; | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case UNSUBSCRIBE: | ||||||
|  | 	{ | ||||||
|  | 		unsigned char dup; | ||||||
|  | 		unsigned short packetid; | ||||||
|  | 		int maxcount = 1, count = 0; | ||||||
|  | 		MQTTString topicFilters[1]; | ||||||
|  | 		if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1) | ||||||
|  | 			strindex =  MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters); | ||||||
|  | 	} | ||||||
|  | 	break; | ||||||
|  | 	case PINGREQ: | ||||||
|  | 	case PINGRESP: | ||||||
|  | 	case DISCONNECT: | ||||||
|  | 		strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	strbuf[strbuflen] = '\0'; | ||||||
|  | 	return strbuf; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,37 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #if !defined(MQTTFORMAT_H) | ||||||
|  | #define MQTTFORMAT_H | ||||||
|  | 
 | ||||||
|  | #include "StackTrace.h" | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | 
 | ||||||
|  | const char* MQTTPacket_getName(unsigned short packetid); | ||||||
|  | int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data); | ||||||
|  | int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent); | ||||||
|  | int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, | ||||||
|  | 		unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen); | ||||||
|  | int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid); | ||||||
|  | int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, | ||||||
|  | 		MQTTString topicFilters[], int requestedQoSs[]); | ||||||
|  | int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs); | ||||||
|  | int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, | ||||||
|  | 		int count, MQTTString topicFilters[]); | ||||||
|  | char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); | ||||||
|  | char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,412 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Sergio R. Caprile - non-blocking packet read functions for stream transport | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "StackTrace.h" | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Encodes the message length according to the MQTT algorithm | ||||||
|  |  * @param buf the buffer into which the encoded data is written | ||||||
|  |  * @param length the length to be encoded | ||||||
|  |  * @return the number of bytes written to buffer | ||||||
|  |  */ | ||||||
|  | int MQTTPacket_encode(unsigned char* buf, int length) | ||||||
|  | { | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	do | ||||||
|  | 	{ | ||||||
|  | 		char d = length % 128; | ||||||
|  | 		length /= 128; | ||||||
|  | 		/* if there are more digits to encode, set the top bit of this digit */ | ||||||
|  | 		if (length > 0) | ||||||
|  | 			d |= 0x80; | ||||||
|  | 		buf[rc++] = d; | ||||||
|  | 	} while (length > 0); | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Decodes the message length according to the MQTT algorithm | ||||||
|  |  * @param getcharfn pointer to function to read the next character from the data source | ||||||
|  |  * @param value the decoded length returned | ||||||
|  |  * @return the number of bytes read from the socket | ||||||
|  |  */ | ||||||
|  | int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) | ||||||
|  | { | ||||||
|  | 	unsigned char c; | ||||||
|  | 	int multiplier = 1; | ||||||
|  | 	int len = 0; | ||||||
|  | #define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	*value = 0; | ||||||
|  | 	do | ||||||
|  | 	{ | ||||||
|  | 		int rc = MQTTPACKET_READ_ERROR; | ||||||
|  | 
 | ||||||
|  | 		if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) | ||||||
|  | 		{ | ||||||
|  | 			rc = MQTTPACKET_READ_ERROR;	/* bad data */ | ||||||
|  | 			goto exit; | ||||||
|  | 		} | ||||||
|  | 		rc = (*getcharfn)(&c, 1); | ||||||
|  | 		if (rc != 1) | ||||||
|  | 			goto exit; | ||||||
|  | 		*value += (c & 127) * multiplier; | ||||||
|  | 		multiplier *= 128; | ||||||
|  | 	} while ((c & 128) != 0); | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(len); | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTPacket_len(int rem_len) | ||||||
|  | { | ||||||
|  | 	rem_len += 1; /* header byte */ | ||||||
|  | 
 | ||||||
|  | 	/* now remaining_length field */ | ||||||
|  | 	if (rem_len < 128) | ||||||
|  | 		rem_len += 1; | ||||||
|  | 	else if (rem_len < 16384) | ||||||
|  | 		rem_len += 2; | ||||||
|  | 	else if (rem_len < 2097151) | ||||||
|  | 		rem_len += 3; | ||||||
|  | 	else | ||||||
|  | 		rem_len += 4; | ||||||
|  | 	return rem_len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static unsigned char* bufptr; | ||||||
|  | 
 | ||||||
|  | int bufchar(unsigned char* c, int count) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < count; ++i) | ||||||
|  | 		*c = *bufptr++; | ||||||
|  | 	return count; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int MQTTPacket_decodeBuf(unsigned char* buf, int* value) | ||||||
|  | { | ||||||
|  | 	bufptr = buf; | ||||||
|  | 	return MQTTPacket_decode(bufchar, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Calculates an integer from two bytes read from the input buffer | ||||||
|  |  * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned | ||||||
|  |  * @return the integer value calculated | ||||||
|  |  */ | ||||||
|  | int readInt(unsigned char** pptr) | ||||||
|  | { | ||||||
|  | 	unsigned char* ptr = *pptr; | ||||||
|  | 	int len = 256*(*ptr) + (*(ptr+1)); | ||||||
|  | 	*pptr += 2; | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reads one character from the input buffer. | ||||||
|  |  * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned | ||||||
|  |  * @return the character read | ||||||
|  |  */ | ||||||
|  | char readChar(unsigned char** pptr) | ||||||
|  | { | ||||||
|  | 	char c = **pptr; | ||||||
|  | 	(*pptr)++; | ||||||
|  | 	return c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Writes one character to an output buffer. | ||||||
|  |  * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned | ||||||
|  |  * @param c the character to write | ||||||
|  |  */ | ||||||
|  | void writeChar(unsigned char** pptr, char c) | ||||||
|  | { | ||||||
|  | 	**pptr = c; | ||||||
|  | 	(*pptr)++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Writes an integer as 2 bytes to an output buffer. | ||||||
|  |  * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned | ||||||
|  |  * @param anInt the integer to write | ||||||
|  |  */ | ||||||
|  | void writeInt(unsigned char** pptr, int anInt) | ||||||
|  | { | ||||||
|  | 	**pptr = (unsigned char)(anInt / 256); | ||||||
|  | 	(*pptr)++; | ||||||
|  | 	**pptr = (unsigned char)(anInt % 256); | ||||||
|  | 	(*pptr)++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Writes a "UTF" string to an output buffer.  Converts C string to length-delimited. | ||||||
|  |  * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned | ||||||
|  |  * @param string the C string to write | ||||||
|  |  */ | ||||||
|  | void writeCString(unsigned char** pptr, const char* string) | ||||||
|  | { | ||||||
|  | 	int len = strlen(string); | ||||||
|  | 	writeInt(pptr, len); | ||||||
|  | 	memcpy(*pptr, string, len); | ||||||
|  | 	*pptr += len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int getLenStringLen(char* ptr) | ||||||
|  | { | ||||||
|  | 	int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) | ||||||
|  | { | ||||||
|  | 	if (mqttstring.lenstring.len > 0) | ||||||
|  | 	{ | ||||||
|  | 		writeInt(pptr, mqttstring.lenstring.len); | ||||||
|  | 		memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); | ||||||
|  | 		*pptr += mqttstring.lenstring.len; | ||||||
|  | 	} | ||||||
|  | 	else if (mqttstring.cstring) | ||||||
|  | 		writeCString(pptr, mqttstring.cstring); | ||||||
|  | 	else | ||||||
|  | 		writeInt(pptr, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @param mqttstring the MQTTString structure into which the data is to be read | ||||||
|  |  * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned | ||||||
|  |  * @param enddata pointer to the end of the data: do not read beyond | ||||||
|  |  * @return 1 if successful, 0 if not | ||||||
|  |  */ | ||||||
|  | int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) | ||||||
|  | { | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	/* the first two bytes are the length of the string */ | ||||||
|  | 	if (enddata - (*pptr) > 1) /* enough length to read the integer? */ | ||||||
|  | 	{ | ||||||
|  | 		mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ | ||||||
|  | 		if (&(*pptr)[mqttstring->lenstring.len] <= enddata) | ||||||
|  | 		{ | ||||||
|  | 			mqttstring->lenstring.data = (char*)*pptr; | ||||||
|  | 			*pptr += mqttstring->lenstring.len; | ||||||
|  | 			rc = 1; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	mqttstring->cstring = NULL; | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string | ||||||
|  |  * @param mqttstring the string to return the length of | ||||||
|  |  * @return the length of the string | ||||||
|  |  */ | ||||||
|  | int MQTTstrlen(MQTTString mqttstring) | ||||||
|  | { | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	if (mqttstring.cstring) | ||||||
|  | 		rc = strlen(mqttstring.cstring); | ||||||
|  | 	else | ||||||
|  | 		rc = mqttstring.lenstring.len; | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Compares an MQTTString to a C string | ||||||
|  |  * @param a the MQTTString to compare | ||||||
|  |  * @param bptr the C string to compare | ||||||
|  |  * @return boolean - equal or not | ||||||
|  |  */ | ||||||
|  | int MQTTPacket_equals(MQTTString* a, char* bptr) | ||||||
|  | { | ||||||
|  | 	int alen = 0, | ||||||
|  | 		blen = 0; | ||||||
|  | 	char *aptr; | ||||||
|  | 	 | ||||||
|  | 	if (a->cstring) | ||||||
|  | 	{ | ||||||
|  | 		aptr = a->cstring; | ||||||
|  | 		alen = strlen(a->cstring); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		aptr = a->lenstring.data; | ||||||
|  | 		alen = a->lenstring.len; | ||||||
|  | 	} | ||||||
|  | 	blen = strlen(bptr); | ||||||
|  | 	 | ||||||
|  | 	return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Helper function to read packet data from some source into a buffer | ||||||
|  |  * @param buf the buffer into which the packet will be serialized | ||||||
|  |  * @param buflen the length in bytes of the supplied buffer | ||||||
|  |  * @param getfn pointer to a function which will read any number of bytes from the needed source | ||||||
|  |  * @return integer MQTT packet type, or -1 on error | ||||||
|  |  * @note  the whole message must fit into the caller's buffer | ||||||
|  |  */ | ||||||
|  | int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) | ||||||
|  | { | ||||||
|  | 	int rc = -1; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int len = 0; | ||||||
|  | 	int rem_len = 0; | ||||||
|  | 
 | ||||||
|  | 	/* 1. read the header byte.  This has the packet type in it */ | ||||||
|  | 	if ((*getfn)(buf, 1) != 1) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	len = 1; | ||||||
|  | 	/* 2. read the remaining length.  This is variable in itself */ | ||||||
|  | 	MQTTPacket_decode(getfn, &rem_len); | ||||||
|  | 	len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ | ||||||
|  | 
 | ||||||
|  | 	/* 3. read the rest of the buffer using a callback to supply the rest of the data */ | ||||||
|  | 	if((rem_len + len) > buflen) | ||||||
|  | 		goto exit; | ||||||
|  | 	if (rem_len && ((*getfn)(buf + len, rem_len) != rem_len)) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	header.byte = buf[0]; | ||||||
|  | 	rc = header.bits.type; | ||||||
|  | exit: | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Decodes the message length according to the MQTT algorithm, non-blocking | ||||||
|  |  * @param trp pointer to a transport structure holding what is needed to solve getting data from it | ||||||
|  |  * @param value the decoded length returned | ||||||
|  |  * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error | ||||||
|  |  */ | ||||||
|  | static int MQTTPacket_decodenb(MQTTTransport *trp) | ||||||
|  | { | ||||||
|  | 	unsigned char c; | ||||||
|  | 	int rc = MQTTPACKET_READ_ERROR; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if(trp->len == 0){		/* initialize on first call */ | ||||||
|  | 		trp->multiplier = 1; | ||||||
|  | 		trp->rem_len = 0; | ||||||
|  | 	} | ||||||
|  | 	do { | ||||||
|  | 		int frc; | ||||||
|  | 		if (trp->len >= MAX_NO_OF_REMAINING_LENGTH_BYTES) | ||||||
|  | 			goto exit; | ||||||
|  | 		if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) | ||||||
|  | 			goto exit; | ||||||
|  | 		if (frc == 0){ | ||||||
|  | 			rc = 0; | ||||||
|  | 			goto exit; | ||||||
|  | 		} | ||||||
|  | 		++(trp->len); | ||||||
|  | 		trp->rem_len += (c & 127) * trp->multiplier; | ||||||
|  | 		trp->multiplier *= 128; | ||||||
|  | 	} while ((c & 128) != 0); | ||||||
|  | 	rc = trp->len; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Helper function to read packet data from some source into a buffer, non-blocking | ||||||
|  |  * @param buf the buffer into which the packet will be serialized | ||||||
|  |  * @param buflen the length in bytes of the supplied buffer | ||||||
|  |  * @param trp pointer to a transport structure holding what is needed to solve getting data from it | ||||||
|  |  * @return integer MQTT packet type, 0 for call again, or -1 on error | ||||||
|  |  * @note  the whole message must fit into the caller's buffer | ||||||
|  |  */ | ||||||
|  | int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) | ||||||
|  | { | ||||||
|  | 	int rc = -1, frc; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 
 | ||||||
|  | 	switch(trp->state){ | ||||||
|  | 	default: | ||||||
|  | 		trp->state = 0; | ||||||
|  | 		/*FALLTHROUGH*/ | ||||||
|  | 	case 0: | ||||||
|  | 		/* read the header byte.  This has the packet type in it */ | ||||||
|  | 		if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) | ||||||
|  | 			goto exit; | ||||||
|  | 		if (frc == 0) | ||||||
|  | 			return 0; | ||||||
|  | 		trp->len = 0; | ||||||
|  | 		++trp->state; | ||||||
|  | 		/*FALLTHROUGH*/ | ||||||
|  | 		/* read the remaining length.  This is variable in itself */ | ||||||
|  | 	case 1: | ||||||
|  | 		if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) | ||||||
|  | 			goto exit; | ||||||
|  | 		if(frc == 0) | ||||||
|  | 			return 0; | ||||||
|  | 		trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ | ||||||
|  | 		if((trp->rem_len + trp->len) > buflen) | ||||||
|  | 			goto exit; | ||||||
|  | 		++trp->state; | ||||||
|  | 		/*FALLTHROUGH*/ | ||||||
|  | 	case 2: | ||||||
|  | 		if(trp->rem_len){ | ||||||
|  | 			/* read the rest of the buffer using a callback to supply the rest of the data */ | ||||||
|  | 			if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) | ||||||
|  | 				goto exit; | ||||||
|  | 			if (frc == 0) | ||||||
|  | 				return 0; | ||||||
|  | 			trp->rem_len -= frc; | ||||||
|  | 			trp->len += frc; | ||||||
|  | 			if(trp->rem_len) | ||||||
|  | 				return 0; | ||||||
|  | 		} | ||||||
|  | 		header.byte = buf[0]; | ||||||
|  | 		rc = header.bits.type; | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | exit: | ||||||
|  | 	trp->state = 0; | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,134 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Xiang Rong - 442039 Add makefile to Embedded C client | ||||||
|  |  *******************************************************************************/ | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #ifndef MQTTPACKET_H_ | ||||||
|  | #define MQTTPACKET_H_ | ||||||
|  | 
 | ||||||
|  | #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(WIN32_DLL) || defined(WIN64_DLL) | ||||||
|  |   #define DLLImport __declspec(dllimport) | ||||||
|  |   #define DLLExport __declspec(dllexport) | ||||||
|  | #elif defined(LINUX_SO) | ||||||
|  |   #define DLLImport extern | ||||||
|  |   #define DLLExport  __attribute__ ((visibility ("default"))) | ||||||
|  | #else | ||||||
|  |   #define DLLImport | ||||||
|  |   #define DLLExport   | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | enum errors | ||||||
|  | { | ||||||
|  | 	MQTTPACKET_BUFFER_TOO_SHORT = -2, | ||||||
|  | 	MQTTPACKET_READ_ERROR = -1, | ||||||
|  | 	MQTTPACKET_READ_COMPLETE | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum msgTypes | ||||||
|  | { | ||||||
|  | 	CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, | ||||||
|  | 	PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, | ||||||
|  | 	PINGREQ, PINGRESP, DISCONNECT | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Bitfields for the MQTT header byte. | ||||||
|  |  */ | ||||||
|  | typedef union | ||||||
|  | { | ||||||
|  | 	unsigned char byte;	                /**< the whole byte */ | ||||||
|  | #if defined(REVERSED) | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		unsigned int type : 4;			/**< message type nibble */ | ||||||
|  | 		unsigned int dup : 1;				/**< DUP flag bit */ | ||||||
|  | 		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */ | ||||||
|  | 		unsigned int retain : 1;		/**< retained flag bit */ | ||||||
|  | 	} bits; | ||||||
|  | #else | ||||||
|  | 	struct | ||||||
|  | 	{ | ||||||
|  | 		unsigned int retain : 1;		/**< retained flag bit */ | ||||||
|  | 		unsigned int qos : 2;				/**< QoS value, 0, 1 or 2 */ | ||||||
|  | 		unsigned int dup : 1;				/**< DUP flag bit */ | ||||||
|  | 		unsigned int type : 4;			/**< message type nibble */ | ||||||
|  | 	} bits; | ||||||
|  | #endif | ||||||
|  | } MQTTHeader; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	int len; | ||||||
|  | 	char* data; | ||||||
|  | } MQTTLenString; | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  | 	char* cstring; | ||||||
|  | 	MQTTLenString lenstring; | ||||||
|  | } MQTTString; | ||||||
|  | 
 | ||||||
|  | #define MQTTString_initializer {NULL, {0, NULL}} | ||||||
|  | 
 | ||||||
|  | int MQTTstrlen(MQTTString mqttstring); | ||||||
|  | 
 | ||||||
|  | #include "MQTTConnect.h" | ||||||
|  | #include "MQTTPublish.h" | ||||||
|  | #include "MQTTSubscribe.h" | ||||||
|  | #include "MQTTUnsubscribe.h" | ||||||
|  | #include "MQTTFormat.h" | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); | ||||||
|  | DLLExport int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); | ||||||
|  | 
 | ||||||
|  | int MQTTPacket_len(int rem_len); | ||||||
|  | DLLExport int MQTTPacket_equals(MQTTString* a, char* b); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTPacket_encode(unsigned char* buf, int length); | ||||||
|  | int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); | ||||||
|  | int MQTTPacket_decodeBuf(unsigned char* buf, int* value); | ||||||
|  | 
 | ||||||
|  | int readInt(unsigned char** pptr); | ||||||
|  | char readChar(unsigned char** pptr); | ||||||
|  | void writeChar(unsigned char** pptr, char c); | ||||||
|  | void writeInt(unsigned char** pptr, int anInt); | ||||||
|  | int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); | ||||||
|  | void writeCString(unsigned char** pptr, const char* string); | ||||||
|  | void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  | 	int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ | ||||||
|  | 	void *sck;	/* pointer to whatever the system may use to identify the transport */ | ||||||
|  | 	int multiplier; | ||||||
|  | 	int rem_len; | ||||||
|  | 	int len; | ||||||
|  | 	char state; | ||||||
|  | }MQTTTransport; | ||||||
|  | 
 | ||||||
|  | int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* MQTTPACKET_H_ */ | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Xiang Rong - 442039 Add makefile to Embedded C client | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef MQTTPUBLISH_H_ | ||||||
|  | #define MQTTPUBLISH_H_ | ||||||
|  | 
 | ||||||
|  | #if !defined(DLLImport) | ||||||
|  |   #define DLLImport  | ||||||
|  | #endif | ||||||
|  | #if !defined(DLLExport) | ||||||
|  |   #define DLLExport | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, | ||||||
|  | 		MQTTString topicName, unsigned char* payload, int payloadlen); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, | ||||||
|  | 		unsigned char** payload, int32_t* payloadlen, unsigned char* buf, int len); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); | ||||||
|  | DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); | ||||||
|  | DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); | ||||||
|  | 
 | ||||||
|  | #endif /* MQTTPUBLISH_H_ */ | ||||||
|  | @ -0,0 +1,169 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144
 | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include "StackTrace.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Determines the length of the MQTT publish packet that would be produced using the supplied parameters | ||||||
|  |   * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) | ||||||
|  |   * @param topicName the topic name to be used in the publish   | ||||||
|  |   * @param payloadlen the length of the payload to be sent | ||||||
|  |   * @return the length of buffer needed to contain the serialized version of the packet | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) | ||||||
|  | { | ||||||
|  | 	int len = 0; | ||||||
|  | 
 | ||||||
|  | 	len += 2 + MQTTstrlen(topicName) + payloadlen; | ||||||
|  | 	if (qos > 0) | ||||||
|  | 		len += 2; /* packetid */ | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the supplied publish data into the supplied buffer, ready for sending | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param dup integer - the MQTT dup flag | ||||||
|  |   * @param qos integer - the MQTT QoS value | ||||||
|  |   * @param retained integer - the MQTT retained flag | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @param topicName MQTTString - the MQTT topic in the publish | ||||||
|  |   * @param payload byte buffer - the MQTT publish payload | ||||||
|  |   * @param payloadlen integer - the length of the MQTT payload | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, | ||||||
|  | 		MQTTString topicName, unsigned char* payload, int payloadlen) | ||||||
|  | { | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rem_len = 0; | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	header.bits.type = PUBLISH; | ||||||
|  | 	header.bits.dup = dup; | ||||||
|  | 	header.bits.qos = qos; | ||||||
|  | 	header.bits.retain = retained; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; | ||||||
|  | 
 | ||||||
|  | 	writeMQTTString(&ptr, topicName); | ||||||
|  | 
 | ||||||
|  | 	if (qos > 0) | ||||||
|  | 		writeInt(&ptr, packetid); | ||||||
|  | 
 | ||||||
|  | 	memcpy(ptr, payload, payloadlen); | ||||||
|  | 	ptr += payloadlen; | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | 
 | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the ack packet into the supplied buffer. | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param type the MQTT packet type | ||||||
|  |   * @param dup the MQTT dup flag | ||||||
|  |   * @param packetid the MQTT packet identifier | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (buflen < 4) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 	header.bits.type = packettype; | ||||||
|  | 	header.bits.dup = dup; | ||||||
|  | 	header.bits.qos = (packettype == PUBREL) ? 1 : 0; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ | ||||||
|  | 	writeInt(&ptr, packetid); | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes a puback packet into the supplied buffer. | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes a pubrel packet into the supplied buffer. | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param dup integer - the MQTT dup flag | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes a pubrel packet into the supplied buffer. | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @return serialized length, or error if 0 | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,39 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Xiang Rong - 442039 Add makefile to Embedded C client | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef MQTTSUBSCRIBE_H_ | ||||||
|  | #define MQTTSUBSCRIBE_H_ | ||||||
|  | 
 | ||||||
|  | #if !defined(DLLImport) | ||||||
|  |   #define DLLImport  | ||||||
|  | #endif | ||||||
|  | #if !defined(DLLExport) | ||||||
|  |   #define DLLExport | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, | ||||||
|  | 		int count, MQTTString topicFilters[], int32_t requestedQoSs[]); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, | ||||||
|  | 		int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int32_t* count, int32_t grantedQoSs[], unsigned char* buf, int len); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* MQTTSUBSCRIBE_H_ */ | ||||||
|  | @ -0,0 +1,137 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include "StackTrace.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters | ||||||
|  |   * @param count the number of topic filter strings in topicFilters | ||||||
|  |   * @param topicFilters the array of topic filter strings to be used in the publish | ||||||
|  |   * @return the length of buffer needed to contain the serialized version of the packet | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	int len = 2; /* packetid */ | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < count; ++i) | ||||||
|  | 		len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the supplied subscribe data into the supplied buffer, ready for sending | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied bufferr | ||||||
|  |   * @param dup integer - the MQTT dup flag | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @param count - number of members in the topicFilters and reqQos arrays | ||||||
|  |   * @param topicFilters - array of topic filter names | ||||||
|  |   * @param requestedQoSs - array of requested QoS | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, | ||||||
|  | 		MQTTString topicFilters[], int32_t requestedQoSs[]) | ||||||
|  | { | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rem_len = 0; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int i = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = SUBSCRIBE; | ||||||
|  | 	header.bits.dup = dup; | ||||||
|  | 	header.bits.qos = 1; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; | ||||||
|  | 
 | ||||||
|  | 	writeInt(&ptr, packetid); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < count; ++i) | ||||||
|  | 	{ | ||||||
|  | 		writeMQTTString(&ptr, topicFilters[i]); | ||||||
|  | 		writeChar(&ptr, requestedQoSs[i]); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into suback data | ||||||
|  |   * @param packetid returned integer - the MQTT packet identifier | ||||||
|  |   * @param maxcount - the maximum number of members allowed in the grantedQoSs array | ||||||
|  |   * @param count returned integer - number of members in the grantedQoSs array | ||||||
|  |   * @param grantedQoSs returned array of integers - the granted qualities of service | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return error code.  1 is success, 0 is failure | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int32_t* count, int32_t grantedQoSs[], unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = NULL; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int mylen; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	if (header.bits.type != SUBACK) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ | ||||||
|  | 	enddata = curdata + mylen; | ||||||
|  | 	if (enddata - curdata < 2) | ||||||
|  | 		goto exit; | ||||||
|  | 
 | ||||||
|  | 	*packetid = readInt(&curdata); | ||||||
|  | 
 | ||||||
|  | 	*count = 0; | ||||||
|  | 	while (curdata < enddata) | ||||||
|  | 	{ | ||||||
|  | 		if (*count > maxcount) | ||||||
|  | 		{ | ||||||
|  | 			rc = -1; | ||||||
|  | 			goto exit; | ||||||
|  | 		} | ||||||
|  | 		grantedQoSs[(*count)++] = readChar(&curdata); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc = 1; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,112 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include "StackTrace.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into subscribe data | ||||||
|  |   * @param dup integer returned - the MQTT dup flag | ||||||
|  |   * @param packetid integer returned - the MQTT packet identifier | ||||||
|  |   * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays | ||||||
|  |   * @param count - number of members in the topicFilters and requestedQoSs arrays | ||||||
|  |   * @param topicFilters - array of topic filter names | ||||||
|  |   * @param requestedQoSs - array of requested QoS | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], | ||||||
|  | 	int requestedQoSs[], unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = NULL; | ||||||
|  | 	int rc = -1; | ||||||
|  | 	int mylen = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	if (header.bits.type != SUBSCRIBE) | ||||||
|  | 		goto exit; | ||||||
|  | 	*dup = header.bits.dup; | ||||||
|  | 
 | ||||||
|  | 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ | ||||||
|  | 	enddata = curdata + mylen; | ||||||
|  | 
 | ||||||
|  | 	*packetid = readInt(&curdata); | ||||||
|  | 
 | ||||||
|  | 	*count = 0; | ||||||
|  | 	while (curdata < enddata) | ||||||
|  | 	{ | ||||||
|  | 		if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) | ||||||
|  | 			goto exit; | ||||||
|  | 		if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */ | ||||||
|  | 			goto exit; | ||||||
|  | 		requestedQoSs[*count] = readChar(&curdata); | ||||||
|  | 		(*count)++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc = 1; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the supplied suback data into the supplied buffer, ready for sending | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @param count - number of members in the grantedQoSs array | ||||||
|  |   * @param grantedQoSs - array of granted QoS | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rc = -1; | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (buflen < 2 + count) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = SUBACK; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */ | ||||||
|  | 
 | ||||||
|  | 	writeInt(&ptr, packetid); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < count; ++i) | ||||||
|  | 		writeChar(&ptr, grantedQoSs[i]); | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,38 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Xiang Rong - 442039 Add makefile to Embedded C client | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef MQTTUNSUBSCRIBE_H_ | ||||||
|  | #define MQTTUNSUBSCRIBE_H_ | ||||||
|  | 
 | ||||||
|  | #if !defined(DLLImport) | ||||||
|  |   #define DLLImport  | ||||||
|  | #endif | ||||||
|  | #if !defined(DLLExport) | ||||||
|  |   #define DLLExport | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, | ||||||
|  | 		int count, MQTTString topicFilters[]); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], | ||||||
|  | 		unsigned char* buf, int len); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); | ||||||
|  | 
 | ||||||
|  | DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); | ||||||
|  | 
 | ||||||
|  | #endif /* MQTTUNSUBSCRIBE_H_ */ | ||||||
|  | @ -0,0 +1,106 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include "StackTrace.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters | ||||||
|  |   * @param count the number of topic filter strings in topicFilters | ||||||
|  |   * @param topicFilters the array of topic filter strings to be used in the publish | ||||||
|  |   * @return the length of buffer needed to contain the serialized version of the packet | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 	int len = 2; /* packetid */ | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < count; ++i) | ||||||
|  | 		len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ | ||||||
|  | 	return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @param dup integer - the MQTT dup flag | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @param count - number of members in the topicFilters array | ||||||
|  |   * @param topicFilters - array of topic filter names | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, | ||||||
|  | 		int count, MQTTString topicFilters[]) | ||||||
|  | { | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rem_len = 0; | ||||||
|  | 	int rc = -1; | ||||||
|  | 	int i = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = UNSUBSCRIBE; | ||||||
|  | 	header.bits.dup = dup; | ||||||
|  | 	header.bits.qos = 1; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; | ||||||
|  | 
 | ||||||
|  | 	writeInt(&ptr, packetid); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < count; ++i) | ||||||
|  | 		writeMQTTString(&ptr, topicFilters[i]); | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into unsuback data | ||||||
|  |   * @param packetid returned integer - the MQTT packet identifier | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return error code.  1 is success, 0 is failure | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) | ||||||
|  | { | ||||||
|  | 	unsigned char type = 0; | ||||||
|  | 	unsigned char dup = 0; | ||||||
|  | 	int rc = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); | ||||||
|  | 	if (type == UNSUBACK) | ||||||
|  | 		rc = 1; | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,102 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #include "MQTTPacket.h" | ||||||
|  | #include "StackTrace.h" | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Deserializes the supplied (wire) buffer into unsubscribe data | ||||||
|  |   * @param dup integer returned - the MQTT dup flag | ||||||
|  |   * @param packetid integer returned - the MQTT packet identifier | ||||||
|  |   * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays | ||||||
|  |   * @param count - number of members in the topicFilters and requestedQoSs arrays | ||||||
|  |   * @param topicFilters - array of topic filter names | ||||||
|  |   * @param buf the raw buffer data, of the correct length determined by the remaining length field | ||||||
|  |   * @param buflen the length in bytes of the data in the supplied buffer | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], | ||||||
|  | 		unsigned char* buf, int len) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	unsigned char* curdata = buf; | ||||||
|  | 	unsigned char* enddata = NULL; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	int mylen = 0; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	header.byte = readChar(&curdata); | ||||||
|  | 	if (header.bits.type != UNSUBSCRIBE) | ||||||
|  | 		goto exit; | ||||||
|  | 	*dup = header.bits.dup; | ||||||
|  | 
 | ||||||
|  | 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ | ||||||
|  | 	enddata = curdata + mylen; | ||||||
|  | 
 | ||||||
|  | 	*packetid = readInt(&curdata); | ||||||
|  | 
 | ||||||
|  | 	*count = 0; | ||||||
|  | 	while (curdata < enddata) | ||||||
|  | 	{ | ||||||
|  | 		if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) | ||||||
|  | 			goto exit; | ||||||
|  | 		(*count)++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc = 1; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |   * Serializes the supplied unsuback data into the supplied buffer, ready for sending | ||||||
|  |   * @param buf the buffer into which the packet will be serialized | ||||||
|  |   * @param buflen the length in bytes of the supplied buffer | ||||||
|  |   * @param packetid integer - the MQTT packet identifier | ||||||
|  |   * @return the length of the serialized data.  <= 0 indicates error | ||||||
|  |   */ | ||||||
|  | int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid) | ||||||
|  | { | ||||||
|  | 	MQTTHeader header = {0}; | ||||||
|  | 	int rc = 0; | ||||||
|  | 	unsigned char *ptr = buf; | ||||||
|  | 
 | ||||||
|  | 	FUNC_ENTRY; | ||||||
|  | 	if (buflen < 2) | ||||||
|  | 	{ | ||||||
|  | 		rc = MQTTPACKET_BUFFER_TOO_SHORT; | ||||||
|  | 		goto exit; | ||||||
|  | 	} | ||||||
|  | 	header.byte = 0; | ||||||
|  | 	header.bits.type = UNSUBACK; | ||||||
|  | 	writeChar(&ptr, header.byte); /* write header */ | ||||||
|  | 
 | ||||||
|  | 	ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ | ||||||
|  | 
 | ||||||
|  | 	writeInt(&ptr, packetid); | ||||||
|  | 
 | ||||||
|  | 	rc = ptr - buf; | ||||||
|  | exit: | ||||||
|  | 	FUNC_EXIT_RC(rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,13 @@ | ||||||
|  |  SRC_FILES +=MQTTPacket.c\
 | ||||||
|  | 			 MQTTConnectClient.c \
 | ||||||
|  | 		     MQTTConnectServer.c \
 | ||||||
|  | 			 MQTTDeserializePublish.c \
 | ||||||
|  | 			 MQTTFormat.c \
 | ||||||
|  | 			 MQTTSerializePublish.c \
 | ||||||
|  | 			 MQTTSubscribeClient.c \
 | ||||||
|  | 			 MQTTSubscribeServer.c \
 | ||||||
|  | 			 MQTTUnsubscribeClient.c \
 | ||||||
|  | 			 MQTTUnsubscribeServer.c \
 | ||||||
|  | 			 transport.c | ||||||
|  | 
 | ||||||
|  | include $(KERNEL_ROOT)/compiler.mk | ||||||
|  | @ -0,0 +1,78 @@ | ||||||
|  | /*******************************************************************************
 | ||||||
|  |  * Copyright (c) 2014 IBM Corp. | ||||||
|  |  * | ||||||
|  |  * All rights reserved. This program and the accompanying materials | ||||||
|  |  * are made available under the terms of the Eclipse Public License v1.0 | ||||||
|  |  * and Eclipse Distribution License v1.0 which accompany this distribution. | ||||||
|  |  * | ||||||
|  |  * The Eclipse Public License is available at | ||||||
|  |  *    http://www.eclipse.org/legal/epl-v10.html
 | ||||||
|  |  * and the Eclipse Distribution License is available at | ||||||
|  |  *   http://www.eclipse.org/org/documents/edl-v10.php.
 | ||||||
|  |  * | ||||||
|  |  * Contributors: | ||||||
|  |  *    Ian Craggs - initial API and implementation and/or initial documentation | ||||||
|  |  *    Ian Craggs - fix for bug #434081 | ||||||
|  |  *******************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifndef STACKTRACE_H_ | ||||||
|  | #define STACKTRACE_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #define NOSTACKTRACE 1 | ||||||
|  | 
 | ||||||
|  | #if defined(NOSTACKTRACE) | ||||||
|  | #define FUNC_ENTRY | ||||||
|  | #define FUNC_ENTRY_NOLOG | ||||||
|  | #define FUNC_ENTRY_MED | ||||||
|  | #define FUNC_ENTRY_MAX | ||||||
|  | #define FUNC_EXIT | ||||||
|  | #define FUNC_EXIT_NOLOG | ||||||
|  | #define FUNC_EXIT_MED | ||||||
|  | #define FUNC_EXIT_MAX | ||||||
|  | #define FUNC_EXIT_RC(x) | ||||||
|  | #define FUNC_EXIT_MED_RC(x) | ||||||
|  | #define FUNC_EXIT_MAX_RC(x) | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | #if defined(WIN32) | ||||||
|  | #define inline __inline | ||||||
|  | #define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM) | ||||||
|  | #define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1) | ||||||
|  | #define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM) | ||||||
|  | #define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM) | ||||||
|  | #define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM) | ||||||
|  | #define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1) | ||||||
|  | #define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM) | ||||||
|  | #define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM) | ||||||
|  | #define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM) | ||||||
|  | #define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM) | ||||||
|  | #define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM) | ||||||
|  | #else | ||||||
|  | #define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM) | ||||||
|  | #define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1) | ||||||
|  | #define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM) | ||||||
|  | #define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM) | ||||||
|  | #define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM) | ||||||
|  | #define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1) | ||||||
|  | #define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM) | ||||||
|  | #define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM) | ||||||
|  | #define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM) | ||||||
|  | #define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM) | ||||||
|  | #define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM) | ||||||
|  | 
 | ||||||
|  | void StackTrace_entry(const char* name, int line, int trace); | ||||||
|  | void StackTrace_exit(const char* name, int line, void* return_value, int trace); | ||||||
|  | 
 | ||||||
|  | void StackTrace_printStack(FILE* dest); | ||||||
|  | char* StackTrace_get(unsigned long); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif /* STACKTRACE_H_ */ | ||||||
|  | @ -0,0 +1,59 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022 AIIT XUOS Lab | ||||||
|  |  * XiUOS is licensed under Mulan PSL v2. | ||||||
|  |  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||||
|  |  * You may obtain a copy of Mulan PSL v2 at: | ||||||
|  |  *        http://license.coscl.org.cn/MulanPSL2
 | ||||||
|  |  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||||
|  |  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||||
|  |  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||||
|  |  * See the Mulan PSL v2 for more details. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file transport.c | ||||||
|  |  * @brief mqtt transport function | ||||||
|  |  * @version 3.0 | ||||||
|  |  * @author AIIT XUOS Lab | ||||||
|  |  * @date 2023.8.10 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "transport.h" | ||||||
|  | #include "lwip/opt.h" | ||||||
|  | #include "lwip/arch.h" | ||||||
|  | #include "lwip/api.h" | ||||||
|  | #include "lwip/inet.h" | ||||||
|  | #include "lwip/sockets.h" | ||||||
|  | #include "string.h" | ||||||
|  | 
 | ||||||
|  | static int mysock; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t transport_sendPacketBuffer( uint8_t* buf, int32_t buflen) | ||||||
|  | { | ||||||
|  | 	int32_t rc; | ||||||
|  | 	rc = write(mysock, buf, buflen); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int transport_getdata(unsigned char* buf, int count) | ||||||
|  | { | ||||||
|  | 	int32_t rc; | ||||||
|  | 	 | ||||||
|  |   rc = recv(mysock, buf, count, 0); | ||||||
|  |   lw_print("get data : %lx\n",rc); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t transport_close(void) | ||||||
|  | { | ||||||
|  |    | ||||||
|  | 	int rc; | ||||||
|  | //	rc = close(mysock);
 | ||||||
|  |   rc = shutdown(mysock, SHUT_WR); | ||||||
|  | 	rc = recv(mysock, NULL, (size_t)0, 0); | ||||||
|  | 	rc = close(mysock); | ||||||
|  | 	return rc; | ||||||
|  | } | ||||||
|  | @ -0,0 +1,40 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022 AIIT XUOS Lab | ||||||
|  |  * XiUOS is licensed under Mulan PSL v2. | ||||||
|  |  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||||
|  |  * You may obtain a copy of Mulan PSL v2 at: | ||||||
|  |  *        http://license.coscl.org.cn/MulanPSL2
 | ||||||
|  |  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||||
|  |  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||||
|  |  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||||
|  |  * See the Mulan PSL v2 for more details. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file transport.h | ||||||
|  |  * @brief mqtt transport function | ||||||
|  |  * @version 3.0 | ||||||
|  |  * @author AIIT XUOS Lab | ||||||
|  |  * @date 2023.8.10 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef __TRANSPORT_H | ||||||
|  | #define __TRANSPORT_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t transport_sendPacketBuffer( uint8_t* buf, int32_t buflen); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int transport_getdata(unsigned char* buf, int count); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t transport_open(int8_t* servip, int32_t port); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t transport_close(void); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -0,0 +1,748 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022 AIIT XUOS Lab | ||||||
|  |  * XiUOS is licensed under Mulan PSL v2. | ||||||
|  |  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||||
|  |  * You may obtain a copy of Mulan PSL v2 at: | ||||||
|  |  *        http://license.coscl.org.cn/MulanPSL2
 | ||||||
|  |  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||||
|  |  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||||
|  |  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||||
|  |  * See the Mulan PSL v2 for more details. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file test_mqttclient.c | ||||||
|  |  * @brief mqtt subscribe fuction test | ||||||
|  |  * @version 3.0 | ||||||
|  |  * @author AIIT XUOS Lab | ||||||
|  |  * @date 2023.8.10 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <transform.h> | ||||||
|  | #include "test_mqttclient.h" | ||||||
|  | #include <stdio.h> | ||||||
|  | #ifdef ADD_XIZI_FEATURES | ||||||
|  | #include <sys_arch.h> | ||||||
|  | #include <lwip/sockets.h> | ||||||
|  | #include "lwip/sys.h" | ||||||
|  | #include "lwip/api.h" | ||||||
|  | 
 | ||||||
|  | #include "mqtt/MQTTPacket.h" | ||||||
|  | #include "mqtt/MQTTSubscribe.h" | ||||||
|  | #include "mqtt/transport.h" | ||||||
|  | #include <cJSON_Process.h> | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef ADD_NUTTX_FEATURES | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include "stdio.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define MQTT_DEMO_BUF_SIZE                    65535 | ||||||
|  | #define MQTT_DEMO_SEND_TIMES                  20 | ||||||
|  | #define LWIP_MQTT_DEMO_TASK_STACK_SIZE        4096 | ||||||
|  | #define LWIP_MQTT_DEMO_TASK_PRIO              20 | ||||||
|  | 
 | ||||||
|  | static char mqtt_demo_ipaddr[] = {192, 168, 130, 77}; | ||||||
|  | static char mqtt_demo_netmask[] = {255, 255, 254, 0}; | ||||||
|  | static char mqtt_demo_gwaddr[] = {192, 168, 130, 1}; | ||||||
|  | 
 | ||||||
|  | static pthread_t mqtt_client_task; | ||||||
|  | static pthread_t mqtt_server_task; | ||||||
|  | 
 | ||||||
|  | static uint16_t mqtt_socket_port = 1883; | ||||||
|  | static char mqtt_ip_str[128] = {192,168,100,1}; | ||||||
|  | 
 | ||||||
|  | void MqttSocketConfigParam(char *ip_str) | ||||||
|  | { | ||||||
|  |     int ip1, ip2, ip3, ip4, port = 0; | ||||||
|  | 
 | ||||||
|  |     if(ip_str == NULL) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     if(sscanf(ip_str, "%d.%d.%d.%d:%d", &ip1, &ip2, &ip3, &ip4, &port)) { | ||||||
|  |         printf("config ip %s port %d\n", ip_str, port); | ||||||
|  |         strcpy(mqtt_ip_str, ip_str); | ||||||
|  |         if(port) | ||||||
|  |             mqtt_socket_port = port; | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(sscanf(ip_str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4)) { | ||||||
|  |         printf("config ip %s\n", ip_str); | ||||||
|  |         strcpy(mqtt_ip_str, ip_str); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MQTT_USER_MSG  mqtt_user_msg; | ||||||
|  | 
 | ||||||
|  | uint8_t MQTT_Connect(void) | ||||||
|  | { | ||||||
|  |     MQTTPacket_connectData data = MQTTPacket_connectData_initializer; | ||||||
|  |     uint8_t buf[200]; | ||||||
|  |     int buflen = sizeof(buf); | ||||||
|  |     int len = 0; | ||||||
|  |     data.clientID.cstring = CLIENT_ID;                   //随机
 | ||||||
|  |     data.keepAliveInterval = KEEPLIVE_TIME;         //保持活跃
 | ||||||
|  |     data.username.cstring = USER_NAME;              //用户名
 | ||||||
|  |     data.password.cstring = PASSWORD;               //密钥
 | ||||||
|  |     data.MQTTVersion = MQTT_VERSION;                //3表示3.1版本,4表示3.11版本
 | ||||||
|  |     data.cleansession = 1; | ||||||
|  |     //组装消息
 | ||||||
|  |     len = MQTTSerialize_connect((unsigned char *)buf, buflen, &data); | ||||||
|  |     //发送消息
 | ||||||
|  |     transport_sendPacketBuffer(buf, len); | ||||||
|  |      | ||||||
|  |     /* 等待连接响应 */ | ||||||
|  |     if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK) | ||||||
|  |     { | ||||||
|  |         unsigned char sessionPresent, connack_rc; | ||||||
|  |         if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0) | ||||||
|  |         { | ||||||
|  |           lw_print("无法连接,错误代码是: %d!\n", connack_rc); | ||||||
|  |             return Connect_NOK; | ||||||
|  |         } | ||||||
|  |         else  | ||||||
|  |         { | ||||||
|  |             lw_print("用户名与密钥验证成功,MQTT连接成功!\n"); | ||||||
|  |             return Connect_OK; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |         lw_print("MQTT连接无响应!\n"); | ||||||
|  |         return Connect_NOTACK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | int32_t MQTT_PingReq(int32_t sock) | ||||||
|  | { | ||||||
|  | 	  int32_t len; | ||||||
|  | 		uint8_t buf[200]; | ||||||
|  | 		int32_t buflen = sizeof(buf);	  | ||||||
|  | 		fd_set readfd; | ||||||
|  | 	  struct timeval tv; | ||||||
|  | 	  tv.tv_sec = 5; | ||||||
|  | 	  tv.tv_usec = 0; | ||||||
|  | 	 | ||||||
|  | 	  FD_ZERO(&readfd); | ||||||
|  | 	  FD_SET(sock,&readfd);			 | ||||||
|  | 	 | ||||||
|  | 		len = MQTTSerialize_pingreq(buf, buflen); | ||||||
|  | 		transport_sendPacketBuffer(buf, len); | ||||||
|  | 	 | ||||||
|  | 		//等待可读事件
 | ||||||
|  | 		if(select(sock+1,&readfd,NULL,NULL,&tv) == 0) | ||||||
|  | 			return -1; | ||||||
|  | 		 | ||||||
|  | 	  //有可读事件
 | ||||||
|  | 		if(FD_ISSET(sock,&readfd) == 0) | ||||||
|  | 			return -2; | ||||||
|  | 		 | ||||||
|  | 		if(MQTTPacket_read(buf, buflen, transport_getdata) != PINGRESP) | ||||||
|  | 			return -3; | ||||||
|  | 		 | ||||||
|  | 		return 0; | ||||||
|  | 	 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | /************************************************************************
 | ||||||
|  | ** 函数名称: MQTTSubscribe								 | ||||||
|  | ** 函数功能: 订阅消息 | ||||||
|  | ** 入口参数: int32_t sock:套接字 | ||||||
|  | **           int8_t *topic:主题 | ||||||
|  | **           enum QoS pos:消息质量 | ||||||
|  | ** 出口参数: >=0:发送成功 <0:发送失败 | ||||||
|  | ** 备    注:  | ||||||
|  | ************************************************************************/ | ||||||
|  | int32_t MQTTSubscribe(int32_t sock,char *topic,enum QoS pos) | ||||||
|  | { | ||||||
|  | 	  static uint32_t PacketID = 0; | ||||||
|  | 	  uint16_t packetidbk = 0; | ||||||
|  | 	  int32_t conutbk = 0; | ||||||
|  | 		uint8_t buf[100]; | ||||||
|  | 		int32_t buflen = sizeof(buf); | ||||||
|  | 	  MQTTString topicString = MQTTString_initializer;   | ||||||
|  | 		int32_t len; | ||||||
|  | 	  int32_t req_qos,qosbk; | ||||||
|  | 
 | ||||||
|  | 		fd_set readfd; | ||||||
|  | 	  struct timeval tv; | ||||||
|  | 	  tv.tv_sec = 2; | ||||||
|  | 	  tv.tv_usec = 0; | ||||||
|  | 	 | ||||||
|  | 	  FD_ZERO(&readfd); | ||||||
|  | 	  FD_SET(sock,&readfd);		 | ||||||
|  | 	 | ||||||
|  | 	  //复制主题
 | ||||||
|  |     topicString.cstring = (char *)topic; | ||||||
|  | 		//订阅质量
 | ||||||
|  | 	  req_qos = pos; | ||||||
|  | 	 | ||||||
|  | 	  //串行化订阅消息
 | ||||||
|  |     len = MQTTSerialize_subscribe(buf, buflen, 0, PacketID++, 1, &topicString, &req_qos); | ||||||
|  | 		//发送TCP数据
 | ||||||
|  | 	  if(transport_sendPacketBuffer(buf, len) < 0) | ||||||
|  | 				return -1; | ||||||
|  | 	   | ||||||
|  |     //等待可读事件--等待超时
 | ||||||
|  | 		if(select(sock+1,&readfd,NULL,NULL,&tv) == 0) | ||||||
|  | 				return -2; | ||||||
|  | 		//有可读事件--没有可读事件
 | ||||||
|  | 		if(FD_ISSET(sock,&readfd) == 0) | ||||||
|  | 				return -3; | ||||||
|  | 
 | ||||||
|  | 		//等待订阅返回--未收到订阅返回
 | ||||||
|  | 		if(MQTTPacket_read(buf, buflen, transport_getdata) != SUBACK) | ||||||
|  | 				return -4;	 | ||||||
|  | 		 | ||||||
|  | 		//拆订阅回应包
 | ||||||
|  | 		if(MQTTDeserialize_suback(&packetidbk,1, &conutbk, &qosbk, buf, buflen) != 1) | ||||||
|  | 				return -5; | ||||||
|  | 		 | ||||||
|  | 		//检测返回数据的正确性
 | ||||||
|  | 		if((qosbk == 0x80)||(packetidbk != (PacketID-1))) | ||||||
|  | 				return -6; | ||||||
|  | 		 | ||||||
|  |     //订阅成功
 | ||||||
|  | 		return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t ReadPacketTimeout(int32_t sock,uint8_t *buf,int32_t buflen,uint32_t timeout) | ||||||
|  | { | ||||||
|  | 		fd_set readfd; | ||||||
|  | 	  struct timeval tv; | ||||||
|  | 	  if(timeout != 0) | ||||||
|  | 		{ | ||||||
|  | 				tv.tv_sec = timeout; | ||||||
|  | 				tv.tv_usec = 0; | ||||||
|  | 				FD_ZERO(&readfd); | ||||||
|  | 				FD_SET(sock,&readfd);  | ||||||
|  | 
 | ||||||
|  | 				 | ||||||
|  | 				if(select(sock+1,&readfd,NULL,NULL,&tv) == 0) | ||||||
|  | 						return -1; | ||||||
|  | 				 | ||||||
|  | 				if(FD_ISSET(sock,&readfd) == 0) | ||||||
|  | 						return -1; | ||||||
|  | 	  } | ||||||
|  | 		 | ||||||
|  | 		return MQTTPacket_read(buf, buflen, transport_getdata); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void deliverMessage(MQTTString  *TopicName,MQTTMessage *msg,MQTT_USER_MSG *mqtt_user_msg) | ||||||
|  | { | ||||||
|  | 		//消息质量
 | ||||||
|  | 		mqtt_user_msg->msgqos = msg->qos; | ||||||
|  | 		//保存消息
 | ||||||
|  | 		memcpy(mqtt_user_msg->msg,msg->payload,msg->payloadlen); | ||||||
|  | 		mqtt_user_msg->msg[msg->payloadlen] = 0; | ||||||
|  | 		//保存消息长度
 | ||||||
|  | 		mqtt_user_msg->msglenth = msg->payloadlen; | ||||||
|  | 		//消息主题
 | ||||||
|  | 		memcpy((char *)mqtt_user_msg->topic,TopicName->lenstring.data,TopicName->lenstring.len); | ||||||
|  | 		mqtt_user_msg->topic[TopicName->lenstring.len] = 0; | ||||||
|  | 		//消息ID
 | ||||||
|  | 		mqtt_user_msg->packetid = msg->id; | ||||||
|  | 		//标明消息合法
 | ||||||
|  | 		mqtt_user_msg->valid = 1;		 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void UserMsgCtl(MQTT_USER_MSG  *msg) | ||||||
|  | { | ||||||
|  | 		//这里处理数据只是打印,用户可以在这里添加自己的处理方式
 | ||||||
|  | 	  lw_print("****收到订阅的消息******\n"); | ||||||
|  | 		//<2F><><EFBFBD>غ<EFBFBD><D8BA><EFBFBD><EFBFBD><EFBFBD>Ϣ
 | ||||||
|  | 	  switch(msg->msgqos) | ||||||
|  | 		{ | ||||||
|  | 			case 0: | ||||||
|  | 				    lw_print("MQTT>>消息质量QoS0\n"); | ||||||
|  | 				    break; | ||||||
|  | 			case 1: | ||||||
|  | 				    lw_print("MQTT>>消息质量QoS1\n"); | ||||||
|  | 				    break; | ||||||
|  | 			case 2: | ||||||
|  | 				    lw_print("MQTT>>消息质量QoS2\n"); | ||||||
|  | 				    break; | ||||||
|  | 			default: | ||||||
|  | 				    lw_print("MQTT>>错误的消息质量\n"); | ||||||
|  | 				    break; | ||||||
|  | 		} | ||||||
|  | 		lw_print("MQTT>>消息主题:%s\n",msg->topic);	 | ||||||
|  | 		lw_print("MQTT>>消息内容:%s\n",msg->msg);	 | ||||||
|  | 		lw_print("MQTT>>消息长度:%d\n",msg->msglenth);	  | ||||||
|  |     Proscess(msg->msg); | ||||||
|  | 	  //处理完后销毁数据
 | ||||||
|  | 	  msg->valid  = 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void mqtt_pktype_ctl(uint8_t packtype,uint8_t *buf,uint32_t buflen) | ||||||
|  | { | ||||||
|  | 	  MQTTMessage msg; | ||||||
|  | 		int32_t rc; | ||||||
|  | 	  MQTTString receivedTopic; | ||||||
|  | 	  uint32_t len; | ||||||
|  |     lw_print("packtype:%d\n",packtype); | ||||||
|  | 		switch(packtype) | ||||||
|  | 		{ | ||||||
|  | 			case PUBLISH: | ||||||
|  |          | ||||||
|  |         if(MQTTDeserialize_publish(&msg.dup,(int*)&msg.qos, &msg.retained, &msg.id, &receivedTopic, | ||||||
|  |           (unsigned char **)&msg.payload, &msg.payloadlen, buf, buflen) != 1) | ||||||
|  |             return;	 | ||||||
|  |          | ||||||
|  |         deliverMessage(&receivedTopic,&msg,&mqtt_user_msg); | ||||||
|  |          | ||||||
|  |        | ||||||
|  |         if(msg.qos == QOS0) | ||||||
|  |         { | ||||||
|  |             //QOS0-不需要ACK
 | ||||||
|  |            //直接处理数据
 | ||||||
|  |            UserMsgCtl(&mqtt_user_msg); | ||||||
|  |            return; | ||||||
|  |         } | ||||||
|  |         //发送PUBACK消息
 | ||||||
|  |         if(msg.qos == QOS1) | ||||||
|  |         { | ||||||
|  |             len =MQTTSerialize_puback(buf,buflen,mqtt_user_msg.packetid); | ||||||
|  |             if(len == 0) | ||||||
|  |               return; | ||||||
|  |              //发送返回
 | ||||||
|  |             if(transport_sendPacketBuffer(buf,len)<0) | ||||||
|  |                return;	 | ||||||
|  |              //返回后处理消息
 | ||||||
|  |             UserMsgCtl(&mqtt_user_msg);  | ||||||
|  |             return;												 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //对于质量2,只需要发送PUBREC就可以了
 | ||||||
|  |         if(msg.qos == QOS2) | ||||||
|  |         { | ||||||
|  |            len = MQTTSerialize_ack(buf, buflen, PUBREC, 0, mqtt_user_msg.packetid);			                 | ||||||
|  |            if(len == 0) | ||||||
|  |              return; | ||||||
|  |             //发送返回
 | ||||||
|  |            transport_sendPacketBuffer(buf,len);	 | ||||||
|  |         }		 | ||||||
|  |         break; | ||||||
|  | 			case  PUBREL:				            | ||||||
|  |           //解析包数据,必须包ID相同才可以
 | ||||||
|  |         rc = MQTTDeserialize_ack(&msg.type,&msg.dup, &msg.id, buf,buflen); | ||||||
|  |         if((rc != 1)||(msg.type != PUBREL)||(msg.id != mqtt_user_msg.packetid)) | ||||||
|  |           return ; | ||||||
|  |        //收到PUBREL,需要处理并抛弃数据
 | ||||||
|  |         if(mqtt_user_msg.valid == 1) | ||||||
|  |         { | ||||||
|  |             //返回后处理消息
 | ||||||
|  |            UserMsgCtl(&mqtt_user_msg); | ||||||
|  |         }       | ||||||
|  |         //串行化PUBCMP消息
 | ||||||
|  |         len = MQTTSerialize_pubcomp(buf,buflen,msg.id);	                   	 | ||||||
|  |         if(len == 0) | ||||||
|  |           return;									 | ||||||
|  |        //发送返回--PUBCOMP
 | ||||||
|  |         transport_sendPacketBuffer(buf,len);										 | ||||||
|  |         break; | ||||||
|  | 			case   PUBACK://等级1客户端推送数据后,服务器返回
 | ||||||
|  | 				break; | ||||||
|  | 			case   PUBREC://等级2客户端推送数据后,服务器返回
 | ||||||
|  | 				break; | ||||||
|  | 			case   PUBCOMP://等级2客户端推送PUBREL后,服务器返回
 | ||||||
|  |         break; | ||||||
|  | 			default: | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void *MqttSocketRecvTask(void *arg) | ||||||
|  | { | ||||||
|  | MQTT_START:  | ||||||
|  |     lw_print("Recv begin**********\n"); | ||||||
|  |     int fd = -1, clientfd; | ||||||
|  |     int recv_len; | ||||||
|  |     int ret; | ||||||
|  |     char *recv_buf; | ||||||
|  |     struct sockaddr_in mqtt_addr; | ||||||
|  |     socklen_t addr_len; | ||||||
|  | 
 | ||||||
|  |      fd = socket(AF_INET, SOCK_STREAM, 0); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         lw_print("Socket error\n"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     struct sockaddr_in mqtt_sock; | ||||||
|  |     mqtt_sock.sin_family = AF_INET; | ||||||
|  |     mqtt_sock.sin_port = htons(mqtt_socket_port); | ||||||
|  |     mqtt_sock.sin_addr.s_addr = inet_addr(mqtt_ip_str); | ||||||
|  | 
 | ||||||
|  |     memset(&(mqtt_sock.sin_zero), 0, sizeof(mqtt_sock.sin_zero)); | ||||||
|  | 
 | ||||||
|  |     ret = connect(fd, (struct sockaddr *)&mqtt_sock, sizeof(struct sockaddr)); | ||||||
|  | 
 | ||||||
|  |     if (ret < 0) { | ||||||
|  |         lw_print("Unable to connect %s:%d = %d\n", mqtt_ip_str, mqtt_socket_port, ret); | ||||||
|  |         close(fd); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lw_print("MQTT connect %s:%d success, begin to verify username and password.\n", mqtt_ip_str, mqtt_socket_port); | ||||||
|  |      | ||||||
|  |     if(MQTT_Connect() != Connect_OK) | ||||||
|  |     { | ||||||
|  |         lw_print("MQTT verify failed.\n"); | ||||||
|  |         shutdown(fd, SHUT_WR); | ||||||
|  |         recv(fd, NULL, (size_t)0, 0); | ||||||
|  |         close(fd); | ||||||
|  |         PrivTaskDelay(1000); | ||||||
|  |         goto MQTT_START; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lw_print("MQTT subscribe begin.\n"); | ||||||
|  |     if(MQTTSubscribe(fd,(char *)TOPIC,QOS1) < 0) | ||||||
|  |     { | ||||||
|  |         lw_print("MQTT subscribe failed.\n"); | ||||||
|  |         shutdown(fd, SHUT_WR); | ||||||
|  |         recv(fd, NULL, (size_t)0, 0); | ||||||
|  |         close(fd); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lw_print("subscribe success.\n"); | ||||||
|  | 
 | ||||||
|  |     fd_set readfd; | ||||||
|  |     uint8_t no_mqtt_msg_exchange = 1; | ||||||
|  |     uint8_t buf[MSG_MAX_LEN]; | ||||||
|  | 	int32_t buflen = sizeof(buf); | ||||||
|  |     int32_t type; | ||||||
|  |     struct timeval tv;     | ||||||
|  | 	tv.tv_sec = 0; | ||||||
|  | 	tv.tv_usec = 10; | ||||||
|  | 
 | ||||||
|  |   int32_t curtick=0; | ||||||
|  | 
 | ||||||
|  |     while(1) | ||||||
|  |     { | ||||||
|  |       // lw_print("waiting********\n");
 | ||||||
|  |       curtick +=1; | ||||||
|  |       no_mqtt_msg_exchange = 1; | ||||||
|  | 			 | ||||||
|  | 		  FD_ZERO(&readfd); | ||||||
|  | 		  FD_SET(fd,&readfd); | ||||||
|  |       select(fd+1,&readfd,NULL,NULL,&tv); | ||||||
|  | 
 | ||||||
|  |       if(FD_ISSET(fd,&readfd) != 0) | ||||||
|  |       { | ||||||
|  |            | ||||||
|  |           type = ReadPacketTimeout(fd,buf,buflen,0); | ||||||
|  |           if(type != -1) | ||||||
|  |           { | ||||||
|  |             lw_print("ctl***********\n"); | ||||||
|  |               mqtt_pktype_ctl(type,buf,buflen); | ||||||
|  |    | ||||||
|  |               no_mqtt_msg_exchange = 0; | ||||||
|  | 								 | ||||||
|  | 					} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  |       if( curtick >(2*10000)) | ||||||
|  |         { | ||||||
|  |             curtick =0; | ||||||
|  |             //判断是否有数据交换
 | ||||||
|  |             if(no_mqtt_msg_exchange == 0) | ||||||
|  |             { | ||||||
|  |                //如果有数据交换,这次就不需要发送PING消息
 | ||||||
|  |                continue; | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             if(MQTT_PingReq(fd) < 0) | ||||||
|  |             { | ||||||
|  |                //重连服务器
 | ||||||
|  |                lw_print("发送保持活性ping失败....\n"); | ||||||
|  |                goto CLOSE;	  | ||||||
|  |             } | ||||||
|  |              | ||||||
|  | 
 | ||||||
|  |             lw_print("发送保持活性ping作为心跳成功....\n"); | ||||||
|  | 
 | ||||||
|  |             no_mqtt_msg_exchange = 0; | ||||||
|  |         }  | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | CLOSE: | ||||||
|  | 	  lw_print("MQTT subscribe failed.\n"); | ||||||
|  |     shutdown(fd, SHUT_WR); | ||||||
|  |     recv(fd, NULL, (size_t)0, 0); | ||||||
|  |     close(fd); | ||||||
|  |     return NULL; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void MqttSocketRecvTest(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  |     if(argc >= 2) { | ||||||
|  |         lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); | ||||||
|  |         MqttSocketConfigParam(argv[1]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // ip4_addr_t dns_ip;
 | ||||||
|  |     // netconn_gethostbyname(HOST_NAME, &dns_ip);
 | ||||||
|  |     // char* host_ip = ip_ntoa(&dns_ip);
 | ||||||
|  |     // lw_print("host name : %s , host_ip : %s\n",HOST_NAME,host_ip);
 | ||||||
|  |     // MqttSocketConfigParam(host_ip);
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef ADD_XIZI_FEATURES | ||||||
|  |     lwip_config_tcp(0, mqtt_demo_ipaddr, mqtt_demo_netmask, mqtt_demo_gwaddr); | ||||||
|  | 
 | ||||||
|  |     pthread_attr_t attr; | ||||||
|  |     attr.schedparam.sched_priority = LWIP_MQTT_DEMO_TASK_PRIO; | ||||||
|  |     attr.stacksize = LWIP_MQTT_DEMO_TASK_STACK_SIZE; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef ADD_NUTTX_FEATURES | ||||||
|  |     pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; | ||||||
|  |     attr.priority = LWIP_mqtt_DEMO_TASK_PRIO; | ||||||
|  |     attr.stacksize = LWIP_mqtt_DEMO_TASK_STACK_SIZE; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     PrivTaskCreate(&mqtt_server_task, &attr, &MqttSocketRecvTask, NULL); | ||||||
|  |     PrivTaskStartup(&mqtt_server_task); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PRIV_SHELL_CMD_FUNCTION(MqttSocketRecvTest, a tcp send sample, PRIV_SHELL_CMD_MAIN_ATTR); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |   uint8_t  humi_high8bit;                //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD>ʪ<EFBFBD>ȸ<EFBFBD>8λ
 | ||||||
|  |   uint8_t  humi_low8bit;                 //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD>ʪ<EFBFBD>ȵ<EFBFBD>8λ
 | ||||||
|  |   uint8_t  temp_high8bit;                 //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD><DDA3>¶ȸ<C2B6>8λ
 | ||||||
|  |   uint8_t  temp_low8bit;                 //ԭʼ<D4AD><CABC><EFBFBD>ݣ<EFBFBD><DDA3>¶ȸ<C2B6>8λ
 | ||||||
|  |   uint8_t  check_sum;                     //У<><D0A3><EFBFBD>
 | ||||||
|  |   double    humidity;        //ʵ<><CAB5>ʪ<EFBFBD><CAAA>
 | ||||||
|  |   double    temperature;     //ʵ<><CAB5><EFBFBD>¶<EFBFBD>  
 | ||||||
|  | } DHT11_Data_TypeDef; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | uint16_t GetNextPackID(void) | ||||||
|  | { | ||||||
|  | 	 static uint16_t pubpacketid = 0; | ||||||
|  | 	 return pubpacketid++; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t WaitForPacket(int32_t sock,uint8_t packettype,uint8_t times) | ||||||
|  | { | ||||||
|  | 	  int32_t type; | ||||||
|  | 		uint8_t buf[MSG_MAX_LEN]; | ||||||
|  | 	  uint8_t n = 0; | ||||||
|  | 		int32_t buflen = sizeof(buf); | ||||||
|  | 		do | ||||||
|  | 		{ | ||||||
|  | 				//读取数据包
 | ||||||
|  | 				type = ReadPacketTimeout(sock,buf,buflen,2); | ||||||
|  | 			  if(type != -1) | ||||||
|  | 					mqtt_pktype_ctl(type,buf,buflen); | ||||||
|  | 				n++; | ||||||
|  | 		}while((type != packettype)&&(n < times)); | ||||||
|  | 		//收到期望的包
 | ||||||
|  | 		if(type == packettype) | ||||||
|  | 			 return 0; | ||||||
|  | 		else  | ||||||
|  | 			 return -1;		 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t MQTTMsgPublish(int32_t sock, char *topic, int8_t qos, uint8_t* msg) | ||||||
|  | { | ||||||
|  |     int8_t retained = 0;      //保留标志位
 | ||||||
|  |     uint32_t msg_len;         //数据长度
 | ||||||
|  | 		uint8_t buf[MSG_MAX_LEN]; | ||||||
|  | 		int32_t buflen = sizeof(buf),len; | ||||||
|  | 		MQTTString topicString = MQTTString_initializer; | ||||||
|  | 	  uint16_t packid = 0,packetidbk; | ||||||
|  | 	 | ||||||
|  | 		//填充主题
 | ||||||
|  | 	  topicString.cstring = (char *)topic; | ||||||
|  | 
 | ||||||
|  | 	  //填充数据包ID
 | ||||||
|  | 	  if((qos == QOS1)||(qos == QOS2)) | ||||||
|  | 		{  | ||||||
|  | 			packid = GetNextPackID(); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			  qos = QOS0; | ||||||
|  | 			  retained = 0; | ||||||
|  | 			  packid = 0; | ||||||
|  | 		} | ||||||
|  |       | ||||||
|  |     msg_len = strlen((char *)msg); | ||||||
|  |      | ||||||
|  | 		//推送消息
 | ||||||
|  | 		len = MQTTSerialize_publish(buf, buflen, 0, qos, retained, packid, topicString, (unsigned char*)msg, msg_len); | ||||||
|  | 		if(len <= 0) | ||||||
|  | 				return -1; | ||||||
|  | 		if(transport_sendPacketBuffer(buf, len) < 0)	 | ||||||
|  | 				return -2;	 | ||||||
|  | 		 | ||||||
|  | 		//质量等级0,不需要返回
 | ||||||
|  | 		if(qos == QOS0) | ||||||
|  | 		{ | ||||||
|  | 				return 0; | ||||||
|  | 		} | ||||||
|  | 		 | ||||||
|  | 		//等级1
 | ||||||
|  | 		if(qos == QOS1) | ||||||
|  | 		{ | ||||||
|  | 				//等待PUBACK
 | ||||||
|  | 			  if(WaitForPacket(sock,PUBACK,5) < 0) | ||||||
|  | 					 return -3; | ||||||
|  | 				return 1; | ||||||
|  | 			   | ||||||
|  | 		} | ||||||
|  | 		//等级2
 | ||||||
|  | 		if(qos == QOS2)	 | ||||||
|  | 		{ | ||||||
|  | 			  //等待PUBREC
 | ||||||
|  | 			  if(WaitForPacket(sock,PUBREC,5) < 0) | ||||||
|  | 					 return -3; | ||||||
|  | 			  //发送PUBREL
 | ||||||
|  |         len = MQTTSerialize_pubrel(buf, buflen,0, packetidbk); | ||||||
|  | 				if(len == 0) | ||||||
|  | 					return -4; | ||||||
|  | 				if(transport_sendPacketBuffer(buf, len) < 0)	 | ||||||
|  | 					return -6;			 | ||||||
|  | 			  //等待PUBCOMP
 | ||||||
|  | 			  if(WaitForPacket(sock,PUBREC,5) < 0) | ||||||
|  | 					 return -7; | ||||||
|  | 				return 2; | ||||||
|  | 		} | ||||||
|  | 		//等级错误
 | ||||||
|  | 		return -8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static void *MqttSocketSendTask(void *arg) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |   int fd = -1, clientfd; | ||||||
|  |     int recv_len; | ||||||
|  |     int ret; | ||||||
|  |     char *recv_buf; | ||||||
|  |     struct sockaddr_in mqtt_addr; | ||||||
|  |     socklen_t addr_len; | ||||||
|  | 
 | ||||||
|  |      fd = socket(AF_INET, SOCK_STREAM, 0); | ||||||
|  |     if (fd < 0) { | ||||||
|  |         lw_print("Socket error\n"); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     struct sockaddr_in mqtt_sock; | ||||||
|  |     mqtt_sock.sin_family = AF_INET; | ||||||
|  |     mqtt_sock.sin_port = htons(mqtt_socket_port); | ||||||
|  |     mqtt_sock.sin_addr.s_addr = inet_addr(mqtt_ip_str); | ||||||
|  | 
 | ||||||
|  |     memset(&(mqtt_sock.sin_zero), 0, sizeof(mqtt_sock.sin_zero)); | ||||||
|  | 
 | ||||||
|  |     ret = connect(fd, (struct sockaddr *)&mqtt_sock, sizeof(struct sockaddr)); | ||||||
|  | 
 | ||||||
|  |     if (ret < 0) { | ||||||
|  |         lw_print("Unable to connect %s:%d = %d\n", mqtt_ip_str, mqtt_socket_port, ret); | ||||||
|  |         close(fd); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lw_print("MQTT connect %s:%d success, begin to verify hostname and password.\n", mqtt_ip_str, mqtt_socket_port); | ||||||
|  |      | ||||||
|  |     if(MQTT_Connect() != Connect_OK) | ||||||
|  |     { | ||||||
|  |         lw_print("MQTT verify failed.\n"); | ||||||
|  |         shutdown(fd, SHUT_WR); | ||||||
|  |         recv(fd, NULL, (size_t)0, 0); | ||||||
|  |         close(fd); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lw_print("MQTT subscribe begin.\n"); | ||||||
|  |     if(MQTTSubscribe(fd,(char *)TOPIC,QOS1) < 0) | ||||||
|  |     { | ||||||
|  |         lw_print("MQTT subscribe failed.\n"); | ||||||
|  |         shutdown(fd, SHUT_WR); | ||||||
|  |         recv(fd, NULL, (size_t)0, 0); | ||||||
|  |         close(fd); | ||||||
|  |         return NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     lw_print("subscribe success.\n"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     uint8_t no_mqtt_msg_exchange = 1; | ||||||
|  |     uint32_t curtick=0; | ||||||
|  |     uint8_t res; | ||||||
|  | 
 | ||||||
|  |     cJSON* cJSON_Data = NULL; | ||||||
|  |     cJSON_Data = cJSON_Data_Init(); | ||||||
|  |     DHT11_Data_TypeDef* recv_data; | ||||||
|  | 
 | ||||||
|  |     double a,b; | ||||||
|  |     while(1) | ||||||
|  |     { | ||||||
|  |       curtick+=1; | ||||||
|  |       char* p ="Hello,here is hc"; | ||||||
|  |       ret = MQTTMsgPublish(fd,(char*)TOPIC,QOS0,(uint8_t*)p); | ||||||
|  |       if(ret >= 0) | ||||||
|  |       { | ||||||
|  |           no_mqtt_msg_exchange = 0; | ||||||
|  |           PrivTaskDelay(1000); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | void MqttSocketSendTest(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  |     if(argc >= 2) { | ||||||
|  |         lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); | ||||||
|  |         MqttSocketConfigParam(argv[1]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // ip4_addr_t dns_ip;
 | ||||||
|  |     // netconn_gethostbyname(HOST_NAME, &dns_ip);
 | ||||||
|  |     // char* host_ip = ip_ntoa(&dns_ip);
 | ||||||
|  |     // lw_print("host name : %s , host_ip : %s\n",HOST_NAME,host_ip);
 | ||||||
|  |     // MqttSocketConfigParam(host_ip);
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifdef ADD_XIZI_FEATURES | ||||||
|  |     lwip_config_tcp(0, mqtt_demo_ipaddr, mqtt_demo_netmask, mqtt_demo_gwaddr); | ||||||
|  | 
 | ||||||
|  |     pthread_attr_t attr; | ||||||
|  |     attr.schedparam.sched_priority = LWIP_MQTT_DEMO_TASK_PRIO; | ||||||
|  |     attr.stacksize = LWIP_MQTT_DEMO_TASK_STACK_SIZE; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifdef ADD_NUTTX_FEATURES | ||||||
|  |     pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; | ||||||
|  |     attr.priority = LWIP_mqtt_DEMO_TASK_PRIO; | ||||||
|  |     attr.stacksize = LWIP_mqtt_DEMO_TASK_STACK_SIZE; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     PrivTaskCreate(&mqtt_client_task, &attr, &MqttSocketSendTask, NULL); | ||||||
|  |     PrivTaskStartup(&mqtt_client_task); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PRIV_SHELL_CMD_FUNCTION(MqttSocketSendTest, a tcp send sample, PRIV_SHELL_CMD_MAIN_ATTR); | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,99 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2022 AIIT XUOS Lab | ||||||
|  |  * XiUOS is licensed under Mulan PSL v2. | ||||||
|  |  * You can use this software according to the terms and conditions of the Mulan PSL v2. | ||||||
|  |  * You may obtain a copy of Mulan PSL v2 at: | ||||||
|  |  *        http://license.coscl.org.cn/MulanPSL2
 | ||||||
|  |  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||||||
|  |  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||||||
|  |  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||||||
|  |  * See the Mulan PSL v2 for more details. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file test_mqttclient.h | ||||||
|  |  * @brief mqtt subscribe fuction test | ||||||
|  |  * @version 3.0 | ||||||
|  |  * @author AIIT XUOS Lab | ||||||
|  |  * @date 2023.8.10 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | #define   MSG_MAX_LEN     1024 | ||||||
|  | #define   MSG_TOPIC_LEN   50 | ||||||
|  | #define   KEEPLIVE_TIME   650 | ||||||
|  | #define   MQTT_VERSION    4 | ||||||
|  | 
 | ||||||
|  | #ifdef    LWIP_DNS | ||||||
|  | #define   HOST_NAME       "iot-06z00im0uwa0ki2.mqtt.iothub.aliyuncs.com"     //服务器域名
 | ||||||
|  | #else | ||||||
|  | #define   HOST_NAME       "iot-06z00im0uwa0ki2.mqtt.iothub.aliyuncs.com"     //服务器IP地址
 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | //#define   HOST_IP       "129.204.201.235"
 | ||||||
|  | #define   HOST_PORT     1883    //由于是TCP连接,端口必须是1883
 | ||||||
|  | 
 | ||||||
|  | #define   CLIENT_ID     "iw3rn3pa11K.test|securemode=2,signmethod=hmacsha256,timestamp=1689296035604|"         //随机的id
 | ||||||
|  | #define   USER_NAME     "test&iw3rn3pa11K"     //用户名
 | ||||||
|  | #define   PASSWORD      "7b948d22fe46f0f63d1a403376d26e7cb298abc227d29e44311d7040307a71f8"  //秘钥
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define   TOPIC         "/iw3rn3pa11K/test/user/Test"      //订阅的主题
 | ||||||
|  | 
 | ||||||
|  | #define   TEST_MESSAGE  "test_message"  //发送测试消息
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // #define   CLIENT_ID     "hc123456789"         //随机的id
 | ||||||
|  | // #define   USER_NAME     "xiuos"     //用户名
 | ||||||
|  | // #define   PASSWORD      "xiuos"  //秘钥
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | enum QoS  | ||||||
|  | { QOS0 = 0,  | ||||||
|  |   QOS1,  | ||||||
|  |   QOS2  | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum MQTT_Connect | ||||||
|  | { | ||||||
|  |   Connect_OK = 0, | ||||||
|  |   Connect_NOK, | ||||||
|  |   Connect_NOTACK | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | //数据交互结构体
 | ||||||
|  | typedef struct __MQTTMessage | ||||||
|  | { | ||||||
|  |     uint32_t qos; | ||||||
|  |     uint8_t retained; | ||||||
|  |     uint8_t dup; | ||||||
|  |     uint16_t id; | ||||||
|  | 	  uint8_t type; | ||||||
|  |     void *payload; | ||||||
|  |     int32_t payloadlen; | ||||||
|  | }MQTTMessage; | ||||||
|  | 
 | ||||||
|  | //用户接收消息结构体
 | ||||||
|  | typedef struct __MQTT_MSG | ||||||
|  | { | ||||||
|  | 	  uint8_t  msgqos;                 //消息质量
 | ||||||
|  | 		uint8_t  msg[MSG_MAX_LEN];       //消息
 | ||||||
|  | 	  uint32_t msglenth;               //消息长度
 | ||||||
|  | 	  uint8_t  topic[MSG_TOPIC_LEN];   //主题    
 | ||||||
|  | 	  uint16_t packetid;               //消息ID
 | ||||||
|  | 	  uint8_t  valid;                  //标明消息是否有效
 | ||||||
|  | }MQTT_USER_MSG; | ||||||
|  | 
 | ||||||
|  | //发送消息结构体
 | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |     int8_t topic[MSG_TOPIC_LEN]; | ||||||
|  |     int8_t qos; | ||||||
|  |     int8_t retained; | ||||||
|  | 
 | ||||||
|  |     uint8_t msg[MSG_MAX_LEN]; | ||||||
|  |     uint8_t msglen; | ||||||
|  | } mqtt_recv_msg_t, *p_mqtt_recv_msg_t, mqtt_send_msg_t, *p_mqtt_send_msg_t; | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
| SRC_FILES :=  cJSON.c | SRC_FILES :=  cJSON.c | ||||||
|  | SRC_FILES +=  cJSON_Process.c | ||||||
| 
 | 
 | ||||||
| include $(KERNEL_ROOT)/compiler.mk | include $(KERNEL_ROOT)/compiler.mk | ||||||
|  |  | ||||||
|  | @ -0,0 +1,85 @@ | ||||||
|  | #include "cJSON_Process.h" | ||||||
|  | #include <lwip/sockets.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | cJSON* cJSON_Data_Init(void) | ||||||
|  | { | ||||||
|  |   cJSON* cJSON_Root = NULL;    //json根节点
 | ||||||
|  |    | ||||||
|  |    | ||||||
|  |   cJSON_Root = cJSON_CreateObject();  /*创建项目*/ | ||||||
|  |   if(NULL == cJSON_Root) | ||||||
|  |   { | ||||||
|  |       return NULL; | ||||||
|  |   } | ||||||
|  |   cJSON_AddStringToObject(cJSON_Root, NAME, DEFAULT_NAME);  /*添加元素  键值对*/ | ||||||
|  |   cJSON_AddNumberToObject(cJSON_Root, TEMP_NUM, DEFAULT_TEMP_NUM); | ||||||
|  |   cJSON_AddNumberToObject(cJSON_Root, HUM_NUM, DEFAULT_HUM_NUM); | ||||||
|  |    | ||||||
|  |   char* p = cJSON_Print(cJSON_Root);  /*p 指向的字符串是json格式的*/ | ||||||
|  | 
 | ||||||
|  |   p = NULL; | ||||||
|  |    | ||||||
|  |   return cJSON_Root; | ||||||
|  |    | ||||||
|  | } | ||||||
|  | uint8_t cJSON_Update(const cJSON * const object,const char * const string,void *d) | ||||||
|  | { | ||||||
|  |   cJSON* node = NULL;    //json根节点
 | ||||||
|  |   node = cJSON_GetObjectItem(object,string); | ||||||
|  |   if(node == NULL) | ||||||
|  |     return 0; | ||||||
|  |   if(cJSON_IsBool(node)) | ||||||
|  |   { | ||||||
|  |     int *b = (int*)d; | ||||||
|  | 
 | ||||||
|  |     cJSON_GetObjectItem(object,string)->type = *b ? cJSON_True : cJSON_False; | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   else if(cJSON_IsString(node)) | ||||||
|  |   { | ||||||
|  |     cJSON_GetObjectItem(object,string)->valuestring = (char*)d; | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   else if(cJSON_IsNumber(node)) | ||||||
|  |   { | ||||||
|  |     double *num = (double*)d; | ||||||
|  | 
 | ||||||
|  |     cJSON_GetObjectItem(object,string)->valuedouble = (double)*num; | ||||||
|  | 
 | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Proscess(void* data) | ||||||
|  | { | ||||||
|  |    | ||||||
|  |   cJSON *root,*json_name,*json_temp_num,*json_hum_num; | ||||||
|  |   root = cJSON_Parse((char*)data); //解析成json形式
 | ||||||
|  | 
 | ||||||
|  |   json_name = cJSON_GetObjectItem( root , NAME);   //获取键值内容
 | ||||||
|  |   json_temp_num = cJSON_GetObjectItem( root , TEMP_NUM ); | ||||||
|  |   json_hum_num = cJSON_GetObjectItem( root , HUM_NUM ); | ||||||
|  | 
 | ||||||
|  |   lw_print("name:%s\n temp_num:%f\n hum_num:%f\n", | ||||||
|  |               json_name->valuestring, | ||||||
|  |               json_temp_num->valuedouble, | ||||||
|  |               json_hum_num->valuedouble); | ||||||
|  | 
 | ||||||
|  |   cJSON_Delete(root); //释放内存 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | #ifndef _CJSON_PROCESS_H_ | ||||||
|  | #define _CJSON_PROCESS_H_ | ||||||
|  | #include "cJSON.h" | ||||||
|  | #include "stdint.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define   NAME            "name"      | ||||||
|  | #define   TEMP_NUM        "temp"   | ||||||
|  | #define   HUM_NUM         "hum"  | ||||||
|  | 
 | ||||||
|  | #define   DEFAULT_NAME          "fire"      | ||||||
|  | #define   DEFAULT_TEMP_NUM       25.0  | ||||||
|  | #define   DEFAULT_HUM_NUM        50.0  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define   UPDATE_SUCCESS       1  | ||||||
|  | #define   UPDATE_FAIL          0 | ||||||
|  | 
 | ||||||
|  | cJSON* cJSON_Data_Init(void); | ||||||
|  | uint8_t cJSON_Update(const cJSON * const object,const char * const string,void * d); | ||||||
|  | void Proscess(void* data); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||